mrio-toolbox 1.0.0__py3-none-any.whl → 1.1.1__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.

Files changed (59) hide show
  1. mrio_toolbox/__init__.py +18 -2
  2. mrio_toolbox/_parts/_Axe.py +95 -37
  3. mrio_toolbox/_parts/_Part.py +264 -70
  4. mrio_toolbox/_parts/__init__.py +4 -0
  5. mrio_toolbox/_parts/part_operations.py +24 -17
  6. mrio_toolbox/extractors/__init__.py +20 -0
  7. mrio_toolbox/extractors/downloaders.py +36 -0
  8. mrio_toolbox/extractors/emerging/__init__.py +3 -0
  9. mrio_toolbox/extractors/emerging/emerging_extractor.py +117 -0
  10. mrio_toolbox/extractors/eora/__init__.py +3 -0
  11. mrio_toolbox/extractors/eora/eora_extractor.py +132 -0
  12. mrio_toolbox/extractors/exiobase/__init__.py +3 -0
  13. mrio_toolbox/extractors/exiobase/exiobase_extractor.py +270 -0
  14. mrio_toolbox/extractors/extractors.py +79 -0
  15. mrio_toolbox/extractors/figaro/__init__.py +3 -0
  16. mrio_toolbox/extractors/figaro/figaro_downloader.py +280 -0
  17. mrio_toolbox/extractors/figaro/figaro_extractor.py +187 -0
  18. mrio_toolbox/extractors/gloria/__init__.py +3 -0
  19. mrio_toolbox/extractors/gloria/gloria_extractor.py +202 -0
  20. mrio_toolbox/extractors/gtap11/__init__.py +7 -0
  21. mrio_toolbox/extractors/gtap11/extraction/__init__.py +3 -0
  22. mrio_toolbox/extractors/gtap11/extraction/extractor.py +129 -0
  23. mrio_toolbox/extractors/gtap11/extraction/harpy_files/__init__.py +6 -0
  24. mrio_toolbox/extractors/gtap11/extraction/harpy_files/_header_sets.py +279 -0
  25. mrio_toolbox/extractors/gtap11/extraction/harpy_files/har_file.py +262 -0
  26. mrio_toolbox/extractors/gtap11/extraction/harpy_files/har_file_io.py +974 -0
  27. mrio_toolbox/extractors/gtap11/extraction/harpy_files/header_array.py +300 -0
  28. mrio_toolbox/extractors/gtap11/extraction/harpy_files/sl4.py +229 -0
  29. mrio_toolbox/extractors/gtap11/gtap_mrio/__init__.py +6 -0
  30. mrio_toolbox/extractors/gtap11/gtap_mrio/mrio_builder.py +158 -0
  31. mrio_toolbox/extractors/icio/__init__.py +3 -0
  32. mrio_toolbox/extractors/icio/icio_extractor.py +121 -0
  33. mrio_toolbox/extractors/wiod/__init__.py +3 -0
  34. mrio_toolbox/extractors/wiod/wiod_extractor.py +143 -0
  35. mrio_toolbox/mrio.py +254 -94
  36. mrio_toolbox/msm/__init__.py +6 -0
  37. mrio_toolbox/msm/multi_scale_mapping.py +863 -0
  38. mrio_toolbox/utils/__init__.py +3 -0
  39. mrio_toolbox/utils/converters/__init__.py +3 -0
  40. mrio_toolbox/utils/converters/pandas.py +8 -6
  41. mrio_toolbox/utils/converters/xarray.py +2 -13
  42. mrio_toolbox/utils/formatting/__init__.py +0 -0
  43. mrio_toolbox/utils/formatting/formatter.py +528 -0
  44. mrio_toolbox/utils/loaders/__init__.py +4 -0
  45. mrio_toolbox/utils/loaders/_loader.py +60 -4
  46. mrio_toolbox/utils/loaders/_loader_factory.py +22 -1
  47. mrio_toolbox/utils/loaders/_nc_loader.py +37 -1
  48. mrio_toolbox/utils/loaders/_pandas_loader.py +29 -3
  49. mrio_toolbox/utils/loaders/_parameter_loader.py +61 -16
  50. mrio_toolbox/utils/savers/__init__.py +3 -0
  51. mrio_toolbox/utils/savers/_path_checker.py +25 -7
  52. mrio_toolbox/utils/savers/_to_folder.py +6 -1
  53. mrio_toolbox/utils/savers/_to_nc.py +26 -18
  54. {mrio_toolbox-1.0.0.dist-info → mrio_toolbox-1.1.1.dist-info}/METADATA +10 -6
  55. mrio_toolbox-1.1.1.dist-info/RECORD +59 -0
  56. {mrio_toolbox-1.0.0.dist-info → mrio_toolbox-1.1.1.dist-info}/WHEEL +1 -1
  57. mrio_toolbox-1.0.0.dist-info/RECORD +0 -26
  58. {mrio_toolbox-1.0.0.dist-info → mrio_toolbox-1.1.1.dist-info/licenses}/LICENSE +0 -0
  59. {mrio_toolbox-1.0.0.dist-info → mrio_toolbox-1.1.1.dist-info}/top_level.txt +0 -0
