导入数据¶

In [1]:
import pandas as pd
c0m0full = pd.read_csv(r'D:\论文\最后一波一鼓作气\数据\c0m0full.csv')  
c1m1full = pd.read_csv(r'D:\论文\最后一波一鼓作气\数据\c1m1full.csv')  
hsf15full = pd.read_csv(r'D:\论文\最后一波一鼓作气\数据\hsf15full.csv')  
hsf18full = pd.read_csv(r'D:\论文\最后一波一鼓作气\数据\hsf18full.csv')  
city_gender_age_premium_ratio_district15 = pd.read_csv(r'D:\论文\最后一波一鼓作气\数据\city_gender_age_premium_ratio_district15.csv')  
In [2]:
#删除城市样本
c1m1full = c1m1full[c1m1full['urban_nbs'] != 'Urban']
#确保15年未整合,18年整合了
c1m1full = c1m1full[(c1m1full['policyintergration2015']==0.0) & (c1m1full['policyintergration2018']==1.0)]

#删除城市样本
c0m0full = c0m0full[c0m0full['urban_nbs'] != 'Urban']
#确保15年未整合,18年整合了
c0m0full = c0m0full[(c0m0full['policyintergration2015']==0.0) & (c0m0full['policyintergration2018']==1.0)]

#删除城市样本
hsf15full = hsf15full[hsf15full['urban_nbs'] != 'Urban']
#确保15年未整合,18年整合了
hsf15full = hsf15full[(hsf15full['policyintergration2015']==0.0) & (hsf15full['policyintergration2018']==1.0)]

#删除城市样本
hsf18full = hsf18full[hsf18full['urban_nbs'] != 'Urban']
#确保15年未整合,18年整合了
hsf18full = hsf18full[(hsf18full['policyintergration2015']==0.0) & (hsf18full['policyintergration2018']==1.0)]
In [3]:
import numpy as np
import pandas as pd

# 1) 只保留需要列,去除缺失,按 city 去重(同城增速一致)
dfu = (city_gender_age_premium_ratio_district15[['city','GDPgrowthrate']]
       .dropna(subset=['GDPgrowthrate'])
       .drop_duplicates(subset=['city']))

# 2) 按增速从高到低排序
df_sorted = dfu.sort_values('GDPgrowthrate', ascending=False).reset_index(drop=True)

# 3) 90%分位(排名法):正数10%位置的阈值 = 从上往下第 ceil(0.1*N) 个
n = len(df_sorted)
k = max(1, int(np.ceil(0.10 * n)))
threshold = df_sorted.loc[k - 1, 'GDPgrowthrate']

# 4) 列出严格高于阈值的城市
top_cities = df_sorted.loc[df_sorted['GDPgrowthrate'] > threshold, 'city'].tolist()

print("90%分位(排名法,正数10%位置)k =", k, "阈值 GDPgrowthrate =", threshold)
print("高于该阈值的城市:", top_cities)
90%分位(排名法,正数10%位置)k = 13 阈值 GDPgrowthrate = 0.384969765991486
高于该阈值的城市: ['Yulin', 'Qiannan', 'Xinzhou', 'Chuxiong', 'Zhangzhou', 'Ganzhou', 'Chengdu', 'Jiujiang', 'Fuzhou', 'Chaohu', 'Fuyang', 'Zhengzhou']
In [4]:
#删去GDP增速过快的城市
c0m0full = c0m0full[c0m0full['GDPgrowthrate']<=threshold]
c0m0= c0m0full[['ID', 'c0','m0']]

c1m1full = c1m1full[c1m1full['GDPgrowthrate']<=threshold]
c1m1= c1m1full[['ID', 'c1','m1']]

hsf15full = hsf15full[hsf15full['GDPgrowthrate']<=threshold]
hsf15= hsf15full[['ID', 'hsf15']]

hsf18full = hsf18full[hsf18full['GDPgrowthrate']<=threshold]
hsf18= hsf18full[['ID', 'hsf18']]

最优化方法¶

consumption-based:合并数据¶

In [5]:
data = pd.merge(c0m0, c1m1full,on="ID", how="inner")
data
Out[5]:
ID c0 m0 householdID communityID c1 m1 gender age marriage ... premium2018 r0 r1 r0adjust r1adjust policyintergration2015 policyintergration2018 district GDPgrowthrate urban_nbs
0 64033321002 112.216 60.0 640333210 640333 123.670 0.0 0.0 59.0 1.0 ... 180.0 0.6167 0.700 0.544192 0.660903 0.0 1.0 east 0.118005 Rural
1 64033327002 1029.200 6000.0 640333270 640333 1054.764 21000.0 0.0 62.0 1.0 ... 180.0 0.6167 0.700 0.544192 0.660903 0.0 1.0 east 0.118005 Rural
2 64033325001 1062.400 3000.0 640333250 640333 78.020 100.0 1.0 66.0 1.0 ... 180.0 0.6167 0.700 0.544192 0.660903 0.0 1.0 east 0.118005 Rural
3 64033322001 592.620 2000.0 640333220 640333 859.050 17050.0 1.0 63.0 1.0 ... 180.0 0.6167 0.700 0.544192 0.660903 0.0 1.0 east 0.118005 Rural
4 64033330002 2058.400 2000.0 640333300 640333 4025.500 2000.0 1.0 59.0 1.0 ... 180.0 0.6167 0.700 0.544192 0.660903 0.0 1.0 east 0.118005 Rural
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
3258 89676104001 2315.700 840.0 896761040 896761 891.420 8000.0 1.0 61.0 1.0 ... 220.0 0.6500 0.725 0.624462 0.685108 0.0 1.0 west 0.284050 Rural
3259 89676114002 1935.145 300.0 896761140 896761 49.800 0.0 0.0 56.0 0.0 ... 220.0 0.6500 0.725 0.624462 0.685108 0.0 1.0 west 0.284050 Rural
3260 89676118001 2466.096 1000.0 896761180 896761 661.095 3000.0 0.0 73.0 0.0 ... 220.0 0.6500 0.725 0.624462 0.685108 0.0 1.0 west 0.284050 Rural
3261 89676115001 10721.940 800.0 896761150 896761 11638.260 2000.0 1.0 55.0 1.0 ... 220.0 0.6500 0.725 0.624462 0.685108 0.0 1.0 west 0.284050 Rural
3262 89676124001 268.422 500.0 896761240 896761 313.242 101.0 1.0 69.0 0.0 ... 220.0 0.6500 0.725 0.624462 0.685108 0.0 1.0 west 0.284050 Rural

3263 rows × 26 columns

