IODE Python Tutorial

1. Introduction and Setup

First, we will import the necessary libraries and check our IODE version.

[1]:
import numpy as np
import pandas as pd
import larray as la
import iode as io

# Check IODE version
print(f"IODE version: {io.__version__}")

# Display documentation for a specific function
print(f"Equations.load() documentation:\n{help(io.equations.load)}")
IODE version: 7.0.0
Help on method load in module iode.iode_python:

load(filepath: 'str') method of pyiode.iode_python.Equations instance
    load(self, filepath: str)

    Load objects stored in file 'filepath' into the current database.
    Erase the database before to load the file.

    Parameters
    ----------
    filepath: str
        path to the file to load

    Examples
    --------
    >>> from iode import SAMPLE_DATA_DIR
    >>> from iode import comments, variables
    >>> comments.load(f"{SAMPLE_DATA_DIR}/fun.cmt")
    >>> len(comments)
    317

    >>> variables.load(f"{SAMPLE_DATA_DIR}/fun.var")
    >>> len(variables)
    394

Equations.load() documentation:
None

2. Loading and Exploring Data

IODE works with different types of data: equations, identities, scalars, and variables.

[2]:
# Load sample data
io.equations.load(f"{io.SAMPLE_DATA_DIR}/fun.eqs")
io.identities.load(f"{io.SAMPLE_DATA_DIR}/fun.idt")
io.scalars.load(f"{io.SAMPLE_DATA_DIR}/fun.scl")
io.variables.load(f"{io.SAMPLE_DATA_DIR}/fun.var")

# Explore the loaded data
print(f"Number of equations: {len(io.equations)}")
print(f"Number of identities: {len(io.identities)}")
print(f"Number of scalars: {len(io.scalars)}")
print(f"Number of variables: {len(io.variables)}")

# List all equation names
print(f"\nAll equation names:\n{io.equations.names}")

# Check for a specific equation
print(f"\nDoes 'ACAF' equation exist? {'ACAF' in io.equations}")

# Get the current sample period for variables
print(f"\nCurrent Variables sample: {io.variables.sample}")
Number of equations: 274
Number of identities: 48
Number of scalars: 161
Number of variables: 394

All equation names:
['ACAF', 'ACAG', 'AOUC', 'BENEF', 'BQY', 'BRUGP', 'BVY', 'CGU', 'COEFON', 'COTRES', 'DEBT', 'DPU', 'DPUF', 'DPUG', 'DPUGO', 'DPUH', 'DPUU', 'DTF', 'DTH', 'DTH1', 'DTH1C', 'EX', 'EXC', 'EXCC', 'FLF', 'FLG', 'FLGR', 'GAP', 'GOSF', 'GOSG', 'GOSH', 'GOSH_', 'IDF', 'IDG', 'IDH', 'IFU', 'IHU', 'IT', 'ITCEE', 'ITCR', 'ITD', 'ITEP', 'ITF', 'ITF5', 'ITFC', 'ITFGI', 'ITFGO', 'ITFQ', 'ITGR', 'ITI5R', 'ITIFR', 'ITIGR', 'ITM', 'ITMQ', 'ITMQR', 'ITNQ', 'ITON', 'ITONQ', 'ITPL', 'ITPR', 'ITPS', 'ITT', 'IUG', 'KL', 'KLFHP', 'KN5', 'KNF', 'KNFF', 'KNFFY', 'KNFY', 'KNI', 'KNIY', 'NATY', 'NFY', 'NFYH', 'OCUF', 'OCUG', 'OCUH', 'PAF_', 'PAG', 'PAH', 'PBBP', 'PBNP', 'PC', 'PC_', 'PDPUG', 'PFI', 'PFI_', 'PFND', 'PG', 'PI5', 'PIF', 'PIG', 'PKF', 'PM', 'PMAB', 'PME', 'PMS', 'PMT', 'POIL', 'PQOG', 'PROD', 'PW3', 'PWBG', 'PWMAB', 'PWMS', 'PWXAB', 'PWXS', 'PX', 'PXAB', 'PXB', 'PXE', 'PXS', 'PXT', 'QAF', 'QAFF', 'QAFF_', 'QAF_', 'QAG', 'QAH', 'QAI', 'QAI_', 'QAT', 'QAT_', 'QBBP', 'QBBPPOT_', 'QBBP_B', 'QBBP_P', 'QBNP', 'QC', 'QC_', 'QFND', 'QG', 'QGO', 'QI', 'QI5', 'QIF', 'QIG', 'QL', 'QM', 'QMAB', 'QME', 'QMS', 'QMT', 'QOUG', 'QQMAB_', 'QS', 'QS_', 'QWXAB', 'QWXS', 'QWXSS', 'QX', 'QXAB', 'QXB', 'QXE', 'QXS', 'QXT', 'Q_F', 'Q_I', 'RDEBT', 'RENT', 'RIDG', 'RIDGG', 'RIPBE', 'RLBE', 'RSBE', 'SBF', 'SBF3L', 'SBG', 'SBGX', 'SBH', 'SF', 'SG', 'SH', 'SSF', 'SSF3', 'SSF3L', 'SSF3P', 'SSFDOM', 'SSFF', 'SSFFIC', 'SSFFX', 'SSFG', 'SSH', 'SSH3GP', 'SSH3O', 'SSH3P', 'SSH3W', 'SSH3WA', 'SSH3WW', 'SSH3ZA', 'SSH3ZW', 'SSHFF', 'SUB', 'SUBCEE', 'TFPFHP_', 'TWG', 'TWGP', 'ULCP', 'UY', 'VAF', 'VAFF', 'VAFF_', 'VAF_', 'VAG', 'VAH', 'VAI', 'VAI_', 'VAMARE', 'VAT', 'VAT_', 'VBBP', 'VBBP_B', 'VBBP_P', 'VBNP', 'VBNP_B', 'VBNP_I', 'VBNP_P', 'VC', 'VC_', 'VI', 'VI5', 'VIF', 'VM', 'VMAB', 'VME', 'VMK', 'VMN', 'VMS', 'VMT', 'VS', 'VS_', 'VX', 'VXAB', 'VXB', 'VXE', 'VXK', 'VXN', 'VXS', 'VXT', 'W', 'WBF', 'WBF_', 'WBG', 'WBGO', 'WBGP', 'WBU', 'WBU_', 'WCF', 'WCF_', 'WCRH', 'WDOM', 'WG', 'WIND', 'WIND_', 'WLCP', 'WMIN', 'WNF', 'WNF_', 'YDH', 'YDH_', 'YDTG', 'YIDG', 'YK', 'YN', 'YSEFP', 'YSEFT1', 'YSEFT2', 'YSFIC', 'YSSF', 'YSSG', 'ZF', 'ZJ', 'ZZF_']

