Example use with Figaro tables¶
Download and extract data¶
This notebook gives examples on how to use the MRIO toolbox. As MRIO tables, the figaro tables are automatically downloaded and processed into an MRIO object which the toolbox can use.
Currently, the automatic downloader is only implemented for the Figaro tables from Eurostat. However, the extractors are implemented for the following tables:
Eora26
Exiobase3
Figaro
Gloria
GTAP11
ICIO
WIOD
So any of those tables can be used with the toolbox, but you have to download the raw data manually, which sometimes involves registering an account.
The following script will give an overview over the most important functions of the toolbox and guide you through a small IO analysis. First we load the data into an mrio object named ‘figaro’ (note that this operation implies downloading the whole figaro table, which might take a few minutes)
[1]:
import os
import numpy as np
import warnings
from mrio_toolbox import MRIO, download_MRIO, extract_MRIO
# Suppress warnings about duplicate dimension names from xarray.namedarray
warnings.filterwarnings("ignore", category=UserWarning, module=r"xarray\.namedarray\.core")
# Make new directories to store the downloaded data (raw) and the netCDF file (formatted)
workbookDir = os.getcwd()
os.makedirs(os.path.join(workbookDir, 'figaro_data'), exist_ok=True)
os.makedirs(os.path.join(workbookDir, 'figaro_data', 'raw'), exist_ok=True)
os.makedirs(os.path.join(workbookDir, 'figaro_data', 'formatted'), exist_ok=True)
source = os.path.join(workbookDir, 'figaro_data', 'raw')
destination = os.path.join(workbookDir, 'figaro_data', 'formatted')
# Define the year and table to download. The figaro downloader currently supports years 2010 to 2023
year = 2023
table = 'figaro'
# Download the Figaro table from its website
download_MRIO(
table=table,
year = year,
destination=source)
# Extract the downloaded Figaro tables into a formatted MRIO netCDF file
extract_MRIO(
table=table,
year = year,
source=source,
destination=destination)
# Load the formatted MRIO netCDF file into a MRIO object
path = os.path.join(destination, f"{table}_year{year}.nc")
figaro = MRIO(file = destination+".nc")
Inspecting the mrio object¶
Metadata¶
We first take a glance at the metadata:
[2]:
figaro.metadata
[2]:
{'file': 'c:\\Users\\beaufils\\Documents\\Projects\\scripts\\mrio_toolbox\\example\\figaro_data\\formatted.nc',
'table': 'figaro',
'edition': np.int64(25),
'year': np.int64(2023),
'format': 'industry by industry',
'sut': 'none',
'parts': {}}
We see that the year is 2023, we’re using the 2025 edition of figaro and the format is ‘industry by industry’ (an alternative would be ‘product by product’). The ‘sut’ variable tells us that the mrio object does not contain any supply or use tables.
Labels¶
Each MRIO object comes with a set of labels that are used for indexing the tables. They are stored in the ‘labels’ dict:
[3]:
figaro.labels.keys()
[3]:
dict_keys(['countries', 'sectors', 'y_labs', 'va_labs'])
The entries of the labels dict are also called ‘dimensions’. Our figaro object has country and sector dimensions as well as labels for value added and final demand.
We check which labels are present in the MRIO object:
[4]:
print(f"Countries: {figaro.labels["countries"]}")
print(f"Sectors: {figaro.labels["sectors"]}")
print(f"y_labs: {figaro.labels["y_labs"]}")
print(f"va_labs: {figaro.labels["va_labs"]}")
Countries: ['AL', 'AR', 'AT', 'AU', 'BE', 'BG', 'BR', 'CA', 'CH', 'CN', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FIGW1', 'FR', 'GB', 'GR', 'HR', 'HU', 'ID', 'IE', 'IN', 'IT', 'JP', 'KR', 'LT', 'LU', 'LV', 'ME', 'MK', 'MT', 'MX', 'NL', 'NO', 'PL', 'PT', 'RO', 'RS', 'RU', 'SA', 'SE', 'SI', 'SK', 'TR', 'US', 'ZA']
Sectors: ['Crop and animal production, hunting and related service activities', 'Forestry and logging', 'Fishing and aquaculture', 'Mining and quarrying', 'Manufacture of food products; beverages and tobacco products', 'Manufacture of textiles, wearing apparel, leather and related products', 'Manufacture of wood and of products of wood and cork, except furniture; manufacture of articles of straw and plaiting materials', 'Manufacture of paper and paper products', 'Printing and reproduction of recorded media', 'Manufacture of coke and refined petroleum products', 'Manufacture of chemicals and chemical products', 'Manufacture of basic pharmaceutical products and pharmaceutical preparations', 'Manufacture of rubber and plastic products', 'Manufacture of other non-metallic mineral products', 'Manufacture of basic metals', 'Manufacture of fabricated metal products, except machinery and equipment', 'Manufacture of computer, electronic and optical products', 'Manufacture of electrical equipment', 'Manufacture of machinery and equipment n.e.c.', 'Manufacture of motor vehicles, trailers and semi-trailers', 'Manufacture of other transport equipment', 'Manufacture of furniture; other manufacturing', 'Repair and installation of machinery and equipment', 'Electricity, gas, steam and air conditioning supply', 'Water collection, treatment and supply', 'Sewerage, waste management, remediation activities', 'Construction', 'Wholesale and retail trade and repair of motor vehicles and motorcycles', 'Wholesale trade, except of motor vehicles and motorcycles', 'Retail trade, except of motor vehicles and motorcycles', 'Land transport and transport via pipelines', 'Water transport', 'Air transport', 'Warehousing and support activities for transportation', 'Postal and courier activities', 'Accommodation and food service activities', 'Publishing activities', 'Motion picture, video, television programme production; programming and broadcasting activities', 'Telecommunications', 'Computer programming, consultancy, and information service activities', 'Financial service activities, except insurance and pension funding', 'Insurance, reinsurance and pension funding, except compulsory social security', 'Activities auxiliary to financial services and insurance activities', 'Real estate activities', 'Legal and accounting activities; activities of head offices; management consultancy activities', 'Architectural and engineering activities; technical testing and analysis', 'Scientific research and development', 'Advertising and market research', 'Other professional, scientific and technical activities; veterinary activities', 'Rental and leasing activities', 'Employment activities', 'Travel agency, tour operator and other reservation service and related activities', 'Security and investigation, service and landscape, office administrative and support activities', 'Public administration and defence; compulsory social security', 'Education', 'Human health activities', 'Residential care activities and social work activities without accommodation', 'Creative, arts and entertainment activities; libraries, archives, museums and other cultural activities; gambling and betting activities', 'Sports activities and amusement and recreation activities', 'Activities of membership organisations', 'Repair of computers and personal and household goods', 'Other personal service activities', 'Activities of households as employers; undifferentiated goods- and services-producing activities of households for own use', 'Activities of extraterritorial organisations and bodies']
y_labs: ['Final consumption expenditure of general government', 'Final consumption expenditure of households', 'Final consumption expenditure of non-profit institutions serving households', 'Gross fixed capital formation', 'Changes in inventories and acquisition less disposals of valuables']
va_labs: ['Taxes less subsidies on products', 'Purchases of non-residents in the domestic territory', 'Direct purchase abroad by residents', 'Compensation of employees', 'Other net taxes on production', 'Gross operating surplus']
Parts¶
Each MRIO object has several parts:
[5]:
figaro.parts
[5]:
{'t': <mrio_toolbox._parts._Part.Part at 0x15d2e63e250>,
'y': <mrio_toolbox._parts._Part.Part at 0x15d2c50f230>,
'va': <mrio_toolbox._parts._Part.Part at 0x15d2a04d190>,
'vay': <mrio_toolbox._parts._Part.Part at 0x15d241ac5f0>}
In our case, we have four parts:
The t table, which holds the inter-industry transactions
The y table, which holds the final demand
The va table, which holds taxes and value added corresponding to the inter-industry transactions
The vay table, which holds taxes and value added corresponding to the final demand
Parts can be accessed with the following syntax:
[6]:
figaro.va
[6]:
<mrio_toolbox._parts._Part.Part at 0x15d2a04d190>
Axes¶
Each part object can have multiple axes. Usually in MRIO analysis, each part has two axes. Each axis on the other hand may hold multiple dimensions, which allows for a multi-index functionality. The axes are aranged like numpy axes and are counted with an index starting from zero.
For example, the t matrix holds two axes:
The 0th axis is the vertical axis. It indicates interindustry sales from a country-sector combination
The 1st axis is the horizontal axis. It indicates interindustry sales to a country-sector combination
Likewise, the y matrix also holds two axes:
The 0th axis is the vertical axis. It indicates sales from a country-sector combination for final demand purposes.
The 1th axis is the horizontal axis. It indicates to which country and final demand sector (e.g. Government or Households) the sale is going.
[7]:
print(f"Axis 0 of the t part is holding the dimensions: {figaro.t.axes[0].dimensions}")
print(f"Axis 1 of the t part is holding the dimensions: {figaro.t.axes[1].dimensions}")
print()
print(f"Axis 0 of the y part is holding the dimensions: {figaro.y.axes[0].dimensions}")
print(f"Axis 1 of the y part is holding the dimensions: {figaro.y.axes[1].dimensions}")
Axis 0 of the t part is holding the dimensions: ['countries', 'sectors']
Axis 1 of the t part is holding the dimensions: ['countries', 'sectors']
Axis 0 of the y part is holding the dimensions: ['countries', 'sectors']
Axis 1 of the y part is holding the dimensions: ['countries', 'y_labs']
Selecting partitions of a part¶
The indexing function of the toolbox is helpful, if you want to select partitions of a part. Let’s assume you want to know the value of inputs from the Czech ‘Mining and quarrying’ sector into the Polish ‘Construction’ sector.
[8]:
figaro.t[["CZ","Mining and quarrying"], ["PL","Construction"]]
[8]:
<mrio_toolbox._parts._Part.Part at 0x15d219e7800>
This operation returns another part object. For accessing the data of the part object, you can access it’s data attribute:
[9]:
figaro.t[["CZ","Mining and quarrying"], ["PL","Construction"]].data
[9]:
array([[1.157]])
We see that in 2023, the Polish ‘Construction’ sector used products worth of 1.157 million € of the Czech ‘Mining and quarrying’ sector as input.
Often times, you want to safe the part tables to their own variable so you don’t have to write ‘figaro’ in front of every operation. You can also use the .to_pandas() function instead of the .data attribute to nicely format the content of a part or a partitioned part object.
[10]:
t = figaro.t
t[["CZ","Mining and quarrying"], ["PL","Construction"]].to_pandas()
[10]:
| countries | PL | |
|---|---|---|
| sectors | Construction | |
| countries | sectors | |
| CZ | Mining and quarrying | 1.157 |
Take a look at the operation again. The first inner parenthesis selects on axis 0 and the second inner parenthesis selects on axis 1. Within each inner parenthesis, we are first denoting the country and then the sector, because the countries are the axe and the sectors are the second dimension.
If we leave out the second dimension, the toolbox automatically fills it with the keyword “all”, indicating that all entries of the second dimension should be selected.
[11]:
t[["CZ"], ["PL","Construction"]].to_pandas()
[11]:
| countries | PL | |
|---|---|---|
| sectors | Construction | |
| countries | sectors | |
| CZ | Crop and animal production, hunting and related service activities | 0.626 |
| Forestry and logging | 1.083 | |
| Fishing and aquaculture | 0.001 | |
| Mining and quarrying | 1.157 | |
| Manufacture of food products; beverages and tobacco products | 1.813 | |
| ... | ... | |
| Activities of membership organisations | 0.018 | |
| Repair of computers and personal and household goods | 0.043 | |
| Other personal service activities | 0.036 | |
| Activities of households as employers; undifferentiated goods- and services-producing activities of households for own use | 0.000 | |
| Activities of extraterritorial organisations and bodies | 0.000 |
64 rows × 1 columns
This gives us the input of all Czech sectors into the Polish construction sector. If we only have one element per axis, we can also write it without the inner parentheses:
[12]:
t["CZ", ["PL","Construction"]].to_pandas()
[12]:
| countries | PL | |
|---|---|---|
| sectors | Construction | |
| countries | sectors | |
| CZ | Crop and animal production, hunting and related service activities | 0.626 |
| Forestry and logging | 1.083 | |
| Fishing and aquaculture | 0.001 | |
| Mining and quarrying | 1.157 | |
| Manufacture of food products; beverages and tobacco products | 1.813 | |
| ... | ... | |
| Activities of membership organisations | 0.018 | |
| Repair of computers and personal and household goods | 0.043 | |
| Other personal service activities | 0.036 | |
| Activities of households as employers; undifferentiated goods- and services-producing activities of households for own use | 0.000 | |
| Activities of extraterritorial organisations and bodies | 0.000 |
64 rows × 1 columns
Note that the “all” keyword is not automatically inserted for the first dimension of an axis, only for the second. E.g. this will result in an error, because the sectors lie on the second dimension of the axis:
[13]:
try:
t["Mining and quarrying", ["PL","Construction"]].to_pandas()
except ValueError as e:
print(f"ValueError: {e}")
ValueError: 'Mining and quarrying' is not in list
Instead we need to provide the “all” keyword to the first dimension of the axis:
[14]:
t[["all", "Mining and quarrying"], ["PL","Construction"]].to_pandas()
[14]:
| countries | PL | |
|---|---|---|
| sectors | Construction | |
| countries | sectors | |
| AL | Mining and quarrying | 0.012 |
| AR | Mining and quarrying | 0.000 |
| AT | Mining and quarrying | 0.153 |
| AU | Mining and quarrying | 2.506 |
| BE | Mining and quarrying | 0.124 |
| BG | Mining and quarrying | 0.330 |
| BR | Mining and quarrying | 2.869 |
| CA | Mining and quarrying | 0.837 |
| CH | Mining and quarrying | 0.001 |
| CN | Mining and quarrying | 0.351 |
| CY | Mining and quarrying | 0.004 |
| CZ | Mining and quarrying | 1.157 |
| DE | Mining and quarrying | 3.017 |
| DK | Mining and quarrying | 0.233 |
| EE | Mining and quarrying | 0.121 |
| ES | Mining and quarrying | 0.205 |
| FI | Mining and quarrying | 1.076 |
| FIGW1 | Mining and quarrying | 101.852 |
| FR | Mining and quarrying | 0.013 |
| GB | Mining and quarrying | 10.426 |
| GR | Mining and quarrying | 0.026 |
| HR | Mining and quarrying | 0.007 |
| HU | Mining and quarrying | 0.131 |
| ID | Mining and quarrying | 1.478 |
| IE | Mining and quarrying | 0.140 |
| IN | Mining and quarrying | 0.044 |
| IT | Mining and quarrying | 0.960 |
| JP | Mining and quarrying | 0.103 |
| KR | Mining and quarrying | 0.054 |
| LT | Mining and quarrying | 0.153 |
| LU | Mining and quarrying | 0.003 |
| LV | Mining and quarrying | 0.079 |
| ME | Mining and quarrying | 0.021 |
| MK | Mining and quarrying | 0.088 |
| MT | Mining and quarrying | 0.003 |
| MX | Mining and quarrying | 0.015 |
| NL | Mining and quarrying | 0.607 |
| NO | Mining and quarrying | 66.595 |
| PL | Mining and quarrying | 1391.199 |
| PT | Mining and quarrying | 0.019 |
| RO | Mining and quarrying | 0.001 |
| RS | Mining and quarrying | 0.051 |
| RU | Mining and quarrying | 5.273 |
| SA | Mining and quarrying | 54.835 |
| SE | Mining and quarrying | 0.305 |
| SI | Mining and quarrying | 0.144 |
| SK | Mining and quarrying | 0.538 |
| TR | Mining and quarrying | 0.468 |
| US | Mining and quarrying | 7.836 |
| ZA | Mining and quarrying | 3.196 |
This gives us the sales from the ‘Mining and quarrying’ sector of all countries to Poland’s construction sector.
We can of course also select from other part objects. E.g. we want to find the final demand of products form the French textile sector by all German and Austrian housholds
[15]:
y = figaro.y
y[["FR", "Manufacture of textiles, wearing apparel, leather and related products"],[["DE", "AT"], "Final consumption expenditure of households"]].to_pandas()
[15]:
| countries | DE | AT | |
|---|---|---|---|
| y_labs | Final consumption expenditure of households | Final consumption expenditure of households | |
| countries | sectors | ||
| FR | Manufacture of textiles, wearing apparel, leather and related products | 190.748 | 39.009 |
Aggregating Data¶
The MRIO toolbox offers two main ways of aggregating data. First, you can sum parts along an axis and a dimension. Second, you can specify to group certain labels together in a grouping file.
Summing along an axis and dimension¶
Say you want to sum the vertical axis of the t table along the countries dimension:
[16]:
t.sum(axis=0, on="countries").to_pandas()
[16]:
| countries | AL | ... | ZA | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| sectors | Crop and animal production, hunting and related service activities | Forestry and logging | Fishing and aquaculture | Mining and quarrying | Manufacture of food products; beverages and tobacco products | Manufacture of textiles, wearing apparel, leather and related products | Manufacture of wood and of products of wood and cork, except furniture; manufacture of articles of straw and plaiting materials | Manufacture of paper and paper products | Printing and reproduction of recorded media | Manufacture of coke and refined petroleum products | ... | Education | Human health activities | Residential care activities and social work activities without accommodation | Creative, arts and entertainment activities; libraries, archives, museums and other cultural activities; gambling and betting activities | Sports activities and amusement and recreation activities | Activities of membership organisations | Repair of computers and personal and household goods | Other personal service activities | Activities of households as employers; undifferentiated goods- and services-producing activities of households for own use | Activities of extraterritorial organisations and bodies |
| sectors | |||||||||||||||||||||
| Crop and animal production, hunting and related service activities | 944.914 | 0.647 | 2.334 | 2.619 | 257.135 | 23.816 | 0.275 | 0.256 | 0.030 | 0.000 | ... | 24.793 | 56.146 | 14.710 | 3.038 | 52.480 | 6.232 | 3.309 | 3.765 | 0.0 | 0.0 |
| Forestry and logging | 0.347 | 3.133 | 0.000 | 1.893 | 0.114 | 0.000 | 3.374 | 0.000 | 0.000 | 0.000 | ... | 0.007 | 2.791 | 0.000 | 0.000 | 0.003 | 0.000 | 0.000 | 0.000 | 0.0 | 0.0 |
| Fishing and aquaculture | 0.213 | 0.001 | 13.569 | 0.014 | 0.845 | 0.005 | 0.002 | 0.001 | 0.000 | 0.000 | ... | 0.071 | 3.376 | 1.451 | 0.304 | 4.827 | 0.402 | 0.000 | 0.000 | 0.0 | 0.0 |
| Mining and quarrying | 1.929 | 0.023 | 0.026 | 62.923 | 0.602 | 1.630 | 2.624 | 0.216 | 0.090 | 10.121 | ... | 0.040 | 14.225 | 0.021 | 0.001 | 0.048 | 0.018 | 0.021 | 0.015 | 0.0 | 0.0 |
| Manufacture of food products; beverages and tobacco products | 47.394 | 0.290 | 8.957 | 1.841 | 70.890 | 0.913 | 0.242 | 0.029 | 0.038 | 0.008 | ... | 4.525 | 193.673 | 70.926 | 14.499 | 241.312 | 19.659 | 0.123 | 0.530 | 0.0 | 0.0 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| Activities of membership organisations | 0.028 | 0.000 | 0.001 | 0.045 | 0.653 | 0.687 | 0.001 | 0.194 | 0.007 | 0.015 | ... | 1.241 | 1.318 | 0.014 | 0.003 | 0.105 | 0.016 | 0.026 | 0.012 | 0.0 | 0.0 |
| Repair of computers and personal and household goods | 0.117 | 0.010 | 0.017 | 0.052 | 0.207 | 0.051 | 0.023 | 0.021 | 0.011 | 0.010 | ... | 2.626 | 25.511 | 1.821 | 0.291 | 5.374 | 1.636 | 1.370 | 0.236 | 0.0 | 0.0 |
| Other personal service activities | 0.042 | 0.001 | 0.002 | 0.024 | 0.025 | 0.025 | 0.005 | 0.008 | 0.001 | 0.000 | ... | 0.205 | 1.392 | 0.008 | 0.002 | 0.087 | 0.007 | 0.008 | 1.415 | 0.0 | 0.0 |
| Activities of households as employers; undifferentiated goods- and services-producing activities of households for own use | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | ... | 0.000 | 0.001 | 0.000 | 0.000 | 0.001 | 0.000 | 0.000 | 0.000 | 0.0 | 0.0 |
| Activities of extraterritorial organisations and bodies | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | ... | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.0 | 0.0 |
64 rows × 3200 columns
For example, this operation allows you to see the output of all agriculture sectors, irrespective of the country, to each country-sector combination
Leaving out the axis keyword will sum along all axes of a dimension:
[17]:
t.sum(on="countries").to_pandas()
[17]:
| sectors | Crop and animal production, hunting and related service activities | Forestry and logging | Fishing and aquaculture | Mining and quarrying | Manufacture of food products; beverages and tobacco products | Manufacture of textiles, wearing apparel, leather and related products | Manufacture of wood and of products of wood and cork, except furniture; manufacture of articles of straw and plaiting materials | Manufacture of paper and paper products | Printing and reproduction of recorded media | Manufacture of coke and refined petroleum products | ... | Education | Human health activities | Residential care activities and social work activities without accommodation | Creative, arts and entertainment activities; libraries, archives, museums and other cultural activities; gambling and betting activities | Sports activities and amusement and recreation activities | Activities of membership organisations | Repair of computers and personal and household goods | Other personal service activities | Activities of households as employers; undifferentiated goods- and services-producing activities of households for own use | Activities of extraterritorial organisations and bodies |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| sectors | |||||||||||||||||||||
| Crop and animal production, hunting and related service activities | 1095792.085 | 10140.381 | 9500.314 | 10413.245 | 2377539.179 | 125876.464 | 12174.120 | 4508.128 | 123.493 | 2546.184 | ... | 6859.100 | 5947.591 | 2247.309 | 2525.170 | 1906.590 | 1210.179 | 45.142 | 1489.415 | 0.0 | 0.0 |
| Forestry and logging | 11027.365 | 70779.118 | 324.626 | 2775.977 | 6183.625 | 568.927 | 142410.927 | 46155.673 | 95.497 | 301.733 | ... | 320.726 | 238.576 | 82.839 | 136.403 | 60.508 | 311.487 | 34.444 | 102.844 | 0.0 | 0.0 |
| Fishing and aquaculture | 1503.919 | 35.239 | 65310.786 | 195.984 | 177272.008 | 2461.897 | 23.248 | 12.689 | 3.859 | 45.036 | ... | 676.194 | 559.263 | 353.506 | 1597.406 | 156.165 | 426.828 | 3.386 | 43.889 | 0.0 | 0.0 |
| Mining and quarrying | 63808.278 | 2147.647 | 15569.083 | 903688.448 | 30788.372 | 6934.921 | 1884.696 | 12404.276 | 637.835 | 2085590.382 | ... | 9128.146 | 6708.176 | 1893.346 | 3327.515 | 3010.003 | 2132.031 | 1216.539 | 2138.657 | 0.0 | 0.0 |
| Manufacture of food products; beverages and tobacco products | 470118.322 | 409.238 | 31186.216 | 2804.483 | 1249867.363 | 30040.475 | 2000.757 | 9037.677 | 1402.461 | 6958.756 | ... | 43960.307 | 46017.301 | 29888.193 | 11976.288 | 23958.604 | 7677.132 | 439.019 | 4241.733 | 0.0 | 0.0 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| Activities of membership organisations | 3763.708 | 214.088 | 593.135 | 8060.836 | 3208.107 | 2540.389 | 555.226 | 806.347 | 363.981 | 1935.352 | ... | 3661.486 | 6329.157 | 2092.388 | 2170.203 | 1998.189 | 11674.040 | 227.093 | 1172.872 | 0.0 | 0.0 |
| Repair of computers and personal and household goods | 1837.492 | 216.878 | 492.271 | 4459.904 | 3859.827 | 1616.503 | 397.803 | 646.411 | 231.110 | 1056.156 | ... | 5143.422 | 5307.890 | 545.680 | 601.094 | 554.843 | 796.969 | 9699.851 | 515.737 | 0.0 | 0.0 |
| Other personal service activities | 725.934 | 166.636 | 589.130 | 8954.800 | 1334.581 | 2069.498 | 782.006 | 620.810 | 206.647 | 1027.218 | ... | 5482.665 | 19707.510 | 4407.033 | 2869.193 | 2055.184 | 2330.123 | 251.886 | 29367.468 | 0.0 | 0.0 |
| Activities of households as employers; undifferentiated goods- and services-producing activities of households for own use | 14.442 | 1.673 | 20.709 | 0.669 | 52.404 | 2.141 | 0.655 | 0.801 | 1.703 | 1.476 | ... | 101.880 | 80.264 | 74.443 | 24.662 | 23.491 | 11.404 | 0.846 | 4.901 | 0.0 | 0.0 |
| Activities of extraterritorial organisations and bodies | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | ... | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.0 | 0.0 |
64 rows × 64 columns
And leaving out the ‘on’ keyword will sum along all dimensions of an axis:
[18]:
t.sum(axis = 1).to_pandas()
[18]:
| 0 | ||
|---|---|---|
| countries | sectors | |
| AL | Crop and animal production, hunting and related service activities | 1577.039 |
| Forestry and logging | 24.171 | |
| Fishing and aquaculture | 43.337 | |
| Mining and quarrying | 616.322 | |
| Manufacture of food products; beverages and tobacco products | 235.822 | |
| ... | ... | ... |
| ZA | Activities of membership organisations | 610.338 |
| Repair of computers and personal and household goods | 660.221 | |
| Other personal service activities | 13.237 | |
| Activities of households as employers; undifferentiated goods- and services-producing activities of households for own use | 0.000 | |
| Activities of extraterritorial organisations and bodies | 0.000 |
3200 rows × 1 columns
Without arguments, the sum() function sums all possible axes:
[19]:
t.sum()
[19]:
np.float64(95304198.16100001)
Note that, because each operation like selecting partitions or summing returns a new part part object, you can chain these operations. E.g. if we want to take a look at the final demand for German cars per country, we can do so with the following command:
[20]:
y[["DE", "Manufacture of motor vehicles, trailers and semi-trailers"], "all"].sum(axis=1, on="y_labs").to_pandas()
[20]:
| countries | AL | AR | AT | AU | BE | BG | BR | CA | CH | CN | ... | RO | RS | RU | SA | SE | SI | SK | TR | US | ZA | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| countries | sectors | |||||||||||||||||||||
| DE | Manufacture of motor vehicles, trailers and semi-trailers | 120.622 | 160.284 | 4074.002 | 1193.021 | 3863.583 | 463.921 | 546.437 | 1883.315 | 2443.124 | 13795.133 | ... | 1794.448 | 291.04 | 162.541 | 1397.732 | 2251.033 | 770.845 | 435.449 | 3211.393 | 13952.156 | 519.881 |
1 rows × 50 columns
Aggregating on a predefined grouping¶
The toolbox allows to aggregate tables along groupings, that have to be provided before the aggregation operation. You can either provide the groupings with a yaml file or directly as a dictionary. Here, we will opt for the latter method.
Our groupings variable is a nested dict, where we group all EU27 countries into one category and group the figaro rest of world region into a ROW category. Furthermore, we group together all primary sectors.
[21]:
groupings = {
'countries': {
'EU': ["AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR", "DE", "GR", "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL", "PL", "PT", "RO", "SK", "SI", "ES", "SE"],
'ROW': ['FIGW1']
},
'sectors': {
'primary': ["Crop and animal production, hunting and related service activities", "Forestry and logging", "Fishing and aquaculture", "Mining and quarrying"]
}
}
figaro.set_groupings(groupings)
Note that we can treat the groups to select partitioned tables. The following gives us the export of US primary products to the EU countries.
[22]:
t[["US", "primary"], "EU"].sum(axis = 1, on = "sectors").to_pandas()
[22]:
| countries | AT | BE | BG | HR | CY | CZ | DK | EE | FI | FR | ... | LU | MT | NL | PL | PT | RO | SK | SI | ES | SE | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| countries | sectors | |||||||||||||||||||||
| US | Crop and animal production, hunting and related service activities | 7.781 | 41.013 | 4.619 | 1.485 | 0.780 | 27.831 | 14.194 | 0.803 | 5.198 | 94.530 | ... | 18.529 | 1.395 | 341.332 | 30.501 | 199.400 | 2.552 | 3.906 | 1.477 | 693.038 | 10.035 |
| Forestry and logging | 1.236 | 4.202 | 0.063 | 0.059 | 0.164 | 1.306 | 4.611 | 0.150 | 1.201 | 1.908 | ... | 0.817 | 0.253 | 11.062 | 1.650 | 8.773 | 0.184 | 0.117 | 0.136 | 2.606 | 3.267 | |
| Fishing and aquaculture | 1.013 | 5.920 | 0.071 | 0.029 | 0.575 | 0.312 | 15.304 | 0.172 | 2.596 | 21.412 | ... | 0.206 | 0.532 | 21.552 | 3.069 | 0.816 | 0.441 | 0.380 | 0.121 | 0.031 | 5.362 | |
| Mining and quarrying | 129.161 | 330.872 | 91.021 | 263.653 | 0.514 | 101.678 | 358.541 | 94.740 | 610.885 | 5022.604 | ... | 3.173 | 6.662 | 7772.372 | 642.654 | 815.594 | 7.006 | 280.013 | 0.884 | 6089.363 | 752.570 |
4 rows × 27 columns
Sometimes, we want to permanently reduce the size of our MRIO tables by aggregating along a certain dimension, e.g. we want to aggregate together the EU countries or the Rest of World countries into one region. Notice that originally the figaro object has 50 countries. After aggregating the EU countries together (and effectively renaming the ‘FIGW1’ region into the ‘ROW’ region), the mrio object has 24 countries.
It’s important to be aware that such aggregation distorts the individual input-output relationships, by effectively eliminating many intra-regional or intra-sectoral transactions. The effects in IO analysis are dependent on the level of aggregation. See e.g. the paper by Su and Ang on the matter.
[23]:
len(figaro.labels["countries"])
[23]:
50
[24]:
figaro.aggregate(on = "countries")
print(len(figaro.labels["countries"]))
print(figaro.labels["countries"])
24
['EU', 'ROW', 'AL', 'AR', 'AU', 'BR', 'CA', 'CH', 'CN', 'GB', 'ID', 'IN', 'JP', 'KR', 'ME', 'MK', 'MX', 'NO', 'RS', 'RU', 'SA', 'TR', 'US', 'ZA']
After aggregation, we will update our old t and y variables with the aggregated ones
[25]:
t = figaro.t
y = figaro.y
IO analysis¶
This section guides you through a small IO analysis exercise. ### Mathematical operations and You can use mathematical operators on the part objects to perform addition, subtraction, multiplication, division, matrix multiplication or leontief inversion, if the parts have adequate shape.
E.g. this does not work…
[26]:
try:
t + y
except ValueError as e:
print(f"ValueError: {e}")
ValueError: operands could not be broadcast together with shapes (1536,120) (1536,1536)
… but by first summing along the horizontal axis, we can compute the gross output vector:
[27]:
x = t.sum(axis=1) + y.sum(axis=1)
You can compute the technical coefficient matrix by dividing the transaction flow matrix through the gross output vector. Usually this will result in some divisions by zero, because not all sectors have an output. We therefore want to replace all NaNs with zeros in the a matrix.
[28]:
a = t / x
a.data = np.nan_to_num(a.data, nan=0.0)
Division by zero in countries_grouped_t_sum_1+countries_grouped_y_sum_1/countries_grouped_t
c:\users\beaufils\documents\projects\scripts\mrio_toolbox\mrio_toolbox\_parts\_Part.py:1729: RuntimeWarning: invalid value encountered in divide
return self.alias(data=self.data/a,
You can compute the leontief matrix with the following operation. Note that this is equivalent to $ L = (I - A)^{-1}$ with the identity matrix \(I\), so the leontief inversion function does not simply compute the inverse but always the inverse of (1-a)
[29]:
l = a.leontief_inversion()
Multiplying the leontief inverse with the final demand vector should give you back the gross output vector
[30]:
x2 = l.mul(y.sum(axis=1))
np.allclose(x.data, x2.data)
[30]:
True
Setting values¶
You may now provide a shock to the final demand vector and see its demand-pull repercussions on the gross output vector. Setting values follows the same syntax as selecting partitions.
Suppose that due to a pandemic outbreak in Brazil, the Brazilien government increases its final demand for healthcare by 10 percent. What effect would this induce in the other sectors? We can increase the value of a certain selection by 10 percent as follows:
[31]:
y_shocked = y.alias()
y_shocked[["BR", "Human health activities"], ["BR", "Final consumption expenditure of general government"]] *= 1.10
x_new = l.mul(y_shocked.sum(axis=1))
x_percent_change = (x_new - x)/x
x_percent_change.to_pandas().sort_values(by=0, ascending=False).head(10) # by=0 sorts the DataFrame by the first column, which lost its name due to the summation operation, where one dimension got dropped.
Division by zero in countries_grouped_t_sum_1+countries_grouped_y_sum_1/l_countries_grouped_t_sum_1+countries_grouped_y_sum_1/countries_grouped_t.countries_grouped_y_alias_sum_1-countries_grouped_t_sum_1+countries_grouped_y_sum_1
[31]:
| 0 | ||
|---|---|---|
| countries | sectors | |
| BR | Human health activities | 0.054227 |
| Other personal service activities | 0.006311 | |
| Scientific research and development | 0.003813 | |
| Accommodation and food service activities | 0.003766 | |
| Security and investigation, service and landscape, office administrative and support activities | 0.003542 | |
| Employment activities | 0.003442 | |
| Postal and courier activities | 0.003373 | |
| Printing and reproduction of recorded media | 0.003312 | |
| Sewerage, waste management, remediation activities | 0.003190 | |
| Repair of computers and personal and household goods | 0.003016 |
From the above dataframe, we can see the following:
The gross output of the ‘Human health activities’ sector increases by 5.4 percent due to direct and indirect effects. Note that this is lower than the 10 % increase in government final demand, because government final demand is only a fraction of the sector’s total output.
The increased government demand has ripple effects and increases the gross output of the ‘Other personal services activities’ by 0.6 percent, the output of ‘scientific research and development’ by 0.38 percent etc
We have to be careful in interpreting results from such a naive IO analysis. I.e. the output of Scientific research and development may react slower in the real world than that of ‘Accomodation and food services activities’.
Another example: Due to a successful advertisement campaign for local cars, the household final demand for cars produced in Japan increases by 10 percent. What is the effect on gross output?
[32]:
y_shocked = y.alias()
y_shocked[["JP", "Wholesale and retail trade and repair of motor vehicles and motorcycles"], ["JP", "Final consumption expenditure of households"]] *= 1.10
x_new = l.mul(y_shocked.sum(axis=1))
x_percent_change = (x_new - x)/x
x_percent_change.to_pandas().sort_values(by=0, ascending=False).head(10) # by=0 sorts the DataFrame by the first column, which lost its name due to the summation operation, where one dimension got dropped.
Division by zero in countries_grouped_t_sum_1+countries_grouped_y_sum_1/l_countries_grouped_t_sum_1+countries_grouped_y_sum_1/countries_grouped_t.countries_grouped_y_alias_sum_1-countries_grouped_t_sum_1+countries_grouped_y_sum_1
c:\users\beaufils\documents\projects\scripts\mrio_toolbox\mrio_toolbox\_parts\_Part.py:1729: RuntimeWarning: invalid value encountered in divide
return self.alias(data=self.data/a,
[32]:
| 0 | ||
|---|---|---|
| countries | sectors | |
| JP | Wholesale and retail trade and repair of motor vehicles and motorcycles | 0.027305 |
| Manufacture of motor vehicles, trailers and semi-trailers | 0.000927 | |
| Advertising and market research | 0.000429 | |
| Manufacture of rubber and plastic products | 0.000305 | |
| Printing and reproduction of recorded media | 0.000261 | |
| Postal and courier activities | 0.000212 | |
| MX | Wholesale and retail trade and repair of motor vehicles and motorcycles | 0.000205 |
| JP | Manufacture of basic metals | 0.000202 |
| Security and investigation, service and landscape, office administrative and support activities | 0.000198 | |
| Manufacture of fabricated metal products, except machinery and equipment | 0.000196 |