In [6]:
#最优化方法——消费计算
import pandas as pd
import numpy as np

# 计算 E(m0) 和 E(m1)
E_m0 = data['m0'].mean()
E_m1 = data['m1'].mean()

# 计算 E(c0^(-3)) 和 E(c1^(-3))
E_c0_inv2 = (data['c0']**(-3)).mean()
E_c1_inv2 = (data['c1']**(-3)).mean()

# 计算协方差
cov_c0_inv2 = np.cov(data['c0']**(-3) / E_c0_inv2, (data['r0'] - data['r1']) * data['m0'] + data['premium2015'] - data['premium2018'])[0, 1]
cov_c1_inv2 = np.cov(data['c1']**(-3) / E_c1_inv2, (data['r0'] - data['r1']) * data['m1'] + data['premium2015'] - data['premium2018'])[0, 1]

# 计算 gamma
data['gamma412'] = abs(data['premium2015'] - data['premium2018']) + abs(0.5 * (data['r0'] - data['r1']) * (E_m0 + E_m1)) + 0.5 * cov_c0_inv2 + 0.5 * cov_c1_inv2
gamma412= data['gamma412'].mean()
print(gamma412)
data
868.3633602368919
Out[6]:
ID c0 m0 householdID communityID c1 m1 gender age marriage ... r0 r1 r0adjust r1adjust policyintergration2015 policyintergration2018 district GDPgrowthrate urban_nbs gamma412
0 64033321002 112.216 60.0 640333210 640333 123.670 0.0 0.0 59.0 1.0 ... 0.6167 0.700 0.544192 0.660903 0.0 1.0 east 0.118005 Rural 673.268989
1 64033327002 1029.200 6000.0 640333270 640333 1054.764 21000.0 0.0 62.0 1.0 ... 0.6167 0.700 0.544192 0.660903 0.0 1.0 east 0.118005 Rural 673.268989
2 64033325001 1062.400 3000.0 640333250 640333 78.020 100.0 1.0 66.0 1.0 ... 0.6167 0.700 0.544192 0.660903 0.0 1.0 east 0.118005 Rural 673.268989
3 64033322001 592.620 2000.0 640333220 640333 859.050 17050.0 1.0 63.0 1.0 ... 0.6167 0.700 0.544192 0.660903 0.0 1.0 east 0.118005 Rural 673.268989
4 64033330002 2058.400 2000.0 640333300 640333 4025.500 2000.0 1.0 59.0 1.0 ... 0.6167 0.700 0.544192 0.660903 0.0 1.0 east 0.118005 Rural 673.268989
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
3258 89676104001 2315.700 840.0 896761040 896761 891.420 8000.0 1.0 61.0 1.0 ... 0.6500 0.725 0.624462 0.685108 0.0 1.0 west 0.284050 Rural 721.976891
3259 89676114002 1935.145 300.0 896761140 896761 49.800 0.0 0.0 56.0 0.0 ... 0.6500 0.725 0.624462 0.685108 0.0 1.0 west 0.284050 Rural 721.976891
3260 89676118001 2466.096 1000.0 896761180 896761 661.095 3000.0 0.0 73.0 0.0 ... 0.6500 0.725 0.624462 0.685108 0.0 1.0 west 0.284050 Rural 721.976891
3261 89676115001 10721.940 800.0 896761150 896761 11638.260 2000.0 1.0 55.0 1.0 ... 0.6500 0.725 0.624462 0.685108 0.0 1.0 west 0.284050 Rural 721.976891
3262 89676124001 268.422 500.0 896761240 896761 313.242 101.0 1.0 69.0 0.0 ... 0.6500 0.725 0.624462 0.685108 0.0 1.0 west 0.284050 Rural 721.976891

3263 rows × 27 columns

In [7]:
#异质性男性 平衡面板
#最优化方法——消费计算 
datamale = data.loc[data['gender'] == 1].copy()
import pandas as pd
import numpy as np

# 计算 E(m0) 和 E(m1)
E_m0 = datamale['m0'].mean()
E_m1 = datamale['m1'].mean()

# 计算 E(c0^(-3)) 和 E(c1^(-3))
E_c0_inv2 = (datamale['c0']**(-3)).mean()
E_c1_inv2 = (datamale['c1']**(-3)).mean()

# 计算协方差
cov_c0_inv2 = np.cov(datamale['c0']**(-3) / E_c0_inv2, (datamale['r0'] - datamale['r1']) * datamale['m0'] + datamale['premium2015'] - datamale['premium2018'])[0, 1]
cov_c1_inv2 = np.cov(datamale['c1']**(-3) / E_c1_inv2, (datamale['r0'] - datamale['r1']) * datamale['m1'] + datamale['premium2015'] - datamale['premium2018'])[0, 1]

# 计算 gamma
datamale['gamma422'] = abs(datamale['premium2015'] - datamale['premium2018']) + abs(0.5 * (datamale['r0'] - datamale['r1']) * (E_m0 + E_m1)) + 0.5 * cov_c0_inv2 + 0.5 * cov_c1_inv2
gamma422= datamale['gamma422'].mean()
print(gamma422)
694.7652383900032
In [8]:
#异质性女性 平衡面板
#最优化方法——消费计算 
datafemale = data.loc[data['gender'] == 0].copy()
import pandas as pd
import numpy as np

# 计算 E(m0) 和 E(m1)
E_m0 = datafemale['m0'].mean()
E_m1 = datafemale['m1'].mean()

# 计算 E(c0^(-3)) 和 E(c1^(-3))
E_c0_inv2 = (datafemale['c0']**(-3)).mean()
E_c1_inv2 = (datafemale['c1']**(-3)).mean()

# 计算协方差
cov_c0_inv2 = np.cov(datafemale['c0']**(-3) / E_c0_inv2, (datafemale['r0'] - datafemale['r1']) * datafemale['m0'] + datafemale['premium2015'] - datafemale['premium2018'])[0, 1]
cov_c1_inv2 = np.cov(datafemale['c1']**(-3) / E_c1_inv2, (datafemale['r0'] - datafemale['r1']) * datafemale['m1'] + datafemale['premium2015'] - datafemale['premium2018'])[0, 1]

# 计算 gamma
datafemale['gamma432'] = abs(datafemale['premium2015'] - datafemale['premium2018']) + abs(0.5 * (datafemale['r0'] - datafemale['r1']) * (E_m0 + E_m1)) + 0.5 * cov_c0_inv2 + 0.5 * cov_c1_inv2
gamma432= datafemale['gamma432'].mean()
print(gamma432)
977.057580096069
In [9]:
import numpy as np
import pandas as pd

