中国姓氏研究
00_前言
中国姓氏数量众多,历史源远流长。中华民族历史上的姓氏历来有百家姓之说,常云:“赵钱孙李、周吴郑王、冯陈褚卫、蒋沈韩杨”,中国人见于文献的姓氏有5662个,其中单姓3484个,复姓2032个,三字姓146个。
为什么我们喜欢说隔壁老「王」?
为什么这个村的姓大多一样?
这转学的同学/新同事的姓氏好像很少见?
这次,让我们对中国姓氏作下简单研究。
- 数据源:100w条天朝公民姓和工作地数据(已做脱敏处理)
- 分析工具:Python
- 可视化工具:Bokeh模块、Powermap、Echarts、Qgis
- PS:数据样本有限,仅代表大致趋势
01_数据清洗、整合
为了后续研究姓氏的分布,需要先将原数据的「工作地」进行数据清洗,并结合表「中国行政代码对照表」,得到其经纬度坐标。
# 导入模块
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
% matplotlib inline
import warnings
warnings.filterwarnings('ignore')
# 不发出警告
from bokeh.io import output_notebook
output_notebook()
# 导入notebook绘图模块
from bokeh.plotting import figure,show
from bokeh.models import ColumnDataSource
# 导入图表绘制、图标展示模块
# 导入ColumnDataSource模块
# 读取、清洗数据
import os
os.chdir('C:/Users/zbd/Desktop/姓氏/')
# 创建工作路径
df01 = pd.read_csv('data01.csv',encoding = 'utf-8')
df02 = pd.read_csv('data02.csv',encoding = 'utf-8')
df_city = pd.read_excel('中国行政代码对照表.xlsx')
# 读取数据
df = pd.concat([df01,df02])
df = pd.merge(df,df_city,left_on='户籍地城市编号',right_on = '行政编码')
df['工作地'] = df['工作地'].str[:15] # 只提取工作地前15个字符即可
del df['行政编码']
del df['户籍地城市编号']
# 合并数据,添加经纬度字段
# 删除无用字段
# 分别提取“工作地”中的省、市、区县
# 新建字段“工作地-省”,“工作地-市”,“工作地-区县”,如果数据中“工作地”字段无法提取省和市,则用“未识别”填充单元格
# *通过查看识别后的单元格,如果字数超过5则为“未识别”
df['工作地_省'] = df['工作地'].str.split('省').str[0]
# 识别工作地-省
df['工作地_市'] = df['工作地'].str.split('省').str[1].str.split('市').str[0]
df['工作地_市'][df['工作地_省'].str.len() > 5] = df['工作地_省'].str.split('市').str[0]
# 识别工作地-市
# 在未识别出省的数据中,可能会有市的信息
df['工作地_区县'] = ''
df['工作地_区县'][(df['工作地_市'].str.len() < 5)&(df['工作地'].str.contains('区'))] = df['工作地'].str.split('市').str[1].str.split('区').str[0] + '区'
df['工作地_区县'][(df['工作地_市'].str.len() > 5)&(df['工作地'].str.contains('区'))] = df['工作地'].str.split('区').str[0] + '区'
df['工作地_区县'][(df['工作地_市'].str.len() < 5)&(df['工作地'].str.contains('县'))] = df['工作地'].str.split('市').str[1].str.split('县').str[0] + '县'
df['工作地_区县'][(df['工作地_市'].str.len() > 5)&(df['工作地'].str.contains('县'))] = df['工作地'].str.split('县').str[0] + '县'
# 识别工作地-区县
df['工作地_省'][df['工作地_省'].str.len() > 5] = '未识别'
df['工作地_市'][df['工作地_市'].str.len() > 5] = '未识别'
df['工作地_区县'][(df['工作地_区县'].str.len() > 5) | (df['工作地_区县'].str.len() < 2)] = '未识别'
# 整理未识别单元格
df.columns = ['姓','工作地','户籍所在地_省','户籍所在地_市','户籍所在地_区县','户籍所在地_lng','户籍所在地_lat',
'工作地_省','工作地_市','工作地_区县']
# 修改列名
print('读取数据共%i条' % len(df))
df.head(20)
02_查看姓氏的分布情况
1、姓氏数量TOP20
# 筛选top20的姓氏,计数并计算占比
name_count = df['姓'].value_counts()[:20]
result1_01 = pd.DataFrame({'count':name_count, 'count_pre':name_count/name_count.sum()})
result1_01
# bokeh出联动柱状图
from bokeh.models import HoverTool
from bokeh.layouts import gridplot
# 导入模块
name_lst = result1_01.index.tolist()
source = ColumnDataSource(result1_01)
# 创建ColumnDataSource数据
hover1 = HoverTool(tooltips=[("姓氏计数", "@count")]) # 设置标签显示内容
result1 = figure(plot_width=800, plot_height=250,x_range = name_lst,
title="中国姓氏TOP20 - 计数" ,
tools=[hover1,'reset,xwheel_zoom,pan']) # 构建绘图空间
result1.vbar(x='index', top='count', source=source,width=0.9, alpha = 0.7,color = 'red')
result1.ygrid.grid_line_dash = [6, 4]
result1.xgrid.grid_line_dash = [6, 4]
# 柱状图1
hover2 = HoverTool(tooltips=[("姓氏占比", "@count_pre")]) # 设置标签显示内容
result2 = figure(plot_width=800, plot_height=250,x_range = result1.x_range,
title="中国姓氏TOP20 - 占比" ,
tools=[hover2,'reset,xwheel_zoom,pan'])
result2.vbar(x='index', top='count_pre', source=source,width=0.9, alpha = 0.7,color = 'green')
result2.ygrid.grid_line_dash = [6, 4]
result2.xgrid.grid_line_dash = [6, 4]
# 柱状图2
p = gridplot([[result1], [result2]])
# 组合图表
show(p)
为什么我们喜欢说隔壁老「王」?因为老王最多啊!
而紧随其后的是「张李刘陈」,“王张李刘陈”霸占中国姓氏人数前五,总和约占总人数一半。回想一下,身边的朋友们是否有一半属于这5个姓氏?
用词云或许能更直观地表达
# 词云分析
from scipy.misc import imread
from wordcloud import WordCloud, ImageColorGenerator
path = 'C:/Users/zbd/Desktop/姓氏/'
back_coloring_path = path + 'timg.jpg' # 设置背景图片路径
imgname1 = path + 'WordCloudDefautColors.png' # 保存的图片名字1(只按照背景图片形状)
font_path = r'./fonts\simkai.ttf' # 为matplotlib设置中文字体路径 ----- 主要是中文时使用
# 设置词云属性
wc = WordCloud( font_path = font_path, # 设置字体
background_color = 'white', # 设置背景颜色
max_words = 1000, # 设置显示的最大词数
mask = back_coloring, # 设置背景图片
max_font_size = 200, # 设置字体最大值
min_font_size = 5, # 设置字体最小值
#random_state = 42,
width = 1000 , height = 860 ,margin = 2 # 设置图片默认的大小,但是如果使用背景图片的话
# 那么保存的图片大小会按照其大小保存,margin为词语边缘距离
)
# 构建词库
name_count = df['姓'].value_counts()
words = {}
for i in range(len(name_count)):
words['{}'.format(name_count.index[i])] = name_count[i]
wc.generate_from_frequencies(words)
# txt_freq例子为 { word1: fre1, word2: fre2, word3: fre3,......, wordn: fren }
plt.figure()
# 以下代码只显示--------形状与背景图片一致,颜色为默认颜色的词云
plt.imshow(wc)
plt.axis("off")
plt.show() # 绘制词云
wc.to_file(imgname1) # 保存图片
3、姓氏分布
隔壁老王最多,但他们都在哪里呢?让我们看下老王们的分布情况吧。
# 查看“王”姓的全国分布
data_wang1 = df[df['姓'] == '王']
writer = pd.ExcelWriter('C:/Users/zbd/Desktop/姓氏/wang1.xlsx')
data_wang1.to_excel(writer,'sheet1',index=False)
writer.save()
# 导出数据1
data_wang2 = data_wang1.groupby(['姓','户籍所在地_lng','户籍所在地_lat'])['户籍所在地_市'].count()
data_wang2 = data_wang2.reset_index()
writer = pd.ExcelWriter('C:/Users/zbd/Desktop/姓氏/wang2.xlsx')
data_wang2.to_excel(writer,'sheet1',index=False)
writer.save()
# 导出数据2
print('导出完成!')
利用python导出我们需要的数据格式后,利用Powermap和Echarts可以制作出老王的空间热力图和空间3D柱状图:
从图明显可以看出,老王果然无处不在啊!
而且「王」姓的分布情况与想象中的国内人口分布情况大体一致。只是,南方地区分布较少,基本集中在长江中下游地区。
反观「姬」姓,传说是黄帝之姓、周朝国姓,并且是10大姓中7个姓的起源。
但千年过去,姬姓后嗣多已改为他姓,开枝散叶。而还保留着这个古老姓氏的人口,也仍然栖息在古老中华文明的发源地——河南。
「那么姓氏又是如何迁徙的呢?」
3、姓氏迁徙
# 导出“冉”姓氏数据
data_tang = df[['姓','户籍所在地_lng','户籍所在地_lat','工作地_市','工作地_区县']][df['姓'] == '冉']
data_tang = data_tang[data_tang['工作地_市'] != '未识别']
data_tang = data_tang[data_tang['工作地_区县'] != '未识别']
data_tang.columns = ['familyname','birth_lng','birth_lat','work_city','work_district']
# 筛选并清洗数据
writer = pd.ExcelWriter('C:/Users/zbd/Desktop/姓氏/ran.xlsx')
data_tang.to_excel(writer,'sheet1',index=False)
writer.save()
# 导出数据
print('数据条数为%i条' % len(data_tang))
data_tang.head(10)
导出「冉」姓的数据,包括了出生地和工作地
利用 http://metrodata.cn的小工具中geocoding,将工作地也转化为坐标 ,并删除掉户籍地与工作地相同的人(未迁移的人)
再利用Qgis中的插件LinePlotter得到迁徙路径
最后用Echarts实现动态可视化
虽然有点卡,但还是可以看出「冉」姓主要集中在成都、重庆,同时也分布在贵州和河北等地
04_总结
本文利用100w余条包含姓氏、户籍地、工作地的样本数据,以小见大:
1、计算了中国姓氏数量和占比Top20,其中老王高居榜首,而前5名「王张李刘陈」的总占比更是超过所有姓氏的了一半
2、利用Powermap和Echarts可以制作出空间热力图或空间3D柱状图,从而可以观察某一姓氏的分布情况
3、利用Qgis和Echarts可以制作出空间OD迁徙图,从而可以观察某一姓氏的迁徙情况