利用Python进行数据分析——第二章 引言(2):利用pandas对babynames数据集进行简单处理

使用数据集为1880年-1929年间美国婴儿名字的频率数据。数据集参见我的资源,附有网址链接。

数据为txt格式,部分数据如下图所示:

python nlp包 识别姓名 python姓名分析_pandas


python nlp包 识别姓名 python姓名分析_python nlp包 识别姓名_02


根据该数据及,可以进行以下处理:

  • 计算指定名字的年度比例;
  • 计算某个名字的相对排名;
  • 计算各年度最流行的名字,以及增长或减少最快的名字;
  • 分析名字的趋势:元音、辅音、长度、总体多样性、拼写变化、首位字母等;
  • 分析外源性趋势:圣经中的名字、名人、人口结构变化等。
    在次,只进行一些简单的操作。

先对其中一个文件进行操作,查看一下数据的相关内容:

import pandas as pd
import matplotlib.pyplot as plt
names = ["name","sex","births"]#给定读入数据的标签
names1880 = pd.read_csv("C:/Users/GuanLi/Desktop/babynames/datasetsbabynamesyob1880.txt",sep = ",",names=names)
print(names1880[:10])#输出前10条数据进行查看

可以看到前十条数据记录如下,含有姓名name、性别sex、出生数births等字段。

name sex  births
0       Mary   F   7065
1       Anna   F   2604
2       Emma   F   2003
3  Elizabeth   F   1939
4     Minnie   F   1746
5   Margaret   F   1578
6        Ida   F   1472
7      Alice   F   1414
8     Bertha   F   1320
9      Sarah   F   1288

按照性别对1880年的出生婴儿总数量进行统计。

sex_births_sum = names1880.groupby("sex").births.sum()
print(sex_births_sum)

输出结果如下:

sex
F     90993
M    110493
Name: births, dtype: int64

上面是对数据集中1880年的婴儿姓名数据进行处理,所使用的整个数据集含有1880年-1929年共50个数据文件。所以,首先将所有数据整合到一个DataFrame中,并加上一个年份year字段,此处调用pandas.concat。

# coding=gbk
import pandas as pd
names = ["name","sex","births"]
years=range(1880,1930)
pieces = []
#通过for循环将50个文件读入
for year in years:
	path = "C:/Users/GuanLi/Desktop/babynames/datasetsbabynamesyob"+str(year)+".txt"
	frame = pd.read_csv(path,names = names)
	frame["year"]=year
	pieces.append(frame)#将frame添加到列表pieces中
names = pd.concat(pieces,ignore_index=True)##concat默认按行将多个DataFrame组合到一起
print(names)

整合以后的数据部分如下:

name sex  births  year
0            Mary   F    7065  1880
1            Anna   F    2604  1880
2            Emma   F    2003  1880
3       Elizabeth   F    1939  1880
4          Minnie   F    1746  1880
5        Margaret   F    1578  1880
...
274820    Yoshimi   M       5  1929
274821    Yoshiro   M       5  1929
274822   Zeferino   M       5  1929
274823       Zell   M       5  1929
274824    Zygmund   M       5  1929

将50个数据文件整合到一起之后,形成了一个含有274825条记录的数据,含有name、sex、births、year四个字段。
有了这些数据,就可以统计在1880年-1929年间,不同性别下婴儿的数量。

total_births = names.pivot_table("births",index="year",columns="sex",aggfunc=sum)
print(total_births)

输出结果如下:

sex         F        M
year
1880    90993   110493
1881    91955   100748
1882   107851   113687
1883   112322   104632
1884   129021   114445
1885   133056   107802
1886   144538   110785
1887   145983   101412
1888   178631   120857
1889   178369   110590
1890   190377   111026
1891   185486   101198
1892   212350   122038
1893   212908   112319
1894   222923   115775
1895   233632   117398
1896   237924   119575
1897   234199   112760
1898   258771   122703
1899   233022   106218
1900   299873   150554
1901   239351   106478
1902   264079   122660
1903   261976   119240
1904   275375   128129
1905   291641   132319
1906   295301   133159
1907   318558   146838
1908   334277   154339
1909   347191   163983
1910   396416   194198
1911   418180   225936
1912   557939   429926
1913   624317   512482
1914   761376   654746
1915   983824   848647
1916  1044249   890142
1917  1081194   925512
1918  1157585  1013720
1919  1130149   980215
1920  1198214  1064468
1921  1232845  1101374
1922  1200796  1088380
1923  1206239  1096227
1924  1248821  1132671
1925  1217217  1115798
1926  1185078  1110440
1927  1192207  1126259
1928  1152836  1107113
1929  1116284  1074833

在有了每一年的不同性别下出生婴儿的总数,就可以计算每个名字被使用的比例(频率)。插入prop列用来存放比例。先按year和sex分组,然后将新列(prop列)加到各个分组上。

#定义函数add_prop()来计算比例
def add_prop(group):
	births = group.births.astype(float)#将出生数births装换成float型数据
	group["prop"] = births/births.sum()#计算比例
	return group
names = names.groupby(["year","sex"]).apply(add_prop)
print(names)

输出的结果如下:

name sex  births  year      prop
0            Mary   F    7065  1880  0.077643
1            Anna   F    2604  1880  0.028618
2            Emma   F    2003  1880  0.022013
3       Elizabeth   F    1939  1880  0.021309
4          Minnie   F    1746  1880  0.019188
5        Margaret   F    1578  1880  0.017342
...
274820    Yoshimi   M       5  1929  0.000005
274821    Yoshiro   M       5  1929  0.000005
274822   Zeferino   M       5  1929  0.000005
274823       Zell   M       5  1929  0.000005
274824    Zygmund   M       5  1929  0.000005