# --------- 异质性条件 ---------
conds = {
    2:  lambda d: d["gender"].eq(1),                                            # 男
    3:  lambda d: d["gender"].eq(0),                                            # 女
    4:  lambda d: d["marriage"].eq(1),                                          # marriage=1
    5:  lambda d: d["marriage"].eq(0),                                          # marriage=0
    6:  lambda d: d["kids15"].eq(1),                                            # kids15=1
    7:  lambda d: d["kids15"].eq(0),                                            # kids15=0
    8:  lambda d: d["age"] < 59,                                                # age<59
    9:  lambda d: d["age"].between(60, 79, inclusive="both"),                   # 60~79
    10: lambda d: d["age"] >= 80,                                               # 80+
    11: lambda d: d["district"].astype(str).str.lower().eq("east"),             # east
    12: lambda d: d["district"].astype(str).str.lower().eq("middle"),           # middle
    13: lambda d: d["district"].astype(str).str.lower().eq("west"),             # west
    14: lambda d: d["hsf15"] > 40,                                              # hsf15>40
    15: lambda d: d["hsf15"].between(25, 40, inclusive="both"),                 # 25~40
    16: lambda d: d["hsf15"] < 25,                                              # <25
    17: lambda d: d["ic15"] > 35000,                                            # ic15>35000
    18: lambda d: d["ic15"].between(5000, 35000, inclusive="both"),             # 5000~35000
    19: lambda d: d["ic15"] < 5000,                                             # <5000
    20: lambda d: d["educationrevised"].isin([6,7,8,9,10,11]),                  # 教育 6-11
    21: lambda d: d["educationrevised"].eq(5),                                  # 教育 5
    22: lambda d: d["educationrevised"].isin([1,2,3,4]),                        # 教育 1-4
}

# --------- 工具函数(更稳健) ---------
def _to_num(s):
    return pd.to_numeric(s, errors="coerce")

def _safe_cov(x, y):
    z = pd.concat([x, y], axis=1).replace([np.inf, -np.inf], np.nan).dropna()
    if len(z) >= 2:
        return float(np.cov(z.iloc[:,0], z.iloc[:,1], ddof=1)[0,1])
    return 0.0

# --------- 批量计算:gamma122 … gamma1222 ---------
_results = {}
for idx, cond_fn in conds.items():
    # 取子样本
    try:
        mask = cond_fn(data)
        sub = data.loc[mask].copy() if isinstance(mask, pd.Series) and len(mask)==len(data) else data.copy()
    except Exception:
        sub = data.copy()

    if sub.empty:
        _results[f"gamma1{idx}2"] = np.nan
        continue

    # 数值化并避免 0 的 -2 次幂产生 inf
    for col in ["c0","c1","r0","r1","m0","m1","premium2015","premium2018"]:
        sub[col] = _to_num(sub[col])

    c0 = sub["c0"].replace(0, np.nan)
    c1 = sub["c1"].replace(0, np.nan)
    r0 = sub["r0"]; r1 = sub["r1"]
    m0 = sub["m0"]; m1 = sub["m1"]
    p15 = sub["premium2015"]; p18 = sub["premium2018"]

    # 子样本的均值
    E_m0 = m0.mean()
    E_m1 = m1.mean()

    c0_pow = c0 ** (-3)
    c1_pow = c1 ** (-3)
    E_c0_inv2 = c0_pow.replace([np.inf, -np.inf], np.nan).mean()
    E_c1_inv2 = c1_pow.replace([np.inf, -np.inf], np.nan).mean()

    # 协方差(标准化)
    cov0 = cov1 = 0.0
    if pd.notna(E_c0_inv2) and E_c0_inv2 != 0:
        x0 = c0_pow / E_c0_inv2
        y0 = (r0 - r1) * m0 + (p15 - p18)
        cov0 = _safe_cov(x0, y0)
    if pd.notna(E_c1_inv2) and E_c1_inv2 != 0:
        x1 = c1_pow / E_c1_inv2
        y1 = (r0 - r1) * m1 + (p15 - p18)
        cov1 = _safe_cov(x1, y1)

    # 行级 gamma,再取均值
    gamma_series = (p15 - p18).abs() + (0.5 * (r0 - r1) * (E_m0 + E_m1)).abs() + 0.5*cov0 + 0.5*cov1
    gamma_val = float(gamma_series.mean())

    name = f"gamma4{idx}2"
    _results[name] = gamma_val
    globals()[name] = gamma_val  # 可选:提升为同名变量

# 打印核对
for idx in range(2, 23):
    key = f"gamma4{idx}2"
    print(f"{key} = {_results.get(key, np.nan)}")
gamma422 = 694.7652383900032
gamma432 = 977.057580096069
gamma442 = 928.9212638897537
gamma452 = 524.1607537999155
gamma462 = 870.8773550140032
gamma472 = 503.0717633593451
gamma482 = 622.4214532204546
gamma492 = 873.8549825967619
gamma4102 = 912.6547535792567
gamma4112 = 908.1031871941158
gamma4122 = 935.7939737706656
gamma4132 = 725.8448703364339
gamma4142 = 787.6652592920365
gamma4152 = 922.5700216388028
gamma4162 = 804.7370749835521
gamma4172 = 740.3300133547918
gamma4182 = 538.9672255756258
gamma4192 = 990.5126460339013
gamma4202 = 1147.9738699490313
gamma4212 = 689.5772417336428
gamma4222 = 818.7179387434956

health-based:合并数据¶

