mrio-toolbox 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mrio-toolbox might be problematic. Click here for more details.
- mrio_toolbox/__init__.py +5 -0
- mrio_toolbox/_parts/_Axe.py +481 -0
- mrio_toolbox/_parts/_Part.py +1504 -0
- mrio_toolbox/_parts/__init__.py +3 -0
- mrio_toolbox/_parts/part_operations.py +50 -0
- mrio_toolbox/mrio.py +739 -0
- mrio_toolbox/utils/__init__.py +0 -0
- mrio_toolbox/utils/converters/__init__.py +2 -0
- mrio_toolbox/utils/converters/pandas.py +245 -0
- mrio_toolbox/utils/converters/xarray.py +141 -0
- mrio_toolbox/utils/loaders/__init__.py +3 -0
- mrio_toolbox/utils/loaders/_loader.py +256 -0
- mrio_toolbox/utils/loaders/_loader_factory.py +75 -0
- mrio_toolbox/utils/loaders/_nc_loader.py +148 -0
- mrio_toolbox/utils/loaders/_np_loader.py +112 -0
- mrio_toolbox/utils/loaders/_pandas_loader.py +102 -0
- mrio_toolbox/utils/loaders/_parameter_loader.py +341 -0
- mrio_toolbox/utils/savers/__init__.py +8 -0
- mrio_toolbox/utils/savers/_path_checker.py +19 -0
- mrio_toolbox/utils/savers/_to_folder.py +160 -0
- mrio_toolbox/utils/savers/_to_nc.py +52 -0
- mrio_toolbox-1.0.0.dist-info/LICENSE +674 -0
- mrio_toolbox-1.0.0.dist-info/METADATA +28 -0
- mrio_toolbox-1.0.0.dist-info/RECORD +26 -0
- mrio_toolbox-1.0.0.dist-info/WHEEL +5 -0
- mrio_toolbox-1.0.0.dist-info/top_level.txt +1 -0
mrio_toolbox/__init__.py
ADDED
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Definition of Part Axes
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
import itertools
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import numpy as np
|
|
10
|
+
from copy import deepcopy
|
|
11
|
+
|
|
12
|
+
log = logging.getLogger(__name__)
|
|
13
|
+
class Axe:
|
|
14
|
+
def __init__(self,labels,groupings=None,
|
|
15
|
+
name=None):
|
|
16
|
+
"""Define an Axe that holds the labels and dimensions for an MRIO part
|
|
17
|
+
|
|
18
|
+
Axes hold the methods to slice MRIO Parts based on labels or indices.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
labels : dict or list of list of str
|
|
23
|
+
Dict of labels for each level of the Axe.
|
|
24
|
+
The levels are interpreted in the order of the keys.
|
|
25
|
+
If a list of list of str is passed, levels are set to 0,1,2...
|
|
26
|
+
groupings : dict, optional
|
|
27
|
+
Dict of groupings for each level of the Axe.
|
|
28
|
+
Grouping of labels into larger categories (e.g countries into zones).
|
|
29
|
+
If groupings contain references not in the labels, these are removed.
|
|
30
|
+
name : str
|
|
31
|
+
Name of the axis
|
|
32
|
+
If left empty, a name is generated automatically
|
|
33
|
+
|
|
34
|
+
Attributes
|
|
35
|
+
----------
|
|
36
|
+
labels : dict
|
|
37
|
+
Dict of labels for each level of the Axe.
|
|
38
|
+
levels : int
|
|
39
|
+
Number of levels in the Axe.
|
|
40
|
+
dims : list of int
|
|
41
|
+
Number of labels for each level.
|
|
42
|
+
dimensions : list of str
|
|
43
|
+
Keys of the labels dict.
|
|
44
|
+
|
|
45
|
+
Main methods
|
|
46
|
+
------------
|
|
47
|
+
label : Generate the labels for the full axis
|
|
48
|
+
get : Get the indices of a selection on the Axe
|
|
49
|
+
"""
|
|
50
|
+
if isinstance(labels,dict):
|
|
51
|
+
self.labels = labels
|
|
52
|
+
elif isinstance(labels,list):
|
|
53
|
+
if isinstance(labels[0],dict):
|
|
54
|
+
self.labels = dict()
|
|
55
|
+
for label in labels:
|
|
56
|
+
self.labels.update(label)
|
|
57
|
+
elif not isinstance(labels[0],list):
|
|
58
|
+
self.labels = {str(0):labels}
|
|
59
|
+
else:
|
|
60
|
+
self.labels = {str(i):labels[i] for i in range(len(labels))}
|
|
61
|
+
else:
|
|
62
|
+
raise TypeError("Labels must be a list or a dict. Input is of type "+str(type(labels)))
|
|
63
|
+
self.levels = len(self.labels)
|
|
64
|
+
self.dims = [len(dim) for dim in self.labels.values()]
|
|
65
|
+
self.dimensions = [
|
|
66
|
+
str(dim) for dim in self.labels.keys()
|
|
67
|
+
] #Dimensions of the Axe
|
|
68
|
+
#Dimensions also save the order of the levels
|
|
69
|
+
self.update_multipliers()
|
|
70
|
+
if groupings is None:
|
|
71
|
+
self.groupings = {i:{} for i in self.dimensions}
|
|
72
|
+
else:
|
|
73
|
+
self.groupings = groupings
|
|
74
|
+
self.mappings = dict()
|
|
75
|
+
self.derive_mappings(groupings)
|
|
76
|
+
if name is None:
|
|
77
|
+
if len(self.dimensions)>1:
|
|
78
|
+
self.name = "_".join(self.dimensions)
|
|
79
|
+
self.name = self.dimensions[0]
|
|
80
|
+
else:
|
|
81
|
+
self.name = name
|
|
82
|
+
|
|
83
|
+
def set_labels(self,labels):
|
|
84
|
+
"""Set the labels of the Axe
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
labels : dict or list of list of str
|
|
89
|
+
Dict of labels for each level of the Axe.
|
|
90
|
+
The levels are interpreted in the order of the keys.
|
|
91
|
+
If a list of list of str is passed, levels are set to 0,1,2...
|
|
92
|
+
"""
|
|
93
|
+
if isinstance(labels,dict):
|
|
94
|
+
self.labels = labels
|
|
95
|
+
elif isinstance(labels,list):
|
|
96
|
+
if not isinstance(labels[0],list):
|
|
97
|
+
self.labels = {str(0):labels}
|
|
98
|
+
else:
|
|
99
|
+
self.labels = {str(i):labels[i] for i in range(len(labels))}
|
|
100
|
+
else:
|
|
101
|
+
raise TypeError("Labels must be a list or a dict. Input is of type "+str(type(labels)))
|
|
102
|
+
self.levels = len(self.labels)
|
|
103
|
+
self.dims = [len(dim) for dim in self.labels.values()]
|
|
104
|
+
self.dimensions = list(self.labels.keys())
|
|
105
|
+
|
|
106
|
+
def update_multipliers(self):
|
|
107
|
+
"""
|
|
108
|
+
Update the multipliers used to convert labels into indices
|
|
109
|
+
"""
|
|
110
|
+
multipliers = []
|
|
111
|
+
for i in range(len(self.dims)):
|
|
112
|
+
multipliers.append(self.dims[i]*multipliers[i-1] if i > 0 else 1)
|
|
113
|
+
self.multipliers = { #Dictionnary of multipliers for each level
|
|
114
|
+
i:multiplier for i,multiplier in zip(self.dimensions,multipliers[::-1])
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
def squeeze(self):
|
|
118
|
+
"""Remove levels with only one label"""
|
|
119
|
+
for key in self.dimensions:
|
|
120
|
+
if len(self.labels[key]) == 1:
|
|
121
|
+
self.labels.pop(key)
|
|
122
|
+
self.multipliers.pop(key)
|
|
123
|
+
if key in self.mappings.keys():
|
|
124
|
+
self.mappings.pop(key)
|
|
125
|
+
self.levels = len(self.labels)
|
|
126
|
+
self.dims = [len(dim) for dim in self.labels.values()]
|
|
127
|
+
self.dimensions = list(self.labels.keys())
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def swap_levels(self,level1,level2):
|
|
131
|
+
"""Swap the positions of two levels in the Axe
|
|
132
|
+
|
|
133
|
+
Parameters
|
|
134
|
+
----------
|
|
135
|
+
level1 : int
|
|
136
|
+
Level to swap
|
|
137
|
+
level2 : int
|
|
138
|
+
Level to swap
|
|
139
|
+
"""
|
|
140
|
+
if level1 not in self.labels.keys() or level2 not in self.labels.keys():
|
|
141
|
+
raise IndexError("Levels must be in the Axe")
|
|
142
|
+
dims = self.dimensions
|
|
143
|
+
dims[level1],dims[level2] = dims[level2],dims[level1]
|
|
144
|
+
self.dimensions = dims
|
|
145
|
+
self.dims = [len(dim) for dim in self.labels.values()]
|
|
146
|
+
|
|
147
|
+
def __str__(self):
|
|
148
|
+
return f"Axe object of len {len(self)}, with {self.levels} levels: {self.dimensions}"
|
|
149
|
+
|
|
150
|
+
def label(self,as_index=False):
|
|
151
|
+
""" Generate the labels for the full axis
|
|
152
|
+
|
|
153
|
+
Parameters
|
|
154
|
+
----------
|
|
155
|
+
as_index : bool, optional
|
|
156
|
+
Whether to return a Pandas MultiIndex.
|
|
157
|
+
The default is False, in which case a list of labels is returned.
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
Returns
|
|
161
|
+
-------
|
|
162
|
+
list of str
|
|
163
|
+
Labels along the Axe
|
|
164
|
+
"""
|
|
165
|
+
if as_index:
|
|
166
|
+
if self.levels == 1:
|
|
167
|
+
return pd.Index(self.labels[self.dimensions[0]],name=self.dimensions[0])
|
|
168
|
+
return pd.MultiIndex.from_product(
|
|
169
|
+
[self.labels[key] for key in self.dimensions],
|
|
170
|
+
names = self.dimensions
|
|
171
|
+
)
|
|
172
|
+
return list(itertools.product(*[self.labels[key] for key in self.dimensions]))
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def isin(self,arg,level):
|
|
176
|
+
"""
|
|
177
|
+
Assert whether an element is in the labels of a given level
|
|
178
|
+
|
|
179
|
+
Parameters
|
|
180
|
+
----------
|
|
181
|
+
arg : str, int or list of int,str
|
|
182
|
+
Element to look for.
|
|
183
|
+
level : str
|
|
184
|
+
Key of the level to look into.
|
|
185
|
+
|
|
186
|
+
Returns
|
|
187
|
+
-------
|
|
188
|
+
bool
|
|
189
|
+
Whether the element is in the labels.
|
|
190
|
+
|
|
191
|
+
"""
|
|
192
|
+
if "all" in arg:
|
|
193
|
+
return True
|
|
194
|
+
if isinstance(arg,(int,np.integer)) and arg < len(self.labels[level]):
|
|
195
|
+
return True
|
|
196
|
+
if isinstance(arg,str):
|
|
197
|
+
return arg in self.labels[level] \
|
|
198
|
+
or arg in self.mappings[level].keys()\
|
|
199
|
+
or arg=="all"
|
|
200
|
+
if isinstance(arg,list):
|
|
201
|
+
for a in arg:
|
|
202
|
+
if not self.isin(a,level):
|
|
203
|
+
return False
|
|
204
|
+
return True
|
|
205
|
+
raise TypeError(
|
|
206
|
+
"Arg must be a string, an int or a list of strings or ints."+\
|
|
207
|
+
" Input is of type "+str(type(arg))
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
def derive_mappings(self,groupings=None):
|
|
211
|
+
"""Update the mappings of the Axe
|
|
212
|
+
|
|
213
|
+
Mappings are defined at Axe level and derive from the current groupings
|
|
214
|
+
Mappings are used to convert grouping labels into indices
|
|
215
|
+
|
|
216
|
+
Parameters
|
|
217
|
+
----------
|
|
218
|
+
groupings : dict of dict
|
|
219
|
+
Dict of groupings for each level of the Axe.
|
|
220
|
+
Grouping of labels into larger categories (e.g countries into zones).
|
|
221
|
+
If groupings contain references not in the labels, these are removed.
|
|
222
|
+
By default, groupings are set to the identity.
|
|
223
|
+
"""
|
|
224
|
+
|
|
225
|
+
if groupings is None:
|
|
226
|
+
for key in self.dimensions:
|
|
227
|
+
self.mappings[key] = {
|
|
228
|
+
label:[i] for i,label in enumerate(self.labels[key])
|
|
229
|
+
}
|
|
230
|
+
return
|
|
231
|
+
to_sort = deepcopy(groupings)
|
|
232
|
+
for key in self.dimensions:
|
|
233
|
+
self.mappings[key] = dict()
|
|
234
|
+
covered = []
|
|
235
|
+
|
|
236
|
+
if to_sort is not None and key in to_sort.keys():
|
|
237
|
+
for group in to_sort[key].keys():
|
|
238
|
+
idlist = []
|
|
239
|
+
for label in to_sort[key][group]:
|
|
240
|
+
if label not in self.labels[key]\
|
|
241
|
+
and label != "all":
|
|
242
|
+
log.debug(
|
|
243
|
+
f"Label {label} in group {group} is not in the labels of the Axe"
|
|
244
|
+
)
|
|
245
|
+
to_sort[key][group].remove(label)
|
|
246
|
+
elif label in covered:
|
|
247
|
+
log.warning(
|
|
248
|
+
f"Label {label} is in multiple groups and will be ignored from group {group}"
|
|
249
|
+
)
|
|
250
|
+
elif label == "all":
|
|
251
|
+
idlist = [i for i in len(self.labels[key])]
|
|
252
|
+
covered = self.labels[key] + "all"
|
|
253
|
+
else:
|
|
254
|
+
idlist.append(self.labels[key].index(label))
|
|
255
|
+
covered.append(label)
|
|
256
|
+
to_sort[key][group] = idlist.copy()
|
|
257
|
+
self.mappings[key] = to_sort[key].copy()
|
|
258
|
+
else:
|
|
259
|
+
self.mappings[key] = dict()
|
|
260
|
+
|
|
261
|
+
def update_groupings(self,groupings=None):
|
|
262
|
+
self.groupings = groupings
|
|
263
|
+
self.derive_mappings(groupings)
|
|
264
|
+
|
|
265
|
+
def get_on(self,arg,level,multiplier):
|
|
266
|
+
"""
|
|
267
|
+
Recursively get the index of an arg on a given level
|
|
268
|
+
|
|
269
|
+
Parameters
|
|
270
|
+
----------
|
|
271
|
+
arg : str, int, list of str, int
|
|
272
|
+
Element to look for.
|
|
273
|
+
level : str
|
|
274
|
+
Key of the level to look into.
|
|
275
|
+
"""
|
|
276
|
+
if isinstance(arg,str):
|
|
277
|
+
if arg == "all":
|
|
278
|
+
return [i*multiplier for i in range(len(self.labels[level]))]
|
|
279
|
+
if arg in self.mappings[level].keys():
|
|
280
|
+
return [
|
|
281
|
+
i*multiplier for i in self.mappings[level][arg]
|
|
282
|
+
]
|
|
283
|
+
return [self.labels[level].index(arg)*multiplier]
|
|
284
|
+
if isinstance(arg,(int,np.integer)):
|
|
285
|
+
return [arg*multiplier]
|
|
286
|
+
sel = []
|
|
287
|
+
for a in arg:
|
|
288
|
+
sel += self.get_on(a,level,multiplier)
|
|
289
|
+
return sel
|
|
290
|
+
|
|
291
|
+
def get_single_ax(self,args):
|
|
292
|
+
"""Make selection for single level Axes"""
|
|
293
|
+
sel = []
|
|
294
|
+
level = self.dimensions[0]
|
|
295
|
+
sel.append(self.get_on(args,level,self.multipliers[level]))
|
|
296
|
+
for level in range(1,self.levels):
|
|
297
|
+
#Fill the rest of the selection with all elements
|
|
298
|
+
sel.append([i*self.multipliers[level] for i in range(len(self.labels[level]))])
|
|
299
|
+
return [sum(arg) for arg in itertools.product(*sel)]
|
|
300
|
+
|
|
301
|
+
def get_labels(self):
|
|
302
|
+
"""Get the labels of the Axe"""
|
|
303
|
+
labels = []
|
|
304
|
+
for key in self.dimensions:
|
|
305
|
+
labels.append(self.labels[key])
|
|
306
|
+
return labels
|
|
307
|
+
|
|
308
|
+
def get_labs(self,args):
|
|
309
|
+
"""Extract the labels corresponding to a given selection of multiple levels
|
|
310
|
+
|
|
311
|
+
Parameters
|
|
312
|
+
----------
|
|
313
|
+
args : list of ints
|
|
314
|
+
Cleaned selection of indices
|
|
315
|
+
|
|
316
|
+
Returns
|
|
317
|
+
-------
|
|
318
|
+
dict
|
|
319
|
+
Dict of labels for each level
|
|
320
|
+
"""
|
|
321
|
+
labels = dict()
|
|
322
|
+
for i,key in enumerate(self.dimensions):
|
|
323
|
+
if isinstance(args[i],(str)):
|
|
324
|
+
labels[key] = [args[i]]
|
|
325
|
+
|
|
326
|
+
else:
|
|
327
|
+
labels[key] = [
|
|
328
|
+
self.labels[key][j//self.multipliers[key]] for j in args[i]
|
|
329
|
+
]
|
|
330
|
+
return labels
|
|
331
|
+
|
|
332
|
+
def get(self,args,labels=False):
|
|
333
|
+
"""
|
|
334
|
+
Get the indices corresponding to a selection on the Axe
|
|
335
|
+
|
|
336
|
+
Parameters
|
|
337
|
+
----------
|
|
338
|
+
args : str, int, dict, list of str, int
|
|
339
|
+
Arguments to select on the Axe.
|
|
340
|
+
If a dict is passed, it is assumed that the keys are the dimensions of the Axe.
|
|
341
|
+
If a list is passed, it is assumed that the first element is the selection on the first level,
|
|
342
|
+
the second on the second level, etc.
|
|
343
|
+
If the selection on multipler levels fails, the selection is assumed to be on the first level only.
|
|
344
|
+
labels : bool, optional
|
|
345
|
+
Whether to return the labels of the selection, by default False
|
|
346
|
+
|
|
347
|
+
Returns
|
|
348
|
+
-------
|
|
349
|
+
list of int or (list of ints, dict, dict)
|
|
350
|
+
If labels is False, returns the indices of the selection.
|
|
351
|
+
If labels is True, returns the indices of the selection,
|
|
352
|
+
the labels of the selection and the groupings of the Axe.
|
|
353
|
+
"""
|
|
354
|
+
if isinstance(args,(int,str,np.integer)):
|
|
355
|
+
if args == "all":
|
|
356
|
+
#Shortcut for all elements
|
|
357
|
+
sel = [i for i in range(len(self))]
|
|
358
|
+
if labels:
|
|
359
|
+
return sel,self.labels.copy(),self.groupings
|
|
360
|
+
return sel
|
|
361
|
+
args = [args]
|
|
362
|
+
if all(isinstance(arg,(int,np.integer)) for arg in args):
|
|
363
|
+
#Shortcut for int based selection
|
|
364
|
+
if labels:
|
|
365
|
+
labels = self.label().copy()
|
|
366
|
+
labs = [labels[arg] for arg in args]
|
|
367
|
+
return args,labs,self.groupings
|
|
368
|
+
return args
|
|
369
|
+
if self.levels == 1:
|
|
370
|
+
if isinstance(args,dict):
|
|
371
|
+
args = list(args.values())
|
|
372
|
+
sel = self.get_on(args,self.dimensions[0],1)
|
|
373
|
+
if labels:
|
|
374
|
+
labs = {
|
|
375
|
+
self.dimensions[0] : [self.labels[self.dimensions[0]][i] for i in sel]
|
|
376
|
+
}
|
|
377
|
+
return sel,labs,self.groupings
|
|
378
|
+
return sel
|
|
379
|
+
sel = []
|
|
380
|
+
|
|
381
|
+
#Preformat the args input
|
|
382
|
+
if isinstance(args,dict):
|
|
383
|
+
#Convert into a list
|
|
384
|
+
filled = []
|
|
385
|
+
for key in self.dimensions:
|
|
386
|
+
if key not in args.keys():
|
|
387
|
+
filled.append(["all"])
|
|
388
|
+
else:
|
|
389
|
+
if isinstance(args[key],list):
|
|
390
|
+
filled.append(args[key])
|
|
391
|
+
else:
|
|
392
|
+
filled.append([args[key]])
|
|
393
|
+
if not all([key in self.dimensions for key in args.keys()]):
|
|
394
|
+
raise IndexError(f"Keys {args.keys()} are invalid in Axe with dimensions {self.dimensions}")
|
|
395
|
+
args = filled
|
|
396
|
+
|
|
397
|
+
if len(args) > self.levels:
|
|
398
|
+
#If the args length exceeds the number of levels,
|
|
399
|
+
#it is assumed that the selection is on the first level only
|
|
400
|
+
return self.get_single_ax(args)
|
|
401
|
+
#Otherwise, try to select on each level
|
|
402
|
+
try:
|
|
403
|
+
for level,arg in enumerate(args):
|
|
404
|
+
lname = self.dimensions[level]
|
|
405
|
+
sel.append(self.get_on(
|
|
406
|
+
arg,lname,self.multipliers[lname]
|
|
407
|
+
))
|
|
408
|
+
for other_dim in range(level+1,self.levels):
|
|
409
|
+
lname = self.dimensions[other_dim]
|
|
410
|
+
sel.append(
|
|
411
|
+
self.get_on(
|
|
412
|
+
"all",lname,self.multipliers[lname])
|
|
413
|
+
)
|
|
414
|
+
composed = [sum(arg) for arg in itertools.product(*sel)]
|
|
415
|
+
if labels:
|
|
416
|
+
labs = self.get_labs(sel)
|
|
417
|
+
return composed,labs,self.groupings
|
|
418
|
+
return composed
|
|
419
|
+
except IndexError:
|
|
420
|
+
#If it fails, try again on a single ax
|
|
421
|
+
return self.get_single_ax(args)
|
|
422
|
+
|
|
423
|
+
def rename_labels(self,old,new):
|
|
424
|
+
"""
|
|
425
|
+
Rename labels in the Axes
|
|
426
|
+
|
|
427
|
+
Parameters
|
|
428
|
+
----------
|
|
429
|
+
old : str
|
|
430
|
+
Former label name
|
|
431
|
+
new : str
|
|
432
|
+
New name
|
|
433
|
+
"""
|
|
434
|
+
if old not in self.labels.keys():
|
|
435
|
+
raise IndexError(f"Label {old} not in the Axe")
|
|
436
|
+
self.labels[new] = self.labels.pop(old)
|
|
437
|
+
self.mappings[new] = self.mappings.pop(old)
|
|
438
|
+
self.dimensions = list(self.labels.keys())
|
|
439
|
+
self.update_multipliers()
|
|
440
|
+
|
|
441
|
+
def replace_label(self,name,labels):
|
|
442
|
+
"""
|
|
443
|
+
Replace a given label
|
|
444
|
+
|
|
445
|
+
Parameters
|
|
446
|
+
----------
|
|
447
|
+
name : str
|
|
448
|
+
Name of the level to replace
|
|
449
|
+
labels : dict
|
|
450
|
+
New labels
|
|
451
|
+
"""
|
|
452
|
+
self.labels.update(labels)
|
|
453
|
+
if name != labels.keys()[0]:
|
|
454
|
+
self.labels.pop(name)
|
|
455
|
+
self.dimensions = list(self.labels.keys())
|
|
456
|
+
self.mappings[labels.keys()[0]] = self.mappings.pop(name)
|
|
457
|
+
self.update_multipliers()
|
|
458
|
+
|
|
459
|
+
def __getitem__(self,*a):
|
|
460
|
+
"""
|
|
461
|
+
Indexing are passed to the get method
|
|
462
|
+
"""
|
|
463
|
+
return self.get(*a)
|
|
464
|
+
|
|
465
|
+
def __eq__(self,other):
|
|
466
|
+
if isinstance(other,Axe):
|
|
467
|
+
if self.labels == other.labels:
|
|
468
|
+
return True
|
|
469
|
+
return False
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def has_dim(self,dim):
|
|
473
|
+
"""Check whether a given dimension is in the Axe"""
|
|
474
|
+
return dim in self.dimensions
|
|
475
|
+
|
|
476
|
+
def __len__(self):
|
|
477
|
+
"""Get the full label length of the Axe"""
|
|
478
|
+
length = 1
|
|
479
|
+
for dim in self.labels.values():
|
|
480
|
+
length *= len(dim)
|
|
481
|
+
return length
|