Does 'ACAF' equation exist? True

Current Variables sample: 1960Y1:2015Y1

Working with Subsets

IODE allows you to subset variables using patterns.

[3]:
# Create a subset of variables starting with 'A' or ending with '_'
vars_subset = io.variables["A*;*_"]

print("Variables in the subset:")
for name in vars_subset:
    print(name)
Variables in the subset:
ACAF
ACAG
AOUC
AOUC_
AQC
GAP_
GOSH_
PAF_
PC_
PFI_
PROIHP_
QAFF_
QAF_
QAI_
QAT_
QBBPPOT_
QC_
QQMAB_
QS_
TFPFHP_
VAFF_
VAF_
VAI_
VAT_
VC_
VS_
WBF_
WBU_
WCF_
WIND_
WNF_
YDH_
ZZF_

3. Working with Equations and Variables

Let us explore how to manipulate equations and variables in IODE.

[ ]:
# Get a specific equation
eq_ACAF = io.equations["ACAF"]
print(f"ACAF equation:\n{eq_ACAF}\n")

# Add a new equation
io.equations["NEW_EQ"] = "NEW_EQ := 2 * X + Y"
print(f"Added NEW_EQ: {io.equations['NEW_EQ']}\n")

# Delete an equation
del io.equations["NEW_EQ"]
print(f"'NEW_EQ' removed: {'NEW_EQ' not in io.equations}\n")

# Get a whole variable
print(f"Variable 'ACAF':\n{io.variables['ACAF']}\n")

# Get a variable for a specific period
print(f"ACAF in 2000Y1: {io.variables['ACAF', '2000Y1']}\n")

# Get a variable for a range of periods
print(f"ACAF from 2000Y1 to 2010Y1:\n{io.variables['ACAF', '2000Y1:2010Y1']}")

4. Estimating Coefficients

IODE provides tools for estimating coefficients of equations. Let us look at how to do this for a single equation and for a block of equations.

[ ]:
# Examine an equation before estimation
print(f"ACAF equation LEC: {io.equations['ACAF'].lec}")
print(f"ACAF coefficients: {io.equations['ACAF'].coefficients}")
print(f"ACAF variables: {io.equations['ACAF'].variables}\n")

# Reset coefficients
for name in io.equations['ACAF'].coefficients:
    io.scalars[name] = 0., 1.