In [10]:
dataa = pd.merge(c0m0, c1m1full,on="ID", how="inner")
dataa
Out[10]:
ID c0 m0 householdID communityID c1 m1 gender age marriage ... premium2018 r0 r1 r0adjust r1adjust policyintergration2015 policyintergration2018 district GDPgrowthrate urban_nbs
0 64033321002 112.216 60.0 640333210 640333 123.670 0.0 0.0 59.0 1.0 ... 180.0 0.6167 0.700 0.544192 0.660903 0.0 1.0 east 0.118005 Rural
1 64033327002 1029.200 6000.0 640333270 640333 1054.764 21000.0 0.0 62.0 1.0 ... 180.0 0.6167 0.700 0.544192 0.660903 0.0 1.0 east 0.118005 Rural
2 64033325001 1062.400 3000.0 640333250 640333 78.020 100.0 1.0 66.0 1.0 ... 180.0 0.6167 0.700 0.544192 0.660903 0.0 1.0 east 0.118005 Rural
3 64033322001 592.620 2000.0 640333220 640333 859.050 17050.0 1.0 63.0 1.0 ... 180.0 0.6167 0.700 0.544192 0.660903 0.0 1.0 east 0.118005 Rural
4 64033330002 2058.400 2000.0 640333300 640333 4025.500 2000.0 1.0 59.0 1.0 ... 180.0 0.6167 0.700 0.544192 0.660903 0.0 1.0 east 0.118005 Rural
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
3258 89676104001 2315.700 840.0 896761040 896761 891.420 8000.0 1.0 61.0 1.0 ... 220.0 0.6500 0.725 0.624462 0.685108 0.0 1.0 west 0.284050 Rural
3259 89676114002 1935.145 300.0 896761140 896761 49.800 0.0 0.0 56.0 0.0 ... 220.0 0.6500 0.725 0.624462 0.685108 0.0 1.0 west 0.284050 Rural
3260 89676118001 2466.096 1000.0 896761180 896761 661.095 3000.0 0.0 73.0 0.0 ... 220.0 0.6500 0.725 0.624462 0.685108 0.0 1.0 west 0.284050 Rural
3261 89676115001 10721.940 800.0 896761150 896761 11638.260 2000.0 1.0 55.0 1.0 ... 220.0 0.6500 0.725 0.624462 0.685108 0.0 1.0 west 0.284050 Rural
3262 89676124001 268.422 500.0 896761240 896761 313.242 101.0 1.0 69.0 0.0 ... 220.0 0.6500 0.725 0.624462 0.685108 0.0 1.0 west 0.284050 Rural

3263 rows × 26 columns

In [11]:
#计算dh/dm 15
import pandas as pd
import numpy as np
import statsmodels.api as sm
e1 = pd.merge(c0m0,hsf15,on="ID",how="inner")
e1= e1[['m0','hsf15']].copy()
# 删除包含 NaN 或 inf 的行
e1= e1.replace([np.inf, -np.inf], np.nan).dropna()
# 删除包含 0 的行
e1 = e1[(e1['hsf15']!=0) & (e1['m0']!=0)]
# 自变量(X)和因变量(Y)
X = e1['m0']
Y = e1['hsf15']

# 在 X 中添加常数项,以便进行 OLS 回归
X = sm.add_constant(X)

# 拟合 OLS 回归模型
model = sm.OLS(Y, X).fit()

# 输出回归结果
print(model.summary())

# 提取回归系数
coefficients = model.params

# 保存特定自变量的回归系数
h_m15 = coefficients['m0'] 
h_m15
                            OLS Regression Results                            
==============================================================================
Dep. Variable:                  hsf15   R-squared:                       0.000
Model:                            OLS   Adj. R-squared:                  0.000
Method:                 Least Squares   F-statistic:                     1.124
Date:                Mon, 29 Dec 2025   Prob (F-statistic):              0.289
Time:                        21:55:28   Log-Likelihood:                -10028.
No. Observations:                2607   AIC:                         2.006e+04
Df Residuals:                    2605   BIC:                         2.007e+04
Df Model:                           1                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const         33.6034      0.237    141.906      0.000      33.139      34.068
m0         -1.724e-05   1.63e-05     -1.060      0.289   -4.91e-05    1.46e-05
==============================================================================
Omnibus:                       37.273   Durbin-Watson:                   1.858
Prob(Omnibus):                  0.000   Jarque-Bera (JB):               28.788
Skew:                          -0.170   Prob(JB):                     5.61e-07
Kurtosis:                       2.613   Cond. No.                     1.55e+04
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.55e+04. This might indicate that there are
strong multicollinearity or other numerical problems.
Out[11]:
np.float64(-1.72380438213464e-05)
In [12]:
#计算dh/dm 18
import pandas as pd
import statsmodels.api as sm
f1 = pd.merge(c1m1,hsf18,on="ID",how="inner")
f1= f1[['m1','hsf18']].copy()
# 删除包含 NaN 或 inf 的行
f1= f1.replace([np.inf, -np.inf], np.nan).dropna()
# 删除包含 0 的行
f1 = f1[(f1['hsf18']!=0) & (f1['m1']!=0)]
# 自变量(X)和因变量(Y)
X = f1['m1']
Y = f1['hsf18']

# 在 X 中添加常数项,以便进行 OLS 回归
X = sm.add_constant(X)

# 拟合 OLS 回归模型
model = sm.OLS(Y, X).fit()

# 输出回归结果
print(model.summary())

# 提取回归系数
coefficients = model.params

# 保存特定自变量的回归系数
h_m18 = coefficients['m1'] 
h_m18
                            OLS Regression Results                            
==============================================================================
Dep. Variable:                  hsf18   R-squared:                       0.000
Model:                            OLS   Adj. R-squared:                  0.000
Method:                 Least Squares   F-statistic:                     1.259
Date:                Mon, 29 Dec 2025   Prob (F-statistic):              0.262
Time:                        21:55:28   Log-Likelihood:                -22129.
No. Observations:                4727   AIC:                         4.426e+04
Df Residuals:                    4725   BIC:                         4.427e+04
Df Model:                           1                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const         52.1728      0.391    133.428      0.000      51.406      52.939
m1         -1.333e-05   1.19e-05     -1.122      0.262   -3.66e-05    9.95e-06
==============================================================================
Omnibus:                     7734.392   Durbin-Watson:                   1.732
Prob(Omnibus):                  0.000   Jarque-Bera (JB):              372.216
Skew:                          -0.275   Prob(JB):                     1.49e-81
Kurtosis:                       1.740   Cond. No.                     3.39e+04
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 3.39e+04. This might indicate that there are
strong multicollinearity or other numerical problems.
Out[12]:
np.float64(-1.3328744662871004e-05)
In [13]:
import pandas as pd
import numpy as np

# 计算 E(m0) 和 E(m1)
E_m0 = dataa['m0'].mean()
E_m1 = dataa['m1'].mean()

# 计算 E(c0^(-3)) 和 E(c1^(-3))
E_c0_inv2 = (dataa['c0']**(-3)).mean()
E_c1_inv2 = (dataa['c1']**(-3)).mean()

# 计算协方差
cov_c0_inv2 = np.cov((0.019743 * h_m15)/ (E_c0_inv2 * dataa['r0']), (dataa['r0'] - dataa['r1']) * dataa['m0'] + dataa['premium2015'] - dataa['premium2018'])[0, 1]
cov_c1_inv2 = np.cov((0.019743 * h_m18)/ (E_c1_inv2 * dataa['r1']), (dataa['r0'] - dataa['r1']) * dataa['m1'] + dataa['premium2015'] - dataa['premium2018'])[0, 1]

