01 起

我们拿到一份1880-2016这136年间美国婴儿取名的数据(数据源在这里下载),数据总共有189万条,57.7M的大小,字段如下:

下面我们使用python3来对该数据集进行分析,看看一百多年来,男孩女孩姓名趋势。

首先读取1880-2016年的取名数据,由于每年的数据是一个独立的dataframe,因此我们使用concat()方法将各年数据整合为一个dataframe

import pandas as pd

#读取1880-2016年婴儿姓名使用信息,由于信息表按年统计,因此需要将多年数据整合到一张表中
years=range(1880,2017)
columns=["name","gender","frequency"]
pieces=[] #用于存储循环读取的每一年的数据,list中的每个元素代表一年的数据

#使用循环读取每年数据,并将各年数据都存入pieces中,pieces列表的各元素表示各年的数据
for year in years:
    path="/Users/dengsudden/Documents/python_course/names/yob%d.txt" %year
    frame=pd.read_csv(path,names=columns)
    frame["year"]=year
    pieces.append(frame)

#中pieces列表中填入各年数据后,需要使用concat方法将pieces各元素整合在一起
baby_names=pd.concat(pieces,ignore_index=True)

baby_names.tail()

import pandas as pd

#读取1880-2016年婴儿姓名使用信息,由于信息表按年统计,因此需要将多年数据整合到一张表中
years=range(1880,2017)
columns=["name","gender","frequency"]
pieces=[] #用于存储循环读取的每一年的数据,list中的每个元素代表一年的数据

#使用循环读取每年数据,并将各年数据都存入pieces中,pieces列表的各元素表示各年的数据
for year in years:
    path="/Users/dengsudden/Documents/python_course/names/yob%d.txt" %year
    frame=pd.read_csv(path,names=columns)
    frame["year"]=year
    pieces.append(frame)

#中pieces列表中填入各年数据后,需要使用concat方法将pieces各元素整合在一起
baby_names=pd.concat(pieces,ignore_index=True)

baby_names.tail()

输出结果如下,这是数据表的最后5行,输出方便大家观察数据表结构:


02 可视化分析

1. 统计不同年份、不同性别分组下,各名字出现次数所占分组人数比例

思路:这里我们先自定义了一个函数dd_pct,然后使用了groupby按年份、性别分组,最后分别对每个分组调用我们的自定义函数add_pct

#先自定义函数,统计分组中各名字占比
def add_pct(group):
    group["pct"]=group.frequency/group.frequency.sum()*100
    return group

baby_names=baby_names.groupby(["year","gender"]).apply(add_pct)
baby_names.head()

#先自定义函数,统计分组中各名字占比
def add_pct(group):
    group["pct"]=group.frequency/group.frequency.sum()*100
    return group

baby_names=baby_names.groupby(["year","gender"]).apply(add_pct)
baby_names.head()

输出结果如下,可以看到,数据表中增加了一列pct,代表各名字出现次数在分组中的比例:

2. 排名:给出不同年份、不同性别分组下,各名字在分组中出现频率的排名

思路:现将数据按年份、性别分组,然后对分组数据的frequency列调用rank()方法降序得到排名,并将排名赋到新增的“ranked”列

baby_names["ranked"]=baby_names.groupby(["year","gender"])["frequency"].rank(ascending=False)
baby_names.head()

baby_names["ranked"]=baby_names.groupby(["year","gender"])["frequency"].rank(ascending=False)
baby_names.head()

输出结果如下:

3. 可视化:每年最流行的男孩女孩名字的使用次数所占当年出生男孩女孩总数的比例

思路:首先使用布尔型索引将数据分为男孩数据、女孩数据(注意,这时的数据中已经有ranked排名这一列了),对于男孩数据,选出每年出现频率最高的名字(ranked==1),然后按照x-y=year-pct作图,女孩数据同理。

import matplotlib.pyplot as plt
%matplotlib inline
%config InlineBackend.figure_format="retina"

#布尔索引选取数据中的男孩女孩
dfm=baby_names[baby_names.gender=="M"]
dff=baby_names[baby_names.gender=="F"]

#每年最流行的男孩名字
rank1m=dfm[dfm.ranked==1]
#每年最流行的女孩名字
rank1f=dff[dff.ranked==1]

