|
| 1 | +import json |
1 | 2 | import numpy as np |
2 | 3 | import pandas as pd |
3 | 4 | import os |
@@ -987,3 +988,134 @@ def dynamic_revenue_decomposition( |
987 | 988 | table = save_return_table(table_df, table_format, path) |
988 | 989 |
|
989 | 990 | return table |
| 991 | + |
| 992 | + |
| 993 | +def calib_table( |
| 994 | + param_list, |
| 995 | + targets_dict, |
| 996 | + params, |
| 997 | + tpi_output, |
| 998 | + t=0, |
| 999 | + table_format=None, |
| 1000 | + path=None, |
| 1001 | +): |
| 1002 | + """ |
| 1003 | + Creates a table summarizing endogenously calibrated parameters, |
| 1004 | + showing parameter values alongside the data targets used in |
| 1005 | + calibration and the corresponding model moments. |
| 1006 | +
|
| 1007 | + Supported target descriptions (used as keys in ``targets_dict`` |
| 1008 | + values): |
| 1009 | +
|
| 1010 | + * ``"Gini coefficient of wealth"`` -- computed from ``b_sp1`` |
| 1011 | + * ``"Investment rate (I/K)"`` -- computed from ``I`` and ``K`` |
| 1012 | + * ``"Gini coefficient of income"`` -- computed from |
| 1013 | + ``before_tax_income`` |
| 1014 | +
|
| 1015 | + Args: |
| 1016 | + param_list (list): OG-Core parameter names to include in the |
| 1017 | + table, e.g. ``['beta_annual', 'delta_annual', 'e']`` |
| 1018 | + targets_dict (dict): maps each parameter name to a one-item |
| 1019 | + dict ``{target_description: data_value}``, e.g.:: |
| 1020 | +
|
| 1021 | + { |
| 1022 | + 'beta_annual': {'Gini coefficient of wealth': 0.82}, |
| 1023 | + 'delta_annual': {'Investment rate (I/K)': 0.07}, |
| 1024 | + 'e': {'Gini coefficient of income': 0.55}, |
| 1025 | + } |
| 1026 | +
|
| 1027 | + params (OG-Core Specifications class): model parameters object |
| 1028 | + tpi_output (dict): output dictionary returned by ``TPI.run_TPI`` |
| 1029 | + t (int): period index used for model moment calculations. |
| 1030 | + Defaults to ``0`` (first period of the transition path). |
| 1031 | + Pass ``-1`` to use the last period, which approximates |
| 1032 | + steady-state values. |
| 1033 | + table_format (string): format to return table in: ``'csv'``, |
| 1034 | + ``'tex'``, ``'excel'``, ``'json'``; if ``None`` a |
| 1035 | + DataFrame is returned |
| 1036 | + path (string): path to save table to |
| 1037 | +
|
| 1038 | + Returns: |
| 1039 | + table (various): table as a DataFrame, formatted string, or |
| 1040 | + ``None`` if saved to disk |
| 1041 | +
|
| 1042 | + """ |
| 1043 | + # Load parameter metadata for title and notation |
| 1044 | + default_params_path = os.path.join(cur_path, "default_parameters.json") |
| 1045 | + with open(default_params_path, "r") as f: |
| 1046 | + default_params_meta = json.load(f) |
| 1047 | + |
| 1048 | + table_dict = { |
| 1049 | + "Parameter": [], |
| 1050 | + "Value": [], |
| 1051 | + "Data Target": [], |
| 1052 | + "Model Moment": [], |
| 1053 | + "Data Moment": [], |
| 1054 | + } |
| 1055 | + |
| 1056 | + for param_name in param_list: |
| 1057 | + # --- Column 1: human-readable name and LaTeX symbol --- |
| 1058 | + if param_name in default_params_meta: |
| 1059 | + meta = default_params_meta[param_name] |
| 1060 | + short_desc = meta.get( |
| 1061 | + "short_description", meta.get("title", param_name) |
| 1062 | + ) |
| 1063 | + notation = meta.get("param_notation", "") |
| 1064 | + col1 = f"{short_desc} {notation}".strip() |
| 1065 | + else: |
| 1066 | + col1 = param_name |
| 1067 | + |
| 1068 | + # --- Column 2: parameter value or range --- |
| 1069 | + param_val = getattr(params, param_name, None) |
| 1070 | + if param_val is None: |
| 1071 | + col2 = "N/A" |
| 1072 | + else: |
| 1073 | + arr = np.asarray(param_val, dtype=float) |
| 1074 | + if arr.ndim == 0 or arr.size == 1: |
| 1075 | + col2 = f"{float(arr.flat[0]):.4f}" |
| 1076 | + elif np.allclose(arr, arr.flat[0]): |
| 1077 | + col2 = f"{arr.flat[0]:.4f}" |
| 1078 | + else: |
| 1079 | + col2 = f"[{arr.min():.4f}, {arr.max():.4f}]" |
| 1080 | + |
| 1081 | + # --- Columns 3-5: target description, model moment, data moment --- |
| 1082 | + target_info = targets_dict.get(param_name, {}) |
| 1083 | + if target_info: |
| 1084 | + target_desc = next(iter(target_info)) |
| 1085 | + data_val = target_info[target_desc] |
| 1086 | + else: |
| 1087 | + target_desc = "" |
| 1088 | + data_val = np.nan |
| 1089 | + |
| 1090 | + # Compute the model moment corresponding to the target description |
| 1091 | + if target_desc == "Gini coefficient of wealth": |
| 1092 | + dist = tpi_output["b_sp1"][t] |
| 1093 | + pop_weights = params.omega[t] |
| 1094 | + pop_weights = pop_weights / pop_weights.sum() |
| 1095 | + ineq = Inequality( |
| 1096 | + dist, pop_weights, params.lambdas, params.S, params.J |
| 1097 | + ) |
| 1098 | + model_val = ineq.gini() |
| 1099 | + elif target_desc == "Investment rate (I/K)": |
| 1100 | + model_val = tpi_output["I"][t] / tpi_output["K"][t] |
| 1101 | + elif target_desc == "Gini coefficient of income": |
| 1102 | + dist = tpi_output["before_tax_income"][t] |
| 1103 | + pop_weights = params.omega[t] |
| 1104 | + pop_weights = pop_weights / pop_weights.sum() |
| 1105 | + ineq = Inequality( |
| 1106 | + dist, pop_weights, params.lambdas, params.S, params.J |
| 1107 | + ) |
| 1108 | + model_val = ineq.gini() |
| 1109 | + else: |
| 1110 | + model_val = np.nan |
| 1111 | + |
| 1112 | + table_dict["Parameter"].append(col1) |
| 1113 | + table_dict["Value"].append(col2) |
| 1114 | + table_dict["Data Target"].append(target_desc) |
| 1115 | + table_dict["Model Moment"].append(model_val) |
| 1116 | + table_dict["Data Moment"].append(data_val) |
| 1117 | + |
| 1118 | + table_df = pd.DataFrame.from_dict(table_dict) |
| 1119 | + table = save_return_table(table_df, table_format, path, precision=4) |
| 1120 | + |
| 1121 | + return table |
0 commit comments