# 计算 gamma
dataa['gamma413'] = abs(dataa['premium2015'] - dataa['premium2018']) + abs(0.5 * (dataa['r0'] - dataa['r1']) * (E_m0 + E_m1)) + 0.5 * cov_c0_inv2 + 0.5 * cov_c1_inv2
gamma413 = dataa['gamma413'].mean()
print(gamma413)
dataa
696.4065068044124
Out[13]:
ID c0 m0 householdID communityID c1 m1 gender age marriage ... r0 r1 r0adjust r1adjust policyintergration2015 policyintergration2018 district GDPgrowthrate urban_nbs gamma413
0 64033321002 112.216 60.0 640333210 640333 123.670 0.0 0.0 59.0 1.0 ... 0.6167 0.700 0.544192 0.660903 0.0 1.0 east 0.118005 Rural 501.312136
1 64033327002 1029.200 6000.0 640333270 640333 1054.764 21000.0 0.0 62.0 1.0 ... 0.6167 0.700 0.544192 0.660903 0.0 1.0 east 0.118005 Rural 501.312136
2 64033325001 1062.400 3000.0 640333250 640333 78.020 100.0 1.0 66.0 1.0 ... 0.6167 0.700 0.544192 0.660903 0.0 1.0 east 0.118005 Rural 501.312136
3 64033322001 592.620 2000.0 640333220 640333 859.050 17050.0 1.0 63.0 1.0 ... 0.6167 0.700 0.544192 0.660903 0.0 1.0 east 0.118005 Rural 501.312136
4 64033330002 2058.400 2000.0 640333300 640333 4025.500 2000.0 1.0 59.0 1.0 ... 0.6167 0.700 0.544192 0.660903 0.0 1.0 east 0.118005 Rural 501.312136
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
3258 89676104001 2315.700 840.0 896761040 896761 891.420 8000.0 1.0 61.0 1.0 ... 0.6500 0.725 0.624462 0.685108 0.0 1.0 west 0.284050 Rural 550.020038
3259 89676114002 1935.145 300.0 896761140 896761 49.800 0.0 0.0 56.0 0.0 ... 0.6500 0.725 0.624462 0.685108 0.0 1.0 west 0.284050 Rural 550.020038
3260 89676118001 2466.096 1000.0 896761180 896761 661.095 3000.0 0.0 73.0 0.0 ... 0.6500 0.725 0.624462 0.685108 0.0 1.0 west 0.284050 Rural 550.020038
3261 89676115001 10721.940 800.0 896761150 896761 11638.260 2000.0 1.0 55.0 1.0 ... 0.6500 0.725 0.624462 0.685108 0.0 1.0 west 0.284050 Rural 550.020038
3262 89676124001 268.422 500.0 896761240 896761 313.242 101.0 1.0 69.0 0.0 ... 0.6500 0.725 0.624462 0.685108 0.0 1.0 west 0.284050 Rural 550.020038

3263 rows × 27 columns

In [14]:
#健康based异质性探索——男性
dataamale = dataa.loc[dataa['gender'] == 1].copy()
import pandas as pd
import numpy as np

# 计算 E(m0) 和 E(m1)
E_m0 = dataamale['m0'].mean()
E_m1 = dataamale['m1'].mean()

# 计算 E(c0^(-3)) 和 E(c1^(-3))
E_c0_inv2 = (dataamale['c0']**(-3)).mean()
E_c1_inv2 = (dataamale['c1']**(-3)).mean()

# 计算协方差
cov_c0_inv2 = np.cov((0.019743 * h_m15)/ (E_c0_inv2 * dataamale['r0']), (dataamale['r0'] - dataamale['r1']) * dataamale['m0'] + dataamale['premium2015'] - dataamale['premium2018'])[0, 1]
cov_c1_inv2 = np.cov((0.019743 * h_m18)/ (E_c1_inv2 * dataamale['r1']), (dataamale['r0'] - dataamale['r1']) * dataamale['m1'] + dataamale['premium2015'] - dataamale['premium2018'])[0, 1]

# 计算 gamma
dataamale['gamma423'] = abs(dataamale['premium2015'] - dataamale['premium2018']) + abs(0.5 * (dataamale['r0'] - dataamale['r1']) * (E_m0 + E_m1)) + 0.5 * cov_c0_inv2 + 0.5 * cov_c1_inv2
gamma423 = dataamale['gamma423'].mean()
print(gamma423)
713.2089647232881
In [15]:
#健康based异质性探索——女性
dataafemale = dataa.loc[dataa['gender'] == 0].copy()
import pandas as pd
import numpy as np

# 计算 E(m0) 和 E(m1)
E_m0 = dataafemale['m0'].mean()
E_m1 = dataafemale['m1'].mean()

# 计算 E(c0^(-3)) 和 E(c1^(-3))
E_c0_inv2 = (dataafemale['c0']**(-3)).mean()
E_c1_inv2 = (dataafemale['c1']**(-3)).mean()

# 计算协方差
cov_c0_inv2 = np.cov((0.019743 * h_m15)/ (E_c0_inv2 * dataafemale['r0']), (dataafemale['r0'] - dataafemale['r1']) * dataafemale['m0'] + dataafemale['premium2015'] - dataafemale['premium2018'])[0, 1]
cov_c1_inv2 = np.cov((0.019743 * h_m18)/ (E_c1_inv2 * dataafemale['r1']), (dataafemale['r0'] - dataafemale['r1']) * dataafemale['m1'] + dataafemale['premium2015'] - dataafemale['premium2018'])[0, 1]

# 计算 gamma
dataafemale['gamma433'] = abs(dataafemale['premium2015'] - dataafemale['premium2018']) + abs(0.5 * (dataafemale['r0'] - dataafemale['r1']) * (E_m0 + E_m1)) + 0.5 * cov_c0_inv2 + 0.5 * cov_c1_inv2
gamma433 = dataafemale['gamma433'].mean()
print(gamma433)
686.9411944892615
In [16]:
import numpy as np
import pandas as pd

PHI = 0.019743

# ========== 工具函数 ==========
def safe_filter(df, cond_fn):
    """对 df 应用条件;若缺列/异常则返回原 df(不筛选)"""
    try:
        m = cond_fn(df)
        if isinstance(m, pd.Series) and len(m) == len(df):
            return df.loc[m].copy()
    except Exception:
        pass
    return df.copy()

def _to_num(s):
    return pd.to_numeric(s, errors="coerce")

def _safe_cov(x, y):
    z = pd.concat([x, y], axis=1).replace([np.inf, -np.inf], np.nan).dropna()
    if len(z) >= 2:
        return float(np.cov(z.iloc[:,0], z.iloc[:,1], ddof=1)[0,1])
    return 0.0

