Skip to content

Commit 1635cde

Browse files
committed
group table moments
1 parent 9785174 commit 1635cde

2 files changed

Lines changed: 114 additions & 40 deletions

File tree

ogcore/output_tables.py

Lines changed: 105 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,19 +1034,84 @@ def model_fit_table(
10341034
``None`` if saved to disk
10351035
10361036
"""
1037-
table_dict = {
1038-
"Moment": [],
1039-
"Data": [],
1040-
"Model": [],
1041-
}
1037+
# Ordered groups and the moment descriptions belonging to each
1038+
MOMENT_GROUPS = [
1039+
(
1040+
"Macroeconomic moments",
1041+
[
1042+
r"Investment rate $(I/K)$",
1043+
r"Capital-Output ratio $(K/Y)$",
1044+
r"Consumption-Output ratio $(C/Y)$",
1045+
r"Savings rate $(B/Y)$",
1046+
r"Interest rate $(r)$",
1047+
r"Capital share of output",
1048+
r"Labor share of output",
1049+
],
1050+
),
1051+
(
1052+
"Fiscal moments",
1053+
[
1054+
r"Revenue to GDP ratio $(T/Y)$",
1055+
r"Gov't consumption to GDP ratio $(G/Y)$",
1056+
r"Pension outlays to GDP ratio $(Pension/Y)$",
1057+
r"Infrastructure spending to GDP ratio $(I_g/Y)$",
1058+
r"Debt to GDP ratio $(D/Y)$",
1059+
],
1060+
),
1061+
(
1062+
"Distributional moments",
1063+
[
1064+
"Gini coefficient, wealth",
1065+
"Gini coefficient, income",
1066+
"Gini coefficient, after-tax income",
1067+
],
1068+
),
1069+
(
1070+
"Demographic moments",
1071+
[
1072+
r"Fraction 65+",
1073+
r"Pop growth rate",
1074+
],
1075+
),
1076+
]
10421077

1078+
# Compute model moments for all entries in targets_dict
1079+
computed = {}
10431080
for moment, data_val in targets_dict.items():
1044-
# --- Columns 1-3: target description, model moment, data moment ---
10451081
target_desc = moment
10461082

1047-
# Compute the model moment corresponding to the target description
1083+
# Macroeconomic moments
1084+
if target_desc == r"Investment rate $(I/K)$":
1085+
model_val = tpi_output["I"][t] / tpi_output["K"][t]
1086+
elif target_desc == r"Capital-Output ratio $(K/Y)$":
1087+
model_val = tpi_output["K"][t] / tpi_output["Y"][t]
1088+
elif target_desc == r"Consumption-Output ratio $(C/Y)$":
1089+
model_val = tpi_output["C"][t] / tpi_output["Y"][t]
1090+
elif target_desc == r"Savings rate $(B/Y)$":
1091+
model_val = tpi_output["B"][t] / tpi_output["Y"][t]
1092+
elif target_desc == r"Interest rate $(r)$":
1093+
model_val = tpi_output["r"][t]
1094+
elif target_desc == r"Capital share of output":
1095+
model_val = (
1096+
1 - tpi_output["r"][t] * tpi_output["K"][t] / tpi_output["Y"][t]
1097+
)
1098+
elif target_desc == r"Labor share of output":
1099+
model_val = tpi_output["w"][t] * tpi_output["L"][t] / tpi_output["Y"][t]
1100+
# Fiscal moments
1101+
elif target_desc == r"Revenue to GDP ratio $(T/Y)$":
1102+
model_val = (
1103+
tpi_output["total_total_tax_revenue"][t] / tpi_output["Y"][t]
1104+
)
1105+
elif target_desc == r"Gov't consumption to GDP ratio $(G/Y)$":
1106+
model_val = tpi_output["G"][t] / tpi_output["Y"][t]
1107+
elif target_desc == r"Pension outlays to GDP ratio $(Pension/Y)$":
1108+
model_val = tpi_output["agg_pension_outlays"][t] / tpi_output["Y"][t]
1109+
elif target_desc == r"Infrastructure spending to GDP ratio $(I_g/Y)$":
1110+
model_val = tpi_output["I_g"][t] / tpi_output["Y"][t]
1111+
elif target_desc == r"Debt to GDP ratio $(D/Y)$":
1112+
model_val = tpi_output["D"][t] / tpi_output["Y"][t]
10481113
# Distributional moments
1049-
if target_desc == "Gini coefficient, wealth":
1114+
elif target_desc == "Gini coefficient, wealth":
10501115
dist = tpi_output["b_sp1"][t]
10511116
pop_weights = params.omega[t]
10521117
pop_weights = pop_weights / pop_weights.sum()
@@ -1063,40 +1128,17 @@ def model_fit_table(
10631128
)
10641129
model_val = ineq.gini()
10651130
elif target_desc == "Gini coefficient, after-tax income":
1066-
dist = tpi_output["before_tax_income"][t] + tpi_output["hh_net_taxes"][t]
1131+
dist = (
1132+
tpi_output["before_tax_income"][t]
1133+
+ tpi_output["hh_net_taxes"][t]
1134+
)
10671135
pop_weights = params.omega[t]
10681136
pop_weights = pop_weights / pop_weights.sum()
10691137
ineq = Inequality(
10701138
dist, pop_weights, params.lambdas, params.S, params.J
10711139
)
10721140
model_val = ineq.gini()
1073-
# Macro moments
1074-
elif target_desc == r"Investment rate $(I/K)$":
1075-
model_val = tpi_output["I"][t] / tpi_output["K"][t]
1076-
elif target_desc == r"Capital-Output ratio $(K/Y)$":
1077-
model_val = tpi_output["K"][t] / tpi_output["Y"][t]
1078-
elif target_desc == r"Consumption-Output ratio $(C/Y)$":
1079-
model_val = tpi_output["C"][t] / tpi_output["Y"][t]
1080-
elif target_desc == r"Savings rate $(B/Y)$":
1081-
model_val = tpi_output["B"][t] / tpi_output["Y"][t]
1082-
elif target_desc == r"Interest rate $(r)$":
1083-
model_val = tpi_output["r"][t]
1084-
elif target_desc == r"Capital share of output":
1085-
model_val = 1 - tpi_output["r"][t] * tpi_output["K"][t] / tpi_output["Y"][t]
1086-
elif target_desc == r"Labor share of output":
1087-
model_val = tpi_output["w"][t] * tpi_output["L"][t] / tpi_output["Y"][t]
1088-
# Fiscal moments
1089-
elif target_desc == r"Revenue to GDP ratio $(T/Y)$":
1090-
model_val = tpi_output["total_total_tax_revenue"][t] / tpi_output["Y"][t]
1091-
elif target_desc == r"Gov't consumption to GDP ratio $(G/Y)$":
1092-
model_val = tpi_output["G"][t] / tpi_output["Y"][t]
1093-
elif target_desc == r"Pension outlays to GDP ratio $(Pension/Y)$":
1094-
model_val = tpi_output["agg_pension_outlays"][t] / tpi_output["Y"][t]
1095-
elif target_desc == r"Infrastructure spending to GDP ratio $(I_g/Y)$":
1096-
model_val = tpi_output["I_g"][t] / tpi_output["Y"][t]
1097-
elif target_desc == r"Debt to GDP ratio $(D/Y)$":
1098-
model_val = tpi_output["D"][t] / tpi_output["Y"][t]
1099-
# Demograhic moments
1141+
# Demographic moments
11001142
elif target_desc == r"Fraction 65+":
11011143
model_val = (
11021144
params.omega[t, -35:].sum() # NOTE: not flexible with S, E changes
@@ -1107,9 +1149,33 @@ def model_fit_table(
11071149
else:
11081150
model_val = np.nan
11091151

1110-
table_dict["Moment"].append(target_desc)
1111-
table_dict["Data"].append(data_val)
1112-
table_dict["Model"].append(model_val)
1152+
computed[target_desc] = (data_val, model_val)
1153+
1154+
# Build the grouped table; skip any group with no matching moments
1155+
all_grouped = {m for _, moments in MOMENT_GROUPS for m in moments}
1156+
table_dict = {"Moment": [], "Data": [], "Model": []}
1157+
1158+
for group_name, group_moments in MOMENT_GROUPS:
1159+
group_entries = [m for m in group_moments if m in computed]
1160+
if not group_entries:
1161+
continue
1162+
# Group header row (no data values)
1163+
table_dict["Moment"].append(group_name)
1164+
table_dict["Data"].append(np.nan)
1165+
table_dict["Model"].append(np.nan)
1166+
# Indented moment rows
1167+
for m in group_entries:
1168+
data_val, model_val = computed[m]
1169+
table_dict["Moment"].append(f" {m}")
1170+
table_dict["Data"].append(data_val)
1171+
table_dict["Model"].append(model_val)
1172+
1173+
# Append any moments not belonging to a known group
1174+
for target_desc, (data_val, model_val) in computed.items():
1175+
if target_desc not in all_grouped:
1176+
table_dict["Moment"].append(target_desc)
1177+
table_dict["Data"].append(data_val)
1178+
table_dict["Model"].append(model_val)
11131179

11141180
table_df = pd.DataFrame.from_dict(table_dict)
11151181
table = save_return_table(table_df, table_format, path, precision=4)

uv.lock

Lines changed: 9 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)