@@ -10,6 +10,7 @@ import itertools
10
10
  import numpy as np
11
11
  import pandas as pd
12
12
  import xarray as xr
13
+ import copy
13
14
  from mrio_toolbox._parts._Axe import Axe
14
15
  import logging
15
16
  from mrio_toolbox.utils import converters
@@ -26,46 +27,151 @@ def load_part(
26
27
  return Part(**loader.load_part(**kwargs))
27
28
 
28
29
  class Part:
30
+ """
31
+ Representation of an MRIO Part object.
32
+
33
+ MRIO Parts are the basic building blocks of the MRIO toolbox. A Part is
34
+ built from a numpy array and a set of Axes, corresponding to the dimensions
35
+ of the array. The Axes hold the labels of the Part in the different
36
+ dimensions and are used to perform advanced indexing and operations on the Part.
37
+
38
+ Axes support multi-level indexing and groupings.
39
+
40
+ Instance variables
41
+ ------------------
42
+ data : numpy.ndarray
43
+ Numerical data of the Part.
44
+ axes : list of Axe instances
45
+ Axes corresponding to the dimensions of the Part.
46
+ groupings : dict
47
+ Groupings of the labels of the Part, for each label defined.
48
+ metadata : dict
49
+ Additional metadata of the Part (e.g., path, name, multiplier, unit).
50
+ name : str
51
+ Name of the Part.
52
+ ndim : int
53
+ Number of dimensions of the Part.
54
+ shape : tuple
55
+ Shape of the Part.
56
+
57
+ Methods
58
+ -------
59
+ __init__(data=None, labels=None, axes=None, **kwargs):
60
+ Initialize a Part object.
61
+ alias(**kwargs):
62
+ Create a new Part with modified parameters.
63
+ fix_dims(skip_labels=False, skip_data=False):
64
+ Align the number of axes with the number of dimensions.
65
+ get(*args, aspart=True, squeeze=False):
66
+ Extract data from the Part object.
67
+ setter(value, *args):
68
+ Change the value of a data selection.
69
+ develop(axis=None, on=None, squeeze=True):
70
+ Reshape a Part to avoid double labels.
71
+ reformat(new_dimensions):
72
+ Reshape a Part to match a new dimensions combination.
73
+ combine_axes(start=0, end=None, in_place=False):
74
+ Combine axes of a Part into a single one.
75
+ swap_axes(axis1, axis2):
76
+ Swap two axes of a Part.
77
+ swap_ax_levels(axis, dim1, dim2):
78
+ Swap two levels of an axis.
79
+ flatten(invert=False):
80
+ Flatten a 2D Part into a 1D Part.
81
+ squeeze():
82
+ Remove dimensions of length 1 from the Part.
83
+ expand_dims(axis, copy=None):
84
+ Add dimensions to a Part instance.
85
+ copy():
86
+ Return a copy of the current Part object.
87
+ extraction(dimensions, labels=["all"], on_groupings=True, domestic_only=False, axis="all"):
88
+ Set labels over dimension(s) to 0.
89
+ leontief_inversion():
90
+ Compute the Leontief inverse of a square Part.
91
+ update_groupings(groupings, ax=None):
92
+ Update the groupings of the current Part object.
93
+ aggregate(on="countries", axis=None):
94
+ Aggregate dimensions along one or several axes.
95
+ aggregate_on(on, axis):
96
+ Aggregate a Part along a given axis.
97
+ get_labels(axis=None):
98
+ Returns a list with the labels of the axes as dictionaries.
99
+ list_labels():
100
+ List the labels of the Part.
101
+ get_dimensions(axis=None):
102
+ Return the list of dimensions of the Part.
103
+ rename_labels(old, new):
104
+ Rename some labels of the Part.
105
+ replace_labels(name, labels, axis=None):
106
+ Update a label of the Part.
107
+ set_labels(labels, axis=None):
108
+ Change the labels of the Part.
109
+ add_labels(labels, dimension=None, axes=None, fill_value=0):
110
+ Add indices to one or multiple Part axes.
111
+ expand(axis=None, over="countries"):
112
+ Expand an axis of the Part.
113
+ issquare():
114
+ Check whether the Part is square.
115
+ hasneg():
116
+ Check whether the Part has negative elements.
117
+ hasax(name=None):
118
+ Return the dimensions along which a Part has given labels.
119
+ sum(axis=None, on=None, keepdims=False):
120
+ Sum the Part along one or several axes or on a given dimension.
121
+ save(file=None, name=None, extension=".npy", overwrite=False, include_labels=False, write_instructions=False, **kwargs):
122
+ Save the Part object to a file.
123
+ to_pandas():
124
+ Return the current Part object as a Pandas DataFrame.
125
+ to_xarray():
126
+ Save the Part object to an xarray DataArray.
127
+ mean(axis=None):
128
+ Compute the mean of the Part along a given axis.
129
+ min(axis=None):
130
+ Compute the minimum value of the Part along a given axis.
131
+ max(axis=None):
132
+ Compute the maximum value of the Part along a given axis.
133
+ mul(a, propagate_labels=True):
134
+ Perform matrix multiplication between Parts with label propagation.
135
+ filter(threshold, fill_value=0):
136
+ Set to 0 the values below a given threshold.
137
+ diag():
138
+ Create a diagonal Part from a 1D Part or extract the diagonal of a 2D Part.
139
+ transpose():
140
+ Transpose the Part object.
141
+ """
142
+
29
143
  def __init__(self,data=None,
30
144
  labels=None,
31
145
  axes=None,
32
146
  **kwargs):
33
- """MRIO Parts object
34
-
35
- MRIO Parts are the basic building blocks of the MRIO toolbox.
36
- A Part is built from a numpy array and a set of Axes,
37
- corresponding to the dimensions of the array.
38
- The Axes hold the labels of the Part in the different dimensions
39
- and are used to perform advanced indexing and operations on the Part.
40
-
41
- Axes support multi-level indexing and groupings.
147
+ """
148
+ Initialize a Part object.
42
149
 
43
150
  Parameters
44
151
  ----------
45
- data : numpy array
46
- Numerical data of the part.
47
- If left empty, a Part of zeros (or any other fill value) is created
48
- with a shape matching the axes.
49
- groupings : dict of label level : dict
50
- Groupings of the labels of the Part, for each label defined.
51
- The groupings are passed to the Axe objects.
152
+ data : numpy.ndarray, optional
153
+ Numerical data of the Part. If not provided, a Part filled with
154
+ zeros (or another fill value) is created based on the shape of the axes.
52
155
  labels : list of str or dict, optional
53
- Labels of the axes.
54
- The upper level of the list correspond to each axe (numpy dimension)
55
- of the part
56
- The lower level correspond to the dict of labels for each axe.
57
- Remember that an Axe can have different levels of labels.
156
+ Labels for the axes. If provided, the labels define the structure
157
+ of the axes. If not provided, axes are created based on the data.
58
158
  axes : list of Axe instances, optional
59
- Custom Axes for the Part.
60
- If left empty, the axes are created from the labels.
159
+ Custom Axes for the Part. If not provided, axes are created from
160
+ the labels or inferred from the data.
61
161
  kwargs : dict
62
- Additional metadata of the Part.
63
- (e.g. path, name, multiplier, unit...)
162
+ Additional metadata for the Part (e.g., path, name, multiplier, unit).
64
163
 
65
- Returns
66
- -------
67
- None.
164
+ Raises
165
+ ------
166
+ ValueError
167
+ If the length of the labels does not match the data dimensions.
168
+ TypeError
169
+ If the provided labels are of an unsupported type.
68
170
 
171
+ Notes
172
+ -----
173
+ If both `data` and `axes` are not provided, the method creates an
174
+ empty Part with default axes and zero-filled data.
69
175
  """
70
176
 
71
177
  if data is not None:
@@ -128,6 +234,7 @@ class Part:
128
234
  new_dims[-1].append(dim)
129
235
 
130
236
  if new_dims != self.get_dimensions():
237
+
131
238
  log.info("Reformat the Part")
132
239
  new_part = self.reformat(new_dims)
133
240
  self.data = new_part.data
@@ -257,13 +364,15 @@ class Part:
257
364
 
258
365
 
259
366
  def __getitem__(self,args):
260
- if isinstance(args,str) or isinstance(args,int) or isinstance(args,np.integer):
367
+ if isinstance(args,str) or isinstance(args,int) or isinstance(args,np.integer) or isinstance(args,dict):
261
368
  args = (args,)
262
369
  return self.get(*args)
263
370
 
264
371
  def __setitem__(self,args,value):
265
372
  if isinstance(value,Part):
266
373
  value = value.data
374
+ if isinstance(args,str) or isinstance(args,int) or isinstance(args,np.integer) or isinstance(args,dict):
375
+ args = (args,)
267
376
  self.setter(value,*args)
268
377
 
269
378
  def setter(self,value,*args):
@@ -319,7 +428,6 @@ class Part:
319
428
  Returns
320
429
  -------
321
430
  New Part object or numpy object
322
-
323
431
  """
324
432
  sels = []
325
433
  axes = []
@@ -379,12 +487,11 @@ class Part:
379
487
  squeeze : bool, optional
380
488
  Whether to remove dimensions of length 1.
381
489
  The default is True.
382
-
490
+
383
491
  Returns
384
492
  -------
385
- Part object
386
- Developped Part
387
-
493
+ Developped Part : Part object
494
+ The developed part
388
495
  """
389
496
  if isinstance(on,str):
390
497
  on = [on]
@@ -414,6 +521,7 @@ class Part:
414
521
  "This operation is not yet supported."
415
522
  )
416
523
  #If the order of the dimensions is unchanged, we can simply reshape
524
+
417
525
  shape = [len(ax) for ax in axes]
418
526
  data = self.data.reshape(shape)
419
527
  if squeeze:
@@ -423,35 +531,42 @@ class Part:
423
531
  return Part(data=data,name=f"developped_{self.name}",
424
532
  groupings=self.groupings,axes=axes)
425
533
 
426
- def reformat(self,new_dimensions):
534
+ def reformat(self, new_dimensions):
427
535
  """
428
- Reshape a Part to match a new dimensions combination
536
+ Reshape a Part to match a new dimensions combination.
429
537
 
430
538
  Equivalent to a combination of the develop and combine_axes methods.
431
539
 
432
540
  This only works for contiguous dimensions in the current Part,
433
541
  without overlapping dimensions.
434
- For example, if the Part has dimensions:
435
- [["countries"],["sectors"],["sectors"]]
436
- The following is allowed:
437
- [["countries","sectors"],["sectors"]]
438
- The following is not allowed:
439
- [["countries"],["sectors","sectors"]]
440
- [["sectors"],["countries","sectors"]]
441
- [["sectors","countries"],["sectors"]]
442
542
 
443
543
  Parameters
444
544
  ----------
445
- dimensions : list of list of str
446
- Original dimensions of the Part
545
+ new_dimensions : list of list of str
546
+ Target dimensions to reshape into.
447
547
 
448
548
  Returns
449
549
  -------
450
- data : numpy array
451
- Reshaped data
452
- axes : list of Axe instances
453
- Reshaped axes
550
+ data : numpy.ndarray
551
+ Reshaped data.
552
+ axes : list of Axe
553
+ Reshaped axes.
554
+
555
+ Examples
556
+ --------
557
+ If the Part has dimensions::
558
+
559
+ [["countries"], ["sectors"], ["sectors"]]
560
+
561
+ The following is allowed::
454
562
 
563
+ [["countries", "sectors"], ["sectors"]]
564
+
565
+ The following is not allowed::
566
+
567
+ [["countries"], ["sectors", "sectors"]]
568
+ [["sectors"], ["countries", "sectors"]]
569
+ [["sectors", "countries"], ["sectors"]]
455
570
  """
456
571
  return part_operations.reformat(self,new_dimensions)
457
572
 
@@ -661,14 +776,6 @@ class Part:
661
776
  dimensions = [dimensions]
662
777
  if isinstance(labels,str) and labels!="all":
663
778
  labels = [labels]
664
- if len(labels) != len(dimensions):
665
- if len(dimensions)==1:
666
- #If only one dimension is passed, we broadcast the labels
667
- labels = [labels]
668
- else:
669
- #Raise an error for ambiguous cases
670
- log.critical("Number of dimensions and labels do not match for extraction")
671
- raise ValueError("Number of dimensions and labels do not match for extraction")
672
779
  if isinstance(dimensions,dict):
673
780
  to_select = dimensions
674
781
  labels = list(to_select.values())
@@ -677,14 +784,30 @@ class Part:
677
784
  to_select = dict()
678
785
  for dim,label in zip(dimensions,labels):
679
786
  to_select[dim] = label
787
+ if len(labels) != len(dimensions):
788
+ if len(dimensions)==1:
789
+ #If only one dimension is passed, we broadcast the labels
790
+ labels = [labels]
791
+ else:
792
+ #Raise an error for ambiguous cases
793
+ log.critical("Number of dimensions and labels do not match for extraction")
794
+ raise ValueError("Number of dimensions and labels do not match for extraction")
680
795
 
681
796
  allowed = []
682
797
  for i,ax in enumerate(self.axes):
683
798
  if all(dimension in ax.dimensions for dimension in dimensions):
684
799
  allowed.append(i)
685
800
  if len(allowed) == 0:
686
- log.critical("No axis found for extraction on "+str(dimensions))
687
- raise ValueError("No axis found for extraction on "+str(dimensions))
801
+ if len(dimensions) == 1:
802
+ log.critical("No axis found for extraction on "+str(dimensions))
803
+ raise ValueError("No axis found for extraction on "+str(dimensions))
804
+ log.info(f"No axis found for simultaneous extractions on {dimensions}")
805
+ log.info(f"Try successive extractions on {dimensions}")
806
+ for dim,label in zip(dimensions,labels):
807
+ self.extraction(dim,label,
808
+ on_groupings=on_groupings,
809
+ domestic_only=domestic_only,
810
+ axis=axis)
688
811
  if axis == "all":
689
812
  log.info(f"Extract {to_select} on axes "+ str(allowed))
690
813
  axis = allowed
@@ -820,7 +943,7 @@ class Part:
820
943
 
821
944
  if isinstance(on,list):
822
945
  for item in on:
823
- self = self.aggregate(axis,item)
946
+ self = self.aggregate(on = item, axis=axis)
824
947
  return self
825
948
  if on not in self.groupings.keys():
826
949
  raise ValueError(f"No groupings defined for dimensions {on}")
@@ -871,8 +994,8 @@ class Part:
871
994
 
872
995
  output = Part(axes=new_axis)
873
996
  idsum = new_axis[axis].dimensions.index(on) #Index of the dimension to sum on
874
- ref_dev = self.develop(axis)
875
- new_dev = output.develop(axis)
997
+ ref_dev = self.develop(axis, squeeze=False)
998
+ new_dev = output.develop(axis,squeeze=False)
876
999
  selector = ["all"]*ref_dev.ndim
877
1000
  for label in new_labels[on]:
878
1001
  selector[axis+idsum] = label
@@ -887,8 +1010,9 @@ class Part:
887
1010
 
888
1011
  def get_labels(self,axis=None):
889
1012
  """
890
- Returns the dictionnary of the Part labels
891
-
1013
+ Returns a list with the labels of each axis
1014
+ of the part in a the dictionary.
1015
+
892
1016
  Parameters
893
1017
  ----------
894
1018
  axis : int or list of int, optional
@@ -1035,12 +1159,12 @@ class Part:
1035
1159
  Value used to initialize the new Part
1036
1160
 
1037
1161
  Returns
1038
- ----------
1162
+ -------
1039
1163
  Part instance
1040
1164
  Part instance with the additional ax indices.
1041
1165
 
1042
1166
  Raise
1043
- ----------
1167
+ -----
1044
1168
  ValueError
1045
1169
  A Value Error is raised if neither the axes nor the
1046
1170
  ref_set arguments are set.
@@ -1073,6 +1197,67 @@ class Part:
1073
1197
  output[sel] = self.data
1074
1198
  return output
1075
1199
 
1200
+ def reorder_data(self,new_labels):
1201
+ """
1202
+ Reorder the data of the Part according to new labels.
1203
+
1204
+ Parameters
1205
+ ----------
1206
+ new_labels : dict
1207
+ New labels for the axes.
1208
+ The keys are the dimensions, the values are the labels.
1209
+
1210
+ Raises
1211
+ ------
1212
+ ValueError
1213
+ If the new labels do not match the current axes.
1214
+ """
1215
+
1216
+ if not isinstance(new_labels,dict):
1217
+ raise ValueError("New labels should be a dictionary")
1218
+
1219
+
1220
+
1221
+ sels = []
1222
+ for axis in self.axes:
1223
+ old_labels = axis.labels
1224
+ if not set(new_labels.keys()).issubset(set(old_labels.keys())):
1225
+ sels.append(axis.get("all"))
1226
+ continue
1227
+ for key in new_labels.keys():
1228
+ set_old = set(old_labels[key])
1229
+ set_new = set(new_labels[key])
1230
+ if not set_old.issubset(set_new):
1231
+ raise ValueError(f"The new labels provided for dimension '{key}' is not a superset of the old labels. " +
1232
+ f"Old labels: {old_labels[key]}, new labels: {new_labels[key]}. "
1233
+ "If you want to rename the labels of this dimensions, use the method 'replace_labels() before reordering the data")
1234
+
1235
+
1236
+ ax_label_dict = {}
1237
+ for key in old_labels.keys():
1238
+ if key in new_labels.keys():
1239
+ ax_label_dict[key] = new_labels[key]
1240
+ for lab in new_labels[key]:
1241
+ if lab not in old_labels[key]:
1242
+ # If the label is not in the list, remove it
1243
+ ax_label_dict[key].remove(lab)
1244
+ else:
1245
+ ax_label_dict[key] = old_labels[key]
1246
+
1247
+ sels.append(axis.get(ax_label_dict))
1248
+
1249
+ if len(sels) == 0:
1250
+ raise ValueError(f"None of the dimensions provided in the new labels dict {new_labels.keys()} are present "+
1251
+ f"in the labels of part '{self.name}', which only contains the dimensions {self.get_dimensions()}")
1252
+
1253
+ #Execute the selection
1254
+ self.data = self.data[np.ix_(*sels)]
1255
+
1256
+ # Update the axes with the new labels
1257
+ for dim in new_labels.keys():
1258
+ self.replace_labels(name = dim, labels = new_labels[dim])
1259
+
1260
+
1076
1261
  def expand(self,axis=None,over="countries"):
1077
1262
  """
1078
1263
  Expand an axis of the Part
@@ -1212,10 +1397,13 @@ class Part:
1212
1397
  raise ValueError(f"Cannot sum on {on} as it is not a dimension of axis {axis}")
1213
1398
  if ax.levels == 1:
1214
1399
  #If the axis has a single level, this is a simple sum
1400
+ axes = self.axes.copy()
1401
+ if not keepdims:
1402
+ del axes[axis]
1215
1403
  return self.alias(
1216
1404
  data = self.data.sum(axis,keepdims=keepdims),
1217
1405
  name=f"{self.name}_sum_{axis}",
1218
- axes = self.axes
1406
+ axes = axes
1219
1407
  )
1220
1408
  #Otherwise, sum on the relevant levels
1221
1409
  idsum = ax.dimensions.index(on) #Index of the dimension to sum on
@@ -1280,7 +1468,7 @@ class Part:
1280
1468
  if path is None:
1281
1469
  raise FileNotFoundError("No path specified for saving the Part")
1282
1470
  if extension == ".nc":
1283
- path = os.path.join(path,name)
1471
+ path = os.path.join(path,name+extension)
1284
1472
  save_to_nc(self,path,overwrite,
1285
1473
  write_instructions=write_instructions,
1286
1474
  **kwargs)
@@ -1385,10 +1573,16 @@ class Part:
1385
1573
 
1386
1574
  def diag(self):
1387
1575
  if self.ndim == 1:
1576
+ log.info("Diagonalize a 1D part")
1388
1577
  return self.alias(data=np.diag(self.data),
1389
1578
  name=f"diag_{self.name}",
1390
1579
  axes = self.axes*2)
1391
-
1580
+ try:
1581
+ log.info("The part has too many dimensions: try to diagonalize the squeezed part")
1582
+ return self.squeeze().diag()
1583
+ except:
1584
+ raise ValueError("Cannot diagonalize a part with more than 2 dimensions")
1585
+
1392
1586
  def __add__(self,a):
1393
1587
  if isinstance(a,Part):
1394
1588
  name = a.name
@@ -1,3 +1,7 @@
1
+ """
2
+ This module provides the Part and Axe classes.
3
+ """
4
+
1
5
  from ._Part import Part,load_part
2
6
 
3
7
  __all__ = ['Part','load_part']
@@ -9,42 +9,49 @@ at a later point.
9
9
 
10
10
  def reformat(part,new_dimensions):
11
11
  """
12
- Reshape a Part to match a new dimensions combination
12
+ Reshape a Part to match a new dimensions combination.
13
13
 
14
14
  Equivalent to a combination of the develop and combine_axes methods.
15
15
 
16
16
  This only works for contiguous dimensions in the current Part,
17
17
  without overlapping dimensions.
18
- For example, if the Part has dimensions:
19
- [["countries"],["sectors"],["sectors"]]
20
- The following is allowed:
21
- [["countries","sectors"],["sectors"]]
22
- The following is not allowed:
23
- [["countries"],["sectors","sectors"]]
24
- [["sectors"],["countries","sectors"]]
25
- [["sectors","countries"],["sectors"]]
26
18
 
27
19
  Parameters
28
20
  ----------
29
- dimensions : list of list of str
30
- Original dimensions of the Part
21
+ new_dimensions : list of list of str
22
+ Target dimensions to reshape into.
31
23
 
32
24
  Returns
33
25
  -------
34
- data : numpy array
35
- Reshaped data
36
- axes : list of Axe instances
37
- Reshaped axes
26
+ data : numpy.ndarray
27
+ Reshaped data.
28
+ axes : list of Axe
29
+ Reshaped axes.
38
30
 
31
+ Examples
32
+ --------
33
+ If the Part has dimensions::
34
+
35
+ [["countries"], ["sectors"], ["sectors"]]
36
+
37
+ The following is allowed::
38
+
39
+ [["countries", "sectors"], ["sectors"]]
40
+
41
+ The following is not allowed::
42
+
43
+ [["countries"], ["sectors", "sectors"]]
44
+ [["sectors"], ["countries", "sectors"]]
45
+ [["sectors", "countries"], ["sectors"]]
39
46
  """
40
47
  def formatting_iteration(part,new_dimensions):
41
48
  if part.get_dimensions() == new_dimensions:
42
49
  return part
43
50
  for i,dim in enumerate(part.get_dimensions()):
44
51
  if dim != new_dimensions[i]:
45
- part = part.combine_axes(i,i+len(new_dimensions)-1)
52
+ part = part.combine_axes(i,i+len(new_dimensions[i])-1)
46
53
  return formatting_iteration(part,new_dimensions)
47
- developed = part.develop()
54
+ developed = part.develop(squeeze = False)
48
55
  return formatting_iteration(developed,new_dimensions)
49
56
 
50
57
 
@@ -0,0 +1,20 @@
1
+ """
2
+ This module provides functions to extract raw MRIO data from various providers.
3
+ Given the raw data files, it allows to build an MRIO object to be used with this library.
4
+ """
5
+ from .extractors import *
6
+ from .downloaders import *
7
+
8
+ __all__ = [
9
+ "extract_MRIO",
10
+ "extract_eora",
11
+ "extract_gloria",
12
+ "extract_wiod",
13
+ "extract_exiobase",
14
+ "extract_figaro",
15
+ "extract_emerging",
16
+ "extract_gtap",
17
+ "extract_icio",
18
+ "download_MRIO",
19
+ "download_figaro"
20
+ ]
@@ -0,0 +1,36 @@
1
+ """
2
+ Module for extracting and converting data from various sources.
3
+ """
4
+
5
+ import logging as log
6
+ log = log.getLogger(__name__)
7
+
8
+
9
+ def download_MRIO(table,year,destination,**kwargs):
10
+ """
11
+ Downloads the MRIO from the internet and saves it to the specified destination.
12
+
13
+ Specific downloaders are called based on the table name.
14
+ Refer to the individual downloader functions for more details.
15
+
16
+ Parameters
17
+ ----------
18
+ table : str
19
+ Name of the MRIO table to extract. Currently supported:
20
+
21
+ - 'figaro': Downloading FIGARO data.
22
+
23
+ year : str
24
+ Year of the data to extract.
25
+ destination : path-like
26
+ Path to the destination directory where the NetCDF file will be saved.
27
+ **kwargs : dict
28
+ Additional keyword arguments specific to the extractor function.
29
+ For example, `extended` for WIOD extraction to specify if extended data should be included.
30
+ """
31
+ log.info(f"Download MRIO data for table '{table}' for year {year} to the folder {destination}")
32
+ if table == 'figaro':
33
+ from mrio_toolbox.extractors.figaro.figaro_downloader import download_figaro
34
+ download_figaro(year, destination, **kwargs)
35
+ else:
36
+ raise ValueError(f"Downloader for table '{table}' is not implemented yet. Currently supported: 'figaro'.")
@@ -0,0 +1,3 @@
1
+ """
2
+ This module contains the emerging extractor
3
+ """