def _align_health_for_subset(dataa_full, data_sub, h_full, id_col="ID"):
    """
    将全样本 h_m15/h_m18 与子样本对齐(索引或按 ID)。
    支持:Series(索引=dataa.index 或 =ID)、array-like。
    """
    if isinstance(h_full, pd.Series):
        if h_full.index.equals(dataa_full.index):
            return h_full.loc[data_sub.index]
        if id_col in data_sub.columns and h_full.index.isin(data_sub[id_col]).any():
            s = h_full.reindex(data_sub[id_col])
            s.index = data_sub.index
            return s
        try:
            return h_full.loc[data_sub.index]
        except Exception:
            return pd.Series(np.nan, index=data_sub.index)
    # array-like
    try:
        base = pd.Series(h_full, index=dataa_full.index)
        return base.loc[data_sub.index]
    except Exception:
        return pd.Series(np.nan, index=data_sub.index)

def compute_gamma_health_explore(dataa_full, data_sub, h_m15, h_m18):
    """
      1) 子样本内均值:E_m0, E_m1;Ec0 = E[c0^(-3)], Ec1 = E[c1^(-3)]
      2) 协方差:
         cov0 = Cov( (PHI*h_m15)/(Ec0*r0), (r0-r1)*m0 + p15 - p18 )
         cov1 = Cov( (PHI*h_m18)/(Ec1*r1), (r0-r1)*m1 + p15 - p18 )
      3) 行级 gamma_i = |p15 - p18| + |0.5*(r0-r1)*(E_m0+E_m1)| + 0.5*cov0 + 0.5*cov1
         返回子样本内 gamma_i 的均值
    """
    # 数值化
    for col in ["c0","c1","r0","r1","m0","m1","premium2015","premium2018"]:
        data_sub[col] = _to_num(data_sub[col])

    # 子样本均值
    E_m0 = data_sub["m0"].mean()
    E_m1 = data_sub["m1"].mean()

    # 处理 c0/c1 的 0
    c0 = data_sub["c0"].replace(0, np.nan)
    c1 = data_sub["c1"].replace(0, np.nan)
    Ec0 = (c0 ** (-3)).replace([np.inf, -np.inf], np.nan).mean()
    Ec1 = (c1 ** (-3)).replace([np.inf, -np.inf], np.nan).mean()

    # 对齐 h_m15 / h_m18
    h15 = _align_health_for_subset(dataa_full, data_sub, h_m15)
    h18 = _align_health_for_subset(dataa_full, data_sub, h_m18)

    r0 = data_sub["r0"].replace(0, np.nan)
    r1 = data_sub["r1"].replace(0, np.nan)
    m0 = data_sub["m0"]; m1 = data_sub["m1"]
    p15 = data_sub["premium2015"]; p18 = data_sub["premium2018"]

    # 协方差(Ec0/Ec1 为 0 或 NaN 时相应项置 0)
    cov0 = cov1 = 0.0
    if pd.notna(Ec0) and Ec0 != 0:
        a0 = (PHI * h15) / (Ec0 * r0)
        y0 = (r0 - r1) * m0 + (p15 - p18)
        cov0 = _safe_cov(a0, y0)
    if pd.notna(Ec1) and Ec1 != 0:
        a1 = (PHI * h18) / (Ec1 * r1)
        y1 = (r0 - r1) * m1 + (p15 - p18)
        cov1 = _safe_cov(a1, y1)

    # 行级 gamma,再取均值
    gamma_series = (p15 - p18).abs() + (0.5 * (data_sub["r0"] - data_sub["r1"]) * (E_m0 + E_m1)).abs() + 0.5*cov0 + 0.5*cov1
    return float(gamma_series.mean())

# ========== 异质性条件 ==========
conds = {
    2:  lambda d: d["gender"].eq(1),                                            # 男
    3:  lambda d: d["gender"].eq(0),                                            # 女
    4:  lambda d: d["marriage"].eq(1),                                          # marriage=1
    5:  lambda d: d["marriage"].eq(0),                                          # marriage=0
    6:  lambda d: d["kids15"].eq(1),                                            # kids15=1
    7:  lambda d: d["kids15"].eq(0),                                            # kids15=0
    8:  lambda d: d["age"] < 59,                                                # age<59
    9:  lambda d: d["age"].between(60, 79, inclusive="both"),                   # 60~79
    10: lambda d: d["age"] >= 80,                                               # 80+
    11: lambda d: d["district"].astype(str).str.lower().eq("east"),             # east
    12: lambda d: d["district"].astype(str).str.lower().eq("middle"),           # middle
    13: lambda d: d["district"].astype(str).str.lower().eq("west"),             # west
    14: lambda d: d["hsf15"] > 40,                                              # hsf15>40
    15: lambda d: d["hsf15"].between(25, 40, inclusive="both"),                 # 25~40
    16: lambda d: d["hsf15"] < 25,                                              # <25
    17: lambda d: d["ic15"] > 35000,                                            # ic15>35000
    18: lambda d: d["ic15"].between(5000, 35000, inclusive="both"),             # 5000~35000
    19: lambda d: d["ic15"] < 5000,                                             # <5000
    20: lambda d: d["educationrevised"].isin([6,7,8,9,10,11]),                  # 教育 6-11
    21: lambda d: d["educationrevised"].eq(5),                                  # 教育 5
    22: lambda d: d["educationrevised"].isin([1,2,3,4]),                        # 教育 1-4
}

# ========== 批量计算:gamma123 … gamma1223 ==========
_results = {}
for idx, cond_fn in conds.items():
    sub = safe_filter(dataa, cond_fn) 
    if sub.empty:
        val = np.nan
    else:
        val = compute_gamma_health_explore(dataa_full=dataa, data_sub=sub, h_m15=h_m15, h_m18=h_m18)
    name = f"gamma4{idx}3"
    _results[name] = val
    globals()[name] = val 

# 打印核对
for idx in range(2, 23):
    key = f"gamma4{idx}3"
    print(f"{key} = {_results.get(key, np.nan)}")
gamma423 = 713.2089647232881
gamma433 = 686.9411944892615
gamma443 = 755.2935186124944
gamma453 = 495.3709187196489
gamma463 = 701.0600646792204
gamma473 = 384.02320419050864
gamma483 = 692.6520656288419
gamma493 = 680.4721585389249
gamma4103 = 554.6613086282168
gamma4113 = 538.9687740426275
gamma4123 = 790.6987379683895
gamma4133 = 637.7637835990973
gamma4143 = 623.7171886986312
gamma4153 = 812.5947344792033
gamma4163 = 605.8677243993955
gamma4173 = 634.2296693498457
gamma4183 = 555.12008641023
gamma4193 = 754.2440697210108
gamma4203 = 779.6733659370648
gamma4213 = 835.7746510292079
gamma4223 = 657.4253712423545