# Estimate the equation
io.equations.estimate("1980Y1", "1996Y1", "ACAF")

# Check results
for coef in ['acaf1', 'acaf2', 'acaf4']:
    print(f"Estimated value for {coef}: {io.scalars[coef]}")

print("\nNow let's estimate a block of equations...\n")

# Prepare a block of equations
block = "ACAF;DPUH"
for name in block.split(";"):
    io.equations[name] = {"block": block, "method": "LSQ"}

# Estimate the block
io.equations.estimate("1980Y1", "1996Y1", block)

# Check results
for coef in ['acaf1', 'acaf2', 'acaf4', 'dpuh_1', 'dpuh_2']:
    print(f"Estimated value for {coef}: {io.scalars[coef]}")

5. Simulation

IODE provides powerful simulation capabilities.

[ ]:
# Create a Simulation instance
simu = io.Simulation(sort_algorithm=io.SimulationSort.BOTH,
                     initialization_method=io.SimulationInitialization.TM1)

# Display simulation parameters
print(f"Simulation parameters:")
print(f"Convergence threshold: {simu.convergence_threshold}")
print(f"Relaxation factor: {simu.rel}")
print(f"Max iterations: {simu.max_nb_iterations}")
print(f"Sort algorithm: {simu.sort_algorithm}")
print(f"Initialization method: {simu.initialization_method}")
print(f"Debug mode: {simu.debug}")
print(f"Number of passes: {simu.nb_passes}")
print(f"Debug Newton: {simu.debug_newton}\n")

# Prepare for simulation
print(f"Exogenous variable 'UY': {io.equations['UY'].lec}")
print(f"Endogenous variable 'XNATY': {io.identities['XNATY']}\n")

# Reset values of exogenous variable
io.variables["UY", "2000Y1:2015Y1"] = 0.0

print(f"UY before simulation:\n{io.variables['UY']}\n")

# Run the simulation
simu.model_simulate("2000Y1", "2015Y1")

print(f"UY after simulation:\n{io.variables['UY']}")

6. Data Conversion and Export

IODE allows for easy conversion between its data structures and common Python data structures like pandas DataFrames and LArrays.

[ ]:
# Convert IODE data to pandas DataFrames
df_eqs = io.equations.to_frame()
df_scl = io.scalars.to_frame()
df_vars = io.variables.to_frame()

print("Equations as DataFrame:")
print(df_eqs.head())

print("\nScalars as DataFrame:")
print(df_scl.head())

print("\nVariables as DataFrame:")
print(df_vars.head())

# Convert IODE variables to LArray Array
arr_vars = io.variables.to_array()
print("\nVariables as LArray:")
print(arr_vars)

# Converting back to IODE structures
io.equations.from_frame(df_eqs)
io.scalars.from_frame(df_scl)
io.variables.from_frame(df_vars)
io.variables.from_array(arr_vars)

print("\nData converted back to IODE structures")

# Saving IODE data
io.equations.save('equations.eqs')  # Save all equations
vars_subset.save('variables_subset.av')  # Save a subset of variables

print("\nSaved equations and variable subset. Contents of variables_subset.av:")
with open("variables_subset.av", "r") as f:
    print(f.read())

7. Advanced IODE Commands

The Python interface also allows for direct execution of commands not yet available in the Python API and for running complex sequences of operations.

[ ]:
# Execute individual IODE commands
print("Executing individual IODE commands:")
io.execute_command("$WsClearVar")
io.execute_command("$WsSample 2000Y1 2005Y1")
io.execute_command("$DataCalcVar A t+1")
io.execute_command("$DataCalcVar B t-1")
io.execute_command("$DataCalcVar C A/B")
io.execute_command("$DataCalcVar D grt A")
io.execute_command("$WsSaveVar test_var.av")

print("\nIODE commands executed. Contents of test_var.av:")
with open("test_var.av", "r") as f:
    print(f.read())

# Execute an IODE report
print("\nNow, let's execute an IODE report:")
with open("create_var.rep", "w") as f:
    f.write("$WsClearVar\n")
    f.write("$WsSample 2000Y1 2005Y1\n")
    f.write("$DataCalcVar %1% t+1 \n")
    f.write("$DataCalcVar %2% t-1 \n")
    f.write("$DataCalcVar %3% %1%/%2%\n")
    f.write("$DataCalcVar %4% grt %1% \n")
    f.write("$WsSaveVar test_var.av\n")

io.execute_report("create_var.rep", ["A", "B", "C", "D"])

print("IODE report executed. Updated contents of test_var.av:")
with open("test_var.av", "r") as f:
    print(f.read())