第二课 统计 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 的数据统计
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))

  * 啊呀,果然多样性越来越厉害了挖。。。。特别是女生,大家都懂的。 :-D
  • 这一讲就到这里了。。。为了给各位同学再多一个偷懒的机会,全部代码统一贴在下面了啊。。。。。
  • 什么?还不会用?? 打开 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))
  • course/python/lesson2.txt
  • 最后更改: 2015/09/22 15:48
  • (外部编辑)