用完全信息法求解¶

In [17]:
d1 = pd.merge(c0m0, c1m1, on="ID", how="inner")
d2 = pd.merge(d1, hsf15, on="ID", how="inner")
d3 = pd.merge(d2, hsf18, on="ID", how="inner")
d3
Out[17]:
ID c0 m0 c1 m1 hsf15 hsf18
0 64033321002 112.216 60.0 123.670 0.0 54.436016 10.551840
1 64033327002 1029.200 6000.0 1054.764 21000.0 28.096293 39.754467
2 64033322001 592.620 2000.0 859.050 17050.0 21.303569 59.803845
3 64033330002 2058.400 2000.0 4025.500 2000.0 34.523055 68.626626
4 64033341001 5436.500 500.0 1806.578 2500.0 50.384267 78.657775
... ... ... ... ... ... ... ...
3145 89676104001 2315.700 840.0 891.420 8000.0 27.522919 42.058152
3146 89676114002 1935.145 300.0 49.800 0.0 35.235805 15.917436
3147 89676118001 2466.096 1000.0 661.095 3000.0 32.251424 54.653869
3148 89676115001 10721.940 800.0 11638.260 2000.0 32.757347 73.821948
3149 89676124001 268.422 500.0 313.242 101.0 34.706036 95.825352

3150 rows × 7 columns

In [18]:
import numpy as np
import pandas as pd
# 参数
sigma = 3.0
phi_tilde = 0.019743

d3["B_bar"] = (d3["c0"]**(1 - sigma)) + (1 - sigma) * phi_tilde * (d3["hsf15"] - d3["hsf18"])
d3["gamma411"] = d3["c1"] - d3["B_bar"]**(1 / (1 - sigma))

gamma411= d3["gamma411"].mean()
print(gamma411)
d3
1966.4793432831154
Out[18]:
ID c0 m0 c1 m1 hsf15 hsf18 B_bar gamma411
0 64033321002 112.216 60.0 123.670 0.0 54.436016 10.551840 -1.732731 NaN
1 64033327002 1029.200 6000.0 1054.764 21000.0 28.096293 39.754467 0.460336 1053.290118
2 64033322001 592.620 2000.0 859.050 17050.0 21.303569 59.803845 1.520225 858.238953
3 64033330002 2058.400 2000.0 4025.500 2000.0 34.523055 68.626626 1.346614 4024.638256
4 64033341001 5436.500 500.0 1806.578 2500.0 50.384267 78.657775 1.116408 1805.631570
... ... ... ... ... ... ... ... ... ...
3145 89676104001 2315.700 840.0 891.420 8000.0 27.522919 42.058152 0.573938 890.100020
3146 89676114002 1935.145 300.0 49.800 0.0 35.235805 15.917436 -0.762805 NaN
3147 89676118001 2466.096 1000.0 661.095 3000.0 32.251424 54.653869 0.884583 660.031762
3148 89676115001 10721.940 800.0 11638.260 2000.0 32.757347 73.821948 1.621477 11637.474684
3149 89676124001 268.422 500.0 313.242 101.0 34.706036 95.825352 2.413371 312.598293

3150 rows × 9 columns

In [19]:
#异质性男性
import numpy as np
import pandas as pd
d4 = pd.merge(d1, hsf18full, on="ID", how="inner")
dmale = d4.loc[d4['gender'] == 1].copy()

# 参数
sigma = 3.0
phi_tilde = 0.019743

dmale["B_bar"] = (dmale["c0"]**(1 - sigma)) + (1 - sigma) * phi_tilde * (dmale["hsf15"] - dmale["hsf18"])
dmale["gamma421"] = dmale["c1"] - dmale["B_bar"]**(1 / (1 - sigma))

gamma421= dmale["gamma421"].mean()
print(gamma421)
1926.768109895933
In [20]:
#异质性女性
import numpy as np
import pandas as pd
d4 = pd.merge(d1, hsf18full, on="ID", how="inner")
dfemale = d4.loc[d4['gender'] == 0].copy()

# 参数
sigma = 3.0
phi_tilde = 0.019743

dfemale["B_bar"] = (dfemale["c0"]**(1 - sigma)) + (1 - sigma) * phi_tilde * (dfemale["hsf15"] - dfemale["hsf18"])
dfemale["gamma431"] = dfemale["c1"] - dfemale["B_bar"]**(1 / (1 - sigma))

gamma431= dfemale["gamma431"].mean()
print(gamma431)
2012.4698507593214
In [21]:
import numpy as np
import pandas as pd

# ===== 1) 构造 d4 =====
# 这里假定 d1 已经包含了 hsf15、c0、c1、gender 等列
d4 = pd.merge(d1, hsf18full, on="ID", how="inner")

# 若某些分组变量是字符串(比如 district),确保为字符串
if "district" in d4.columns:
    d4["district"] = d4["district"].astype(str)

# ===== 2) 异质性条件=====
conds = {
    2:  lambda d: d["gender"].eq(1),                                            # 男
    3:  lambda d: d["gender"].eq(0),                                            # 女
    4:  lambda d: d["marriage"].eq(1),                                          # marriage=1
    5:  lambda d: d["marriage"].eq(0),                                          # marriage=0
    6:  lambda d: d["kids15"].eq(1),                                            # kids15=1
    7:  lambda d: d["kids15"].eq(0),                                            # kids15=0
    8:  lambda d: d["age"] < 59,                                                # age<59
    9:  lambda d: d["age"].between(60, 79, inclusive="both"),                   # 60~79
    10: lambda d: d["age"] >= 80,                                               # 80+
    11: lambda d: d["district"].str.lower().eq("east"),                         # east
    12: lambda d: d["district"].str.lower().eq("middle"),                       # middle
    13: lambda d: d["district"].str.lower().eq("west"),                         # west
    14: lambda d: d["hsf15"] > 40,                                              # hsf15>40
    15: lambda d: d["hsf15"].between(25, 40, inclusive="both"),                 # 25~40
    16: lambda d: d["hsf15"] < 25,                                              # <25
    17: lambda d: d["ic15"] > 35000,                                            # ic15>35000
    18: lambda d: d["ic15"].between(5000, 35000, inclusive="both"),             # 5000~35000
    19: lambda d: d["ic15"] < 5000,                                             # <5000
    20: lambda d: d["educationrevised"].isin([6,7,8,9,10,11]),                  # 教育 6-11
    21: lambda d: d["educationrevised"].eq(5),                                  # 教育 5
    22: lambda d: d["educationrevised"].isin([1,2,3,4]),                        # 教育 1-4
}

