如何对含互斥与非互斥类别的观测值生成交叉表及可视化
交叉统计实现方案
核心计算逻辑
你需要先把字段拆分为两组:
- 互斥类别(种族):
white、hispanic、asian - 非互斥类别(国籍):
usa、canada、uk
循环计算的步骤如下:
- 初始化计数矩阵,行对应种族、列对应国籍,初始值全为0
- 逐行遍历所有观测数据:
- 先匹配当前观测的唯一归属种族:遍历种族字段,找到值为1的字段作为当前行的种族标签
- 再遍历所有国籍字段:如果当前国籍字段值为1,就给对应
[种族][国籍]的计数位加1
- 补充百分比与合计值:
- 行合计:每个种族对应的所有国籍计数求和,得到该种族总人数
- 列合计:每个国籍对应的所有种族计数求和,得到该国籍总人数
- 单元格百分比 = 当前单元格计数 / 对应列合计 * 100,保留整数即可
Python 代码示例
这里先给出纯循环实现的版本,也附了更简洁的pandas实现供参考:
import pandas as pd # 样例数据导入 data = pd.DataFrame([ [1,0,1,0,1,0,1], [2,1,0,0,0,1,0], [3,0,0,1,1,0,1], [4,1,0,0,1,1,0], [5,0,1,0,0,0,1], [6,0,0,1,0,0,1] ], columns=["id","white","hispanic","asian","usa","canada","uk"]) race_cols = ["white", "hispanic", "asian"] nation_cols = ["usa", "canada", "uk"] # ---------- 纯循环实现 ---------- # 初始化计数表 count_table = {race: {nation:0 for nation in nation_cols} for race in race_cols} for _, row in data.iterrows(): # 匹配当前种族 curr_race = [r for r in race_cols if row[r] == 1][0] # 遍历国籍累加 for nation in nation_cols: if row[nation] == 1: count_table[curr_race][nation] += 1 # 计算合计与百分比 result = pd.DataFrame(count_table).T col_total = result.sum(axis=0) row_total = result.sum(axis=1) # 格式化为要求的带百分比样式 for col in nation_cols: result[col] = result[col].apply(lambda x: f"{x} ({int(round(x/col_total[col]*100,0))}%)" if x>0 else "0") result["total"] = row_total result.loc["total"] = list(col_total) + [""] print(result) # ---------- 简化pandas实现(无需手写循环) ---------- # 先提取种族标签列 data["race"] = data[race_cols].idxmax(axis=1) # 展开国籍多标签后生成交叉表 melted = data.melt(id_vars=["id", "race"], value_vars=nation_cols, var_name="nation", value_name="flag") melted = melted[melted["flag"] == 1] cross_final = pd.crosstab(melted["race"], melted["nation"], margins=True, margins_name="total")
可视化建议
针对这类互斥+非互斥类别交叉的数据,推荐三种可视化方案:
- 堆叠条形图:X轴设为国籍,Y轴设为人数,每个条形按种族拆分堆叠,可以直观看到每个国籍下的种族构成,兼容重复计数的统计逻辑
- 热力图:行设为种族、列设为国籍,颜色深浅对应单元格的计数或占比,能快速识别分布规律,比如哪类种族在哪个国籍的占比更高
- 分组条形图:X轴设为种族,每个种族下对应多个国籍的计数柱,适合对比不同种族的国籍分布差异
内容的提问来源于stack exchange,提问作者taylor
相关产品推荐
相关产品推荐

