统计 1880-2010 年间 全美婴儿姓名的趋势
美国社会保障总署(SSA)提供了一个1880-2010 年间
全美婴儿姓名频率的数据(names文件在这里下载)。
我们可以根据这一个数据统计做很多事情,比如:
1.计算特定名字的年度比例 (谁是最受欢迎的男女性名字?)
2.计算特定名字的相对排名
3.计算增长或者减少最快的名字
4.分析姓名趋势:元音,辅音,长度,总体多样性,拼写变化,首位字母等等。。
5.圣经中的名字,名人效应,人口结构变化等等
数据格式
比如用文本编辑器打开文件yob1880.txt,可以看到:
Mary,F,7065
Anna,F,2604
Emma,F,2003
Elizabeth,F,1939
Minnie,F,1746
Margaret,F,1578
Ida,F,1472
Alice,F,1414
Bertha,F,1320
。。。 |
上述是一个非常标准的都和分隔开的文本格式,可以利用 pandas.read_csv 指令将其加载到DataFrame
中:
什么?文件找不到? 利用pwd指令可以看看你的工作目录在哪里哦~, 可以通过cd 指令切换工作目录哦。。
Windows 里面用import os, os.chdir(“……”)
In [11]:
cd "d:\\Desktop"
d:\Desktop
In [12]: pwd
Out[12]: u'D:\\Desktop'
In [13]: names1880 = pd.read_csv('names/yob1880.txt',
names= ['name', 'sex', 'births'])
In [14]: names1880
Out[14]:
<class 'pandas.core.frame.DataFrame'>
Int64Index: 2000 entries, 0 to 1999
Data columns (total 3 columns):
name 2000 non-null values
sex 2000 non-null values
births 2000 non-null values
dtypes: int64(1), object(2) |
我们来统计一下当年的男女数量:
In [16]:
names1880.groupby('sex').births.sum()
Out[16]:
sex
F 90993
M 110493
Name: births, dtype: int64 |
如何对于所有1880-2010 年的数据统一处理?
显然单个文件和多个文件的复杂程度不同,为了避免你们反复打错,你需要利用文件编辑器将着一些代码编辑好了,复制过去。
#1880 -
2010 的数据统计
years = range(1880, 2011 )
pieces = []
columns = names= ['name', 'sex', 'births']
for year in years:
path = 'names/yob%d.txt' % year
frame = pd.read_csv(path, names = columns)
frame['year'] = year
pieces.append(frame)
#利用pd.concat 连接数据,将所有数据整合到单个DataFrame中
#注意利用ignore_index = True 可以忽略read_csv 所返回的原始行号
names = pd.concat(pieces, ignore_index =
True) |
现在看一下names 就很庞大了。。。
In [36]:
names
Out[36]:
<class 'pandas.core.frame.DataFrame'>
Int64Index: 1690784 entries, 0 to 1690783
Data columns (total 4 columns):
name 1690784 non-null values
sex 1690784 non-null values
births 1690784 non-null values
year 1690784 non-null values
dtypes: int64(2), object(2) |
有了以上这些数据以后,我们可以用groupby 或者pivot_table 在year 和sex
级别上面对其进行聚合(SUM)
#观察下按照sex
& year 统计的出生数量
total_births = names.pivot_table('births',
rows= 'year', cols='sex', aggfunc=sum)
total_births.tail()
#来,我们画个图看看是否重男轻女?
total_births.plot(title='Total births by
sex and year') |
我们再插入一个prop列,记录指定婴儿数量相对于总出生人数的比例,我们将按year&sex分组以后进行累加计算比例后新增加到每个分组中,便于以后使用。
#我们再插入一个prop列,记录指定婴儿数量相对于总出生人数的比例,我们将按year&sex分组以后进行累加计算比例后新增加到每个分组中
def add_prop(group):
#按照整数除法会出事。。。
births = group.births.astype(float)
group['prop']=births /births.sum()
return group
names = names.groupby(['year', 'sex']).apply(add_prop) |
显然,我们希望拿出比较小的一部分数据快速处理,比如每年排名前1000的姓名做一个统计(即 sex/year
的前1000名),这是一个分组操作
#这里供大家偷懒,我写了,大家可以粘贴过去运行,
**后面就不赘述了。。 **
def get_top1k(group):
return group.sort_index(by = 'births', ascending
= False)[:1000]
grouped = names.groupby(['year', 'sex'])
top1k = grouped.apply(get_top1k) |
#粘贴过去运行是这个样子的:
In [37]: def get_top1k(group):
....: return group.sort_index(by = 'births',
ascending = False)[:1000]
....:
In [38]: grouped = names.groupby(['year',
'sex'])
In [39]: top1k = grouped.apply(get_top1k)
In [40]: top1k
Out[40]:
<class 'pandas.core.frame.DataFrame'>
MultiIndex: 261877 entries, (1880, F, 0)
to (2010, M, 1677643)
Data columns (total 4 columns):
name 261877 non-null values
sex 261877 non-null values
births 261877 non-null values
year 261877 non-null values
dtypes: int64(2), object(2) |
分析一下命名趋势
#首先我们将前1000的名字分成男女两部分
boys = top1k[top1k.sex == 'M']
girls = top1k[top1k.sex == 'F']
#按照year & name 统计的总出生数透视表格
total_births = top1k.pivot_table('births',
rows= 'year', cols = 'name', aggfunc = sum)
#整理以后可以利用DataFrame 的plot方法绘制几个名字的曲线图
subset = total_births[['John', 'Harry',
'Mary', 'Marilyn' ]]
subset.plot(subplots=True, figsize = (12,10),
grid= False, title='Number of births per
year') |
很神奇的我们就可以拿到下面的图片。
数据说明有些名字在美国人民中的风光在20世纪中叶达到顶峰。。。是否在现在就风光不再了呢?
评估一下命名的多样性的增长
上图反映的情况是否 说明父母愿意给小孩起常见名字的越来越少了?
我们怎么通过数据去验证这个假定呢?
一种办法是通过计算最流行的1000个名字在总数中所占的比例。
table =
top1k.pivot_table('prop', rows='year', cols='sex',
aggfunc = sum)
table.plot(title='Sum of table1k.prop by year
& sex', yticks=np.linspace(0, 1.2, 13),
xticks= range(1880, 2020, 10)) |
这一讲就到这里了。。。为了给各位同学再多一个偷懒的机会,全部代码统一贴在下面了啊。。。。。
什么?还不会用?? 打开 pylab, ctrl-c ,ctrl-v,所有的数据和图片都有了啊。。。当然你要把names
目录copy到工作目录下面,比如你的桌面上!
** 反正我在Win7下,ctrl-v没有用,要右键再粘贴才可以完整复制Codes
import pandas
as pd;import numpy as np
#1880 - 2010 的数据统计
years = range(1880, 2011 )
pieces = []
columns = names= ['name', 'sex', 'births']
for year in years:
path = 'names/yob%d.txt' % year
frame = pd.read_csv(path, names = columns)
frame['year'] = year
pieces.append(frame)
#利用pd.concat 连接数据,将所有数据整合到单个DataFrame中
#注意利用ignore_index = True 可以忽略read_csv 所返回的原始行号
names = pd.concat(pieces, ignore_index =
True)
#观察下按照sex & year 统计的出生数量
total_births = names.pivot_table('births',
rows= 'year', cols='sex', aggfunc=sum) //应该改为total_births
= names.pivot_table('births', 'year', 'sex',
aggfunc=sum)
total_births.tail()
#来,我们画个图看看是否重男轻女?
total_births.plot(title='Total births by
sex and year')
#我们再插入一个prop列,记录指定婴儿数量相对于总出生人数的比例,我们将按year&sex分组以后进行累加计算比例后新增加到每个分组中
def add_prop(group):
#按照整数除法会出事。。。
births = group.births.astype(float)
group['prop']=births /births.sum()
return group
names = names.groupby(['year', 'sex']).apply(add_prop)
#top 1k 分组
def get_top1k(group):
return group.sort_index(by = 'births', ascending
= False)[:1000]
grouped = names.groupby(['year', 'sex'])
top1k = grouped.apply(get_top1k)
#首先我们将前1000的名字分成男女两部分
boys = top1k[top1k.sex == 'M']
girls = top1k[top1k.sex == 'F']
#按照year & name 统计的总出生数透视表格
total_births = top1k.pivot_table('births',
rows= 'year', cols = 'name', aggfunc = sum)
#整理以后可以利用DataFrame 的plot方法绘制几个名字的曲线图
subset = total_births[['John', 'Harry',
'Mary', 'Marilyn' ]]
subset.plot(subplots=True, figsize = (12,10),
grid= False, title='Number of births per
year')
table = top1k.pivot_table('prop', rows='year',
cols='sex', aggfunc = sum)
table.plot(title='Sum of table1k.prop by
year & sex', yticks=np.linspace(0, 1.2,
13), xticks= range(1880, 2020, 10)) |
|