import matplotlib.pyplot as plt
%matplotlib inline
%config InlineBackend.figure_format="retina"

#布尔索引选取数据中的男孩女孩
dfm=baby_names[baby_names.gender=="M"]
dff=baby_names[baby_names.gender=="F"]

#每年最流行的男孩名字
rank1m=dfm[dfm.ranked==1]
#每年最流行的女孩名字
rank1f=dff[dff.ranked==1]

输出结果如下,这里只输出了前5行数据:

接下来我们对以上数据可视化,从而对于最流行的名字的占比趋势有个直观的了解。

方法:调用matplotlib包,具体使用方法如下。

#绘制折线图
plt.plot(rank1m.year,rank1m.pct,color="blue",linewidth=2)

#plt.fill_between设置填充线与坐标轴之间的空间
plt.fill_between(rank1m.year,rank1m.pct,color="blue",alpha=0.2)

#设置坐标轴区间范围
plt.xlim(1880,2016)
plt.ylim(0,9)

#美化图:给图添加标题,调整字体大小等
plt.title("Popularity of 1# boys'name by year",size=18,color="blue")
plt.xlabel("Year",size=16)
plt.ylabel("% of male births",size=16)

#绘制折线图
plt.plot(rank1m.year,rank1m.pct,color="blue",linewidth=2)

#plt.fill_between设置填充线与坐标轴之间的空间
plt.fill_between(rank1m.year,rank1m.pct,color="blue",alpha=0.2)

#设置坐标轴区间范围
plt.xlim(1880,2016)
plt.ylim(0,9)

#美化图:给图添加标题,调整字体大小等
plt.title("Popularity of 1# boys'name by year",size=18,color="blue")
plt.xlabel("Year",size=16)
plt.ylabel("% of male births",size=16)

输出结果如下:

女孩姓名同理,输出结果如下:

根据以上输出结果,我们发现,不论是男孩还是女孩,每年最流行的名字所占当年分组出生人数比例都在下降,是什么原因导致的呢?

  • 民众的思想更加自主,越来越个性化
  • 还有一个原因可能是每年可选择的姓名数越来越多了,下面我们用统计结果来证明

4. 可视化:每年可选择的姓名数量趋势图

思路:按年份、性别分组,统计各分组下的姓名数量,然后根据按年份,绘制不同性别各年度可选姓名数量的趋势图

name_count=baby_names.groupby(["year","gender"]).size() #.size()返回一个series
#使用to_frame将series转换为dataframe
name_count=name_count.to_frame(name="name_count").reset_index()

#将数据按男女分组
name_countm=name_count[name_count.gender=="M"]
name_countf=name_count[name_count.gender=="F"]

name_count=baby_names.groupby(["year","gender"]).size() #.size()返回一个series
#使用to_frame将series转换为dataframe
name_count=name_count.to_frame(name="name_count").reset_index()

#将数据按男女分组
name_countm=name_count[name_count.gender=="M"]
name_countf=name_count[name_count.gender=="F"]

name_count的前5行输出结果如下:

接下来根据统计结果绘图

plt.plot(name_countm.year,name_countm.name_count,color="blue",linewidth=2)
plt.fill_between(name_countm.year,name_countm.name_count,color="blue",alpha=0.2)
plt.xlim(1880,2016)
plt.ylim(0,17000)
plt.title("Number of boy's name by year",size=20,color="blue")
plt.xlabel("Year",size=18)
plt.ylabel("Number of names",size=18)

plt.plot(name_countm.year,name_countm.name_count,color="blue",linewidth=2)
plt.fill_between(name_countm.year,name_countm.name_count,color="blue",alpha=0.2)
plt.xlim(1880,2016)
plt.ylim(0,17000)
plt.title("Number of boy's name by year",size=20,color="blue")
plt.xlabel("Year",size=18)
plt.ylabel("Number of names",size=18)

趋势图如下:

通过以上验证可以看到,不论男女,可选名字数量逐年递增,这可能也是导致每年最流行的名字占比当年出生人口比例逐年下降的因素之一。(当然,这也可能是民众思想解放,创造力爆发的结果)


03 总结

本文分析了一份189完数据量的婴儿姓名数据,主要利用python numpy/pandas模块以及matplotlib模块。

实操代码我放在了github上

实操代码

希望对你有帮助~~