如第一行数据prop=0.077643,表示每100个女婴儿中,就有8个(四舍五入)取名为Mary。
另外还可以绘制1880-1929年间美国出生婴儿不同性别随时间的变化图:

year = []#定义一个列表,用来做图的横轴年份
for i in range(1880,1930):
	year.append(i)
fig=plt.figure()
ax=fig.add_subplot(1,1,1)
ax.set_xlabel=("years")
plt.plot(year,total_births["M"])
plt.plot(year,total_births["F"])
plt.legend(loc="best",edgecolor="black")#设置图例边框颜色
plt.legend(loc="best",title="sex")
plt.title("Total births by sex and year")
plt.show()

绘图结果如下:

python nlp包 识别姓名 python姓名分析_groupby_03


在上面一系列操作之后,得到的一个数据集names含有以下字段:name、sex、births、year、prop。在此数据中,我们取出每对sex/year组合的前1000个名字进行进一步的分析工作。

#定义抽取函数get_top1000()
def get_top1000(group):
	return group.sort_index(by="births",ascending=False)[:1000]
grouped = names.groupby(["sex","year"])
top1000 = grouped.apply(get_top1000)
print(top1000)

接下来的工作在top1000数据集上进行相关的分析。
分析起名字的趋势:
首先将前1000个名字分为男、女两部分。

boys = top1000[top1000.sex == "M"]
girls = top1000[top1000.sex == "F"]

在此基础上,可以进行某些名字在美国婴儿中使用的变化情况分析。比如,绘制top1000中,John、Harry、Mary三个名字随时间的变化趋势:

fig1=plt.figure()
plt.plot(year,top1000_total_births["John"])
plt.plot(year,top1000_total_births["Harry"])
plt.plot(year,top1000_total_births["Mary"])
plt.title("Number of births per year")
plt.legend(loc="best",edgecolor="black",title="name")
plt.show()

结果如下图:

python nlp包 识别姓名 python姓名分析_数据_04


可以看出在1880-1929年间,三个名字趋势为先上升后下降。这也说明三个名字在一开始很流行,到后来父母给孩子起比较流行,或者说大众化的名字就很少了。教材上采用了1880-2010年间的数据,可以看到更长时间范围内的变化。

为进一步验证名字的变化趋势,可以计算最流行的1000个名字所占的比例,并绘制变化图。

table=top1000.pivot_table("prop",index="year",columns="sex",aggfunc=sum)
fig2=plt.figure()
plt.plot(year,table["F"])
plt.plot(year,table["M"])
plt.legend(loc="best")
plt.title("sum of top1000.prop by year and sex")
plt.show()

结果如下:

sex          F         M
year
1880  1.000000  0.997375
1881  1.000000  1.000000
1882  0.998702  0.995646
1883  0.997596  0.998566
1884  0.993156  0.994539
1885  0.992251  0.995501
1886  0.989504  0.995035
1887  0.988279  0.996697
1888  0.984241  0.992429
1889  0.984061  0.994981
1890  0.982566  0.992749
1891  0.982177  0.993725
1892  0.979746  0.988815
1893  0.980001  0.991720
1894  0.978571  0.989048
1895  0.975479  0.989071
1896  0.975660  0.988041
1897  0.976558  0.989349
1898  0.972806  0.987197
1899  0.975170  0.990115
1900  0.967760  0.979702
1901  0.972304  0.989603
1902  0.970467  0.985749
1903  0.969490  0.986020
1904  0.968142  0.982502
1905  0.967038  0.981650
1906  0.967535  0.981759
1907  0.964942  0.976975
1908  0.964500  0.976409
1909  0.962744  0.973412
1910  0.959797  0.969778
1911  0.960108  0.967743
1912  0.955857  0.958370
1913  0.953979  0.955881
1914  0.951697  0.952579
1915  0.948814  0.950604
1916  0.948050  0.950211
1917  0.947547  0.950382
1918  0.946388  0.950806
1919  0.945567  0.949201
1920  0.945281  0.950184
1921  0.945001  0.951235
1922  0.944200  0.950868
1923  0.945099  0.952030
1924  0.944637  0.952422
1925  0.944954  0.953723
1926  0.945526  0.953889
1927  0.945487  0.954031
1928  0.946658  0.954890
1929  0.947905  0.956236

python nlp包 识别姓名 python姓名分析_groupby_05


从图中可以看出,名字的多样性出现了增长,从而使某一具体的名字所占比例出现下降。假设我们将名字所占比例从高到低排序,前50%的名字定义为比较流行的名字。那么可以根据前50%名字的数量来观察名字的多样性变化。

def get_quantile_count(group,q=0.5):
	group = group.sort_index(by="prop",ascending=False)
	return group.prop.cumsum().searchsorted(q) + 1
diversity = top1000.groupby(["year","sex"]).apply(get_quantile_count)
diversity = diversity.unstack("sex")
print(diversity)
fig3=plt.figure()
plt.plot(year,diversity["F"])
plt.plot(year,diversity["M"])
plt.legend(loc="best")
plt.title("Number of popular names in top 50%")
plt.show()

可以得到1880-1930年间,占比前50%的姓名数量和趋势图:

sex      F     M
year
1880  [38]  [14]
1881  [38]  [14]
1882  [38]  [15]
1883  [39]  [15]
1884  [39]  [16]
...
1925  [52]  [28]
1926  [52]  [28]
1927  [52]  [28]
1928  [52]  [28]
1929  [51]  [28]

python nlp包 识别姓名 python姓名分析_groupby_06


从图中可以看出女孩姓名的多样性总是比男孩的高。两者的总体变化趋势为升高,即婴儿名字的多样性在升高,名字种类越来越多。

(后面还有名字末字母的分析、名字“变性”分析,_,不做了,哈哈)