从人口普查数据到收入洞察:一套可复用的 Pandas EDA 教程
- 原文标题:Exploring Income Patterns with Python Pandas, Matplotlib, and Seaborn
- 原文链接:https://towardsdatascience.com/from-raw-census-data-to-powerful-insights-exploring-income-patterns-with-pandas-matplotlib-seaborn/
- 原始数据集:UCI Adult Census Income Dataset
- 原始数据集链接:https://archive.ics.uci.edu/dataset/2/adult
- 本文定位:基于原文方法整理的团队分享教程;代码示例为教学脚手架,使用小样本数据复现核心分析流程,不等同于完整 1994 年美国人口普查数据结论。
你将学到什么
这篇教程不是教你画几张图,而是教你把一份原始表格数据变成可解释的收入分析结论。
完整流程分 5 步:
- 明确分析问题:收入是否与教育、工时、性别、职业、年龄有关?
- 读取数据并做基础检查。
- 清洗缺失值和异常标记。
- 用分组统计找出收入差异。
- 用图表或表格把结论讲清楚,同时说明局限。
场景背景
原文分析的是 UCI Adult Census Income Dataset。这个数据集来自 1990 年代美国人口普查,常用于预测一个人的年收入是否超过 50K 美元。
典型字段包括:
age:年龄education:教育水平workclass:工作类别occupation:职业sex:性别hours_per_week:每周工作时长income:收入分类,通常为<=50K或>50K
原文的核心结论是:收入不是由单一因素决定的。教育、职业、经验、工时、性别都可能影响收入分布,但文章主要展示的是相关性,不是因果关系。
环境准备
如果你只想跑本文的小样本统计,pandas 就够了。
python3 -m pip install pandas如果你要复现图表,再安装:
python3 -m pip install matplotlib seaborn注意:生产或团队项目里不要随手新增依赖,先确认项目是否已有数据分析环境。
示例 1:用小样本复现收入分析主流程
先准备一份小 CSV,模拟人口普查数据。
from io import StringIO
import pandas as pd
CSV_DATA = """age,education,workclass,occupation,sex,hours_per_week,income
39,Bachelors,Private,Exec-managerial,Male,45,>50K
28,HS-grad,Private,Handlers-cleaners,Male,40,<=50K
44,Masters,Private,Prof-specialty,Female,50,>50K
31,Some-college,Private,Sales,Female,38,<=50K
52,Doctorate,Self-emp-inc,Prof-specialty,Male,55,>50K
23,HS-grad,Private,Other-service,Female,35,<=50K
48,Prof-school,Self-emp-inc,Exec-managerial,Male,60,>50K
37,Bachelors,Private,Adm-clerical,Female,42,<=50K
"""
df = pd.read_csv(StringIO(CSV_DATA))
print(df.head())
print(df.shape)预期输出形态:
age education workclass occupation sex hours_per_week income
0 39 Bachelors Private Exec-managerial Male 45 >50K
...
(8, 7)这里先看两件事:
- 数据是否能正常读入。
- 行数、列数是否符合预期。
真实项目里,这一步要补充 df.info()、df.describe()、df.isna().sum(),避免拿脏数据直接画图。
示例 2:清洗缺失值和异常标记
原文数据里用 ? 表示缺失值。常见做法是先替换为 NA,再删除缺失行。
from io import StringIO
import pandas as pd
CSV_DATA = """age,education,workclass,occupation,sex,hours_per_week,income
39,Bachelors,Private,Exec-managerial,Male,45,>50K
28,HS-grad,?,Handlers-cleaners,Male,40,<=50K
44,Masters,Private,Prof-specialty,Female,50,>50K
"""
df = pd.read_csv(StringIO(CSV_DATA))
clean_df = df.replace("?", pd.NA).dropna()
print("before", df.shape)
print("after", clean_df.shape)
print(clean_df)预期输出:
before (3, 7)
after (2, 7)原文中,数据从 32,561 行清洗到 30,162 行。这个数字很重要,因为它说明分析结论基于清洗后的样本,而不是原始全集。
示例 3:按教育水平统计高收入比例
教育是原文重点分析的变量之一。下面用 groupby 计算不同教育水平下 >50K 的占比。
from io import StringIO
import pandas as pd
CSV_DATA = """age,education,workclass,occupation,sex,hours_per_week,income
39,Bachelors,Private,Exec-managerial,Male,45,>50K
28,HS-grad,Private,Handlers-cleaners,Male,40,<=50K
44,Masters,Private,Prof-specialty,Female,50,>50K
31,Some-college,Private,Sales,Female,38,<=50K
52,Doctorate,Self-emp-inc,Prof-specialty,Male,55,>50K
23,HS-grad,Private,Other-service,Female,35,<=50K
48,Prof-school,Self-emp-inc,Exec-managerial,Male,60,>50K
37,Bachelors,Private,Adm-clerical,Female,42,<=50K
"""
df = pd.read_csv(StringIO(CSV_DATA))
summary = (
df.assign(is_high_income=df["income"].eq(">50K"))
.groupby("education", as_index=False)
.agg(total=("income", "size"), high_income_rate=("is_high_income", "mean"))
.sort_values("high_income_rate", ascending=False)
)
summary["high_income_rate"] = (summary["high_income_rate"] * 100).round(1)
print(summary.to_string(index=False))预期输出:
education total high_income_rate
Doctorate 1 100.0
Masters 1 100.0
Prof-school 1 100.0
Bachelors 2 50.0
HS-grad 2 0.0
Some-college 1 0.0讲解时不要把这个结论说成“读博一定高收入”。更准确的表达是:
在这个样本中,高教育水平群体的高收入比例更高;但这只是相关性,仍需结合职业、年龄、地区等变量进一步验证。
示例 4:分析工时与收入的关系
原文用箱线图说明:高收入群体通常工作时间更长,但低收入群体中也有人每周工作很久。
先用表格复现这个观察:
from io import StringIO
import pandas as pd
CSV_DATA = """age,education,workclass,occupation,sex,hours_per_week,income
39,Bachelors,Private,Exec-managerial,Male,45,>50K
28,HS-grad,Private,Handlers-cleaners,Male,40,<=50K
44,Masters,Private,Prof-specialty,Female,50,>50K
31,Some-college,Private,Sales,Female,38,<=50K
52,Doctorate,Self-emp-inc,Prof-specialty,Male,55,>50K
23,HS-grad,Private,Other-service,Female,35,<=50K
48,Prof-school,Self-emp-inc,Exec-managerial,Male,60,>50K
37,Bachelors,Private,Adm-clerical,Female,72,<=50K
"""
df = pd.read_csv(StringIO(CSV_DATA))
result = df.groupby("income")["hours_per_week"].describe()[["count", "mean", "50%", "max"]]
print(result.round(1))预期输出形态:
count mean 50% max
income
<=50K 4.0 46.2 39.0 72.0
>50K 4.0 52.5 52.5 60.0这个结果的讲法:
- 高收入组平均工时更高。
- 低收入组也可能出现超长工时样本。
- 所以“工作更久”不是充分条件,还要看职业、技能、行业、议价能力等因素。
示例 5:如果要画图,怎么组织代码
如果你已经安装了 Matplotlib 和 Seaborn,可以这样画教育与收入关系:
import matplotlib.pyplot as plt
import seaborn as sns
sns.countplot(data=df, x="education", hue="income")
plt.xticks(rotation=45, ha="right")
plt.title("Income by Education")
plt.tight_layout()
plt.show()画工时箱线图:
sns.boxplot(data=df, x="income", y="hours_per_week")
plt.title("Hours per Week by Income")
plt.tight_layout()
plt.show()图表要服务结论,不要为了画图而画图。每张图至少回答一个问题:
- 教育越高,高收入比例是否更高?
- 高收入人群是否工作更久?
- 不同性别的收入分布是否明显不同?
- 哪些职业更集中在高收入组?
分享时可以这样讲
推荐按这个顺序讲:
- 先抛问题:收入到底由什么决定?努力、学历、职业还是运气?
- 再给数据:使用 1994 年美国人口普查 Adult 数据集。
- 讲清洗:原始 32,561 行,清洗后 30,162 行,缺失值用
?标记。 - 讲发现:教育、工时、性别、职业、年龄都与收入分布有关。
- 讲边界:这些是相关性,不是因果结论;数据也不代表今天的就业市场。
- 讲迁移:这个流程可以复用到员工薪酬、用户分层、客户价值、销售线索评分等场景。
可迁移到业务分析的模板
你可以把这套 EDA 方法迁移到任何分层问题:
- 收入分析:谁更可能进入高收入组?
- 用户分析:谁更可能成为高价值客户?
- 运维分析:哪些服务更容易触发高频告警?
- 销售分析:哪些线索更可能成交?
通用模板如下:
import pandas as pd
TARGET_COLUMN = "income"
GROUP_COLUMN = "education"
POSITIVE_LABEL = ">50K"
df = pd.read_csv("your_data.csv")
clean_df = df.replace("?", pd.NA).dropna()
analysis = (
clean_df.assign(is_positive=clean_df[TARGET_COLUMN].eq(POSITIVE_LABEL))
.groupby(GROUP_COLUMN, as_index=False)
.agg(total=(TARGET_COLUMN, "size"), positive_rate=("is_positive", "mean"))
.sort_values("positive_rate", ascending=False)
)
analysis["positive_rate"] = (analysis["positive_rate"] * 100).round(2)
print(analysis)把 TARGET_COLUMN、GROUP_COLUMN、POSITIVE_LABEL 换掉,就能复用到其他二分类分析。
常见坑
- 不要跳过缺失值检查。
?、空字符串、unknown都可能是伪装缺失值。 - 不要只看人数,要同时看比例。某个群体人数多,不代表高收入率高。
- 不要把相关性写成因果。图表只能提示问题,不能替代实验或因果建模。
- 不要忽略时间背景。1994 年的 50K 美元收入门槛不能直接迁移到今天。
- 不要让图表脱离业务问题。每张图都要服务一个明确判断。
练习任务
- 换成你自己的 CSV,找一个二分类目标字段。
- 选 3 个分组字段,例如部门、地区、岗位、客户等级。
- 分别计算正样本比例。
- 找出一个“人数多但正样本率不高”的群体。
- 写一句谨慎结论,必须包含“相关性,不代表因果”。
总结
这篇原文的价值不在于“教育一定带来高收入”这样的结论,而在于展示了一套简单、可复制的数据分析路径:
先清洗数据,再分组统计,然后用图表验证直觉,最后明确结论边界。
这套方法适合数据分析入门,也适合在业务场景里快速做第一轮探索。真正需要决策时,还要进一步加入多变量建模、时间变化、样本偏差检查和业务专家复核。