# ===== 3) 核心计算(逐行算,再对子样本取均值)=====
sigma = 3.0
phi_tilde = 0.019743
power = 1.0 - sigma             # = -2
inv_power = 1.0 / power         # = -0.5

def compute_gamma_subset_like_yours(df_sub: pd.DataFrame) -> float:
    if df_sub.empty:
        return float("nan")
    # 严格按你原始写法:不做 base>0 保护
    B_bar = (df_sub["c0"] ** power) + power * phi_tilde * (df_sub["hsf15"] - df_sub["hsf18"])
    gamma_i = df_sub["c1"] - (B_bar ** inv_power)
    return float(gamma_i.mean())  # mean 默认会跳过 NaN

# ===== 4) 批量生成 gamma121 … gamma1221 =====
results = {}
for idx, cond_fn in conds.items():
    # 如果条件里用到的列不存在,会抛错;这里捕获后把该组记为 NaN(避免改变其它组结果)
    try:
        mask = cond_fn(d4)
        sub = d4.loc[mask].copy() if isinstance(mask, pd.Series) and len(mask) == len(d4) else d4.copy()
    except Exception:
        sub = pd.DataFrame(columns=d4.columns)  # 列缺失时该组置空
    name = f"gamma4{idx}1"
    results[name] = compute_gamma_subset_like_yours(sub)
    globals()[name] = results[name] 

# 打印核对
for idx in range(2, 23):
    key = f"gamma4{idx}1"
    print(f"{key} = {results.get(key, np.nan)}")
gamma421 = 1926.768109895933
gamma431 = 2012.4698507593214
gamma441 = 2066.4616597642485
gamma451 = 1454.7471914412529
gamma461 = 1978.5411036233604
gamma471 = 783.4967750642056
gamma481 = 2498.9166338774003
gamma491 = 1449.6459516847665
gamma4101 = 1052.1186704357992
gamma4111 = 2262.993445095227
gamma4121 = 2027.3414098113271
gamma4131 = 1580.7204444612387
gamma4141 = 2301.6770212805272
gamma4151 = 1909.327378044254
gamma4161 = 1481.321882864932
gamma4171 = 2480.90139971734
gamma4181 = 2063.6424961812704
gamma4191 = 1873.807932658593
gamma4201 = 2643.282946856107
gamma4211 = 2279.502473938127
gamma4221 = 1705.0261814509524
In [22]:
# -*- coding: utf-8 -*-
import pandas as pd

# 1) 行索引与数据
rows = [
    "全样本",
    "男性","女性",
    "有配偶","无配偶",
    "有子女","无子女",
    "小于59 岁","60 岁—79 岁","80 岁及以上",
    "东部","中部","西部",
    "健康状况较好","健康状况中等","健康状况较差",
    "较高收入","中等收入","较低收入",
    "教育程度较高","教育程度中等","教育程度较低",
]

data = [
[gamma411, gamma412, gamma413],
[gamma421, gamma422, gamma423],
[gamma431, gamma432, gamma433],
[gamma441, gamma442, gamma443],
[gamma451, gamma452, gamma453],
[gamma461, gamma462, gamma463],
[gamma471, gamma472, gamma473],
[gamma481, gamma482, gamma483],
[gamma491, gamma492, gamma493],
[gamma4101, gamma4102, gamma4103],
[gamma4111, gamma4112, gamma4113],
[gamma4121, gamma4122, gamma4123],
[gamma4131, gamma4132, gamma4133],
[gamma4141, gamma4142, gamma4143],
[gamma4151, gamma4152, gamma4153],
[gamma4161, gamma4162, gamma4163],
[gamma4171, gamma4172, gamma4173],
[gamma4181, gamma4182, gamma4183],
[gamma4191, gamma4192, gamma4193],
[gamma4201, gamma4202, gamma4203],
[gamma4211, gamma4212, gamma4213],
[gamma4221, gamma4222, gamma4223],
]

# 2) 多级列索引
cols = pd.MultiIndex.from_tuples([
    ("完全信息方法",""),
    ("最优化方法","仅假设效用函数\n的消费部分"),
    ("最优化方法","仅假设效用函数\n的健康部分"),
])

df = pd.DataFrame(data, index=rows, columns=cols)

# 3) 分组起始行(加粗横线)
group_starts = {
    "男性",           # 性别组
    "有配偶",         # 婚姻组
    "有子女",         # 子女组
    "45 岁—59 岁",    # 年龄组
    "东部",           # 地区组
    "健康状况较好",   # 健康组
    "较高收入",       # 收入组
    "教育程度较高"    # 教育组
}

def row_borders(row):
    label = row.name
    if label in group_starts:
        return ['border-top: 2px solid #4a4a4a'] * len(row)
    return [''] * len(row)

# 4) 样式与展示
styler = (
    df.style
      .set_table_styles([
          {'selector': 'th.col_heading.level0',
           'props': [('font-weight', '700'),
                     ('border-bottom','1px solid #4a4a4a')]},
          {'selector': 'th.col_heading.level1',
           'props': [('font-weight', '700')]},
          {'selector': 'th.row_heading',
           'props': [('font-weight', '700')]},
          {'selector': 'table',
           'props': [('border-collapse','collapse'),
                     ('font-family','-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,PingFang SC,Helvetica,Arial')]}
      ])
      .format(precision=0)
      .set_properties(**{
          'text-align': 'center',
          'padding': '6px',
          'border':'1px solid #a0a0a0'
      })
      .apply(row_borders, axis=1)
)

# 在 Jupyter 中显示
styler
Out[22]:
  完全信息方法 最优化方法
  仅假设效用函数 的消费部分 仅假设效用函数 的健康部分
全样本 1966 868 696
男性 1927 695 713
女性 2012 977 687
有配偶 2066 929 755
无配偶 1455 524 495
有子女 1979 871 701
无子女 783 503 384
小于59 岁 2499 622 693
60 岁—79 岁 1450 874 680
80 岁及以上 1052 913 555
东部 2263 908 539
中部 2027 936 791
西部 1581 726 638
健康状况较好 2302 788 624
健康状况中等 1909 923 813
健康状况较差 1481 805 606
较高收入 2481 740 634
中等收入 2064 539 555
较低收入 1874 991 754
教育程度较高 2643 1148 780
教育程度中等 2280 690 836
教育程度较低 1705 819 657