@@ -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 )
0 commit comments