除了发论文,CFPS数据还能这么玩:用Python可视化分析中国家庭十年变迁

张开发
2026/4/18 7:32:20 15 分钟阅读

分享文章

除了发论文,CFPS数据还能这么玩:用Python可视化分析中国家庭十年变迁
用Python解锁CFPS数据可视化中国家庭十年变迁的5种创新方法当一份包含数万家庭十年追踪数据的宝藏摆在面前大多数研究者第一反应可能是跑回归、做检验、发论文。但数据科学真正的魅力在于用代码讲出那些统计数字背后鲜活的社会变迁故事。中国家庭追踪调查CFPS作为国内最具代表性的微观数据库之一其价值远未被普通研究者充分挖掘——直到你看到这些动态图表import pandas as pd import seaborn as sns import matplotlib.pyplot as plt # 加载CFPS预处理数据 cfps pd.read_csv(cfps_processed.csv) plt.figure(figsize(10,6)) sns.lineplot(datacfps, xyear, yfamily_income, hueurban, estimatormedian, err_styleband) plt.title(城乡家庭收入中位数变迁 (2010-2020)) plt.xlabel(调查年份); plt.ylabel(收入对数)这张简单的折线图瞬间揭示了城乡收入差距的演变轨迹比任何表格都更具冲击力。本文将带你突破传统研究范式用Python实现五种高阶可视化技术让沉默的数据开始讲述中国家庭十年来的真实故事。1. 数据准备从原始问卷到分析就绪数据集处理CFPS这类复杂追踪数据时80%的分析效果取决于前期的数据清洗质量。不同于普通横截面数据我们需要特别注意三个特性面板数据结构同一家庭/个人在不同年份的记录需要通过pid/fid等关键字段精确匹配抽样权重处理必须使用swgt等权重变量保证结果代表性缺失值模式识别CFPS采用轮换样本设计缺失可能包含信息1.1 核心变量提取与合并CFPS包含家庭、成人、少儿等多层次问卷建议先从核心变量入手# 家庭层面关键变量 fam_vars [fid, year, urban, fincome, fasset, fexpend] # 成人层面关键变量 adult_vars [pid, fid, gender, age, edu, health, employ] # 使用pandas合并多轮调查数据 def load_cfps(waves[2010,2012,2014,2016,2018,2020]): dfs [] for wave in waves: fam pd.read_stata(fcfps{wave}_family.dta, columnsfam_vars) adult pd.read_stata(fcfps{wave}_adult.dta, columnsadult_vars) merged pd.merge(fam, adult, on[fid, year], howinner) dfs.append(merged) return pd.concat(dfs, ignore_indexTrue)提示CFPS官方提供的STATA格式数据可直接用pandas读取但需注意编码问题特别是中文文本变量1.2 数据清洗实用技巧处理真实调查数据时这些方法能大幅提升分析质量异常值处理对收入等连续变量建议使用Winsorize缩尾而非简单删除from scipy.stats.mstats import winsorize df[income] winsorize(df[income], limits[0.01, 0.01])缺失值可视化用missingno矩阵快速定位缺失模式import missingno as msno msno.matrix(df.sample(1000))追踪样本筛选识别持续参与调查的核心样本# 计算每个pid出现的调查轮次 participant_counts df[pid].value_counts() # 筛选参与至少3轮的样本 stable_sample participant_counts[participant_counts 3].index2. 时空动态用动画展现区域差异演变静态图表难以捕捉变迁过程的动态特征。以下方法可以将中国家庭的经济社会变化转化为直观的时空动画2.1 地理热力图动画虽然CFPS出于隐私考虑不提供精确地理位置但省级代码(provcd)允许我们绘制宏观区域差异。使用plotly.express可以轻松创建交互式动画import plotly.express as px # 计算各省年度平均指标 province_stats df.groupby([year,provcd])[income].median().reset_index() fig px.choropleth(province_stats, locationsprovcd, locationmodeISO-3, colorincome, animation_frameyear, color_continuous_scaleViridis, scopeasia, title中国各省家庭收入中位数变迁) fig.update_geos(fitboundslocations, visibleFalse) fig.show()2.2 教育代际流动桑基图展示父母与子女教育程度的代际传递桑基图是最佳选择。我们需要先构建跨代匹配数据# 假设已匹配父母-子女对 edu_flow df.groupby([parent_edu,child_edu,year]).size().reset_index(namecount) import plotly.graph_objects as go fig go.Figure(go.Sankey( nodedict(label[小学以下,初中,高中,大学,研究生]), linkdict( sourceedu_flow[parent_edu], targetedu_flow[child_edu], valueedu_flow[count] ) )) fig.update_layout(title_text教育代际流动模式)3. 多维透视用交互式仪表盘探索复杂关系当研究问题涉及多个维度交叉分析时静态图表很快会变得拥挤。Dash或Panel构建的交互式仪表盘能有效解决这个问题。3.1 健康-收入-教育三维关系探索import dash from dash import dcc, html import dash_bootstrap_components as dbc app dash.Dash(__name__) app.layout dbc.Container([ dbc.Row([ dbc.Col(dcc.Dropdown( idyear-selector, options[{label:y, value:y} for y in df[year].unique()], value2020 ), width3), dbc.Col(dcc.Dropdown( idregion-selector, options[{label:全国,value:all}] [{label:r,value:r} for r in df[region].unique()], valueall ), width3) ]), dbc.Row([ dbc.Col(dcc.Graph(idscatter-matrix), width12) ]) ]) app.callback( Output(scatter-matrix,figure), [Input(year-selector,value), Input(region-selector,value)] ) def update_graph(year, region): filtered df[df[year]year] if region ! all: filtered filtered[filtered[region]region] return px.scatter_matrix( filtered, dimensions[income,health,edu], colorurban, opacity0.5 )3.2 家庭结构变迁旭日图family_structure df.groupby([year,family_type])[fid].nunique().reset_index() fig px.sunburst(family_structure, path[year,family_type], valuesfid, title中国家庭结构类型占比变迁) fig.show()4. 模型可视化从统计结果到直观洞察许多高级分析方法的结果可以通过创意可视化变得更易懂。以下是两个典型案例4.1 收入影响因素SHAP值瀑布图import shap from sklearn.ensemble import GradientBoostingRegressor # 准备建模数据 X df[[age,edu,health,urban,industry]] y df[income] model GradientBoostingRegressor().fit(X, y) # 计算SHAP值 explainer shap.Explainer(model) shap_values explainer(X) # 绘制个体解释 shap.plots.waterfall(shap_values[0])4.2 多维标度法展示家庭类型相似性from sklearn.manifold import MDS from sklearn.preprocessing import StandardScaler # 计算家庭特征矩阵 fam_features df.pivot_table(indexfid, columnsyear, values[income,asset,size]) # 多维标度降维 mds MDS(n_components2) scaler StandardScaler() X_scaled scaler.fit_transform(fam_features.fillna(0)) embedding mds.fit_transform(X_scaled) # 可视化 plt.scatter(embedding[:,0], embedding[:,1], alpha0.3) plt.title(家庭经济状况多维相似性空间)5. 高级技巧让图表讲出更深刻的故事5.1 小倍数图表集展示群体差异g sns.FacetGrid(df, colbirth_cohort, hueurban, col_wrap3, height4) g.map(sns.kdeplot, income, fillTrue) g.add_legend()5.2 动态分位数回归可视化import statsmodels.formula.api as smf quantiles [0.1, 0.25, 0.5, 0.75, 0.9] results [] for q in quantiles: model smf.quantreg(income ~ edu age, df) res model.fit(qq) results.append(res.params.to_frame(str(q))) pd.concat(results, axis1).T.plot()在完成这些可视化后你会发现数据中浮现出许多传统分析难以察觉的模式比如城乡差异在某些维度正在缩小而在另一些维度持续扩大教育代际传递存在明显的非线性门槛效应健康不平等的地理分布与经济水平并不完全重合。这些发现都可能成为新的研究起点。好的数据可视化就像给社会变迁装上显微镜和望远镜——既能看清微观个体生活的变化细节又能把握宏观结构的演变趋势。当你在Jupyter Notebook中交互式探索这些图表时或许会和我一样感受到数据科学最令人着迷的一点用代码将冰冷数字转化为对社会现实的温暖理解。

更多文章