引入包并做初始设置

from __future__ import division
from numpy.random import randn
import numpy as np
import matplotlib.pyplot as plt
plt.rc('figure', figsize=(12, 5))
np.set_printoptions(precision=4)
%pwd

通过命令查看数据文件

!head -n 10 names/yob1880.txt

通过pandas导入数据文件,并查看数据

import pandas as pd
names1880 = pd.read_csv('names/yob1880.txt', names=['name', 'sex', 'births'])
names1880

以sex分组,统计births

names1880.groupby('sex').births.sum()

合并众多数据文件,导入到同一个DataFrame

# 2010 is the last available year right now
years = range(1880, 2011)

pieces = []
columns = ['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)

# Concatenate everything into a single DataFrame
names = pd.concat(pieces, ignore_index=True)

concat默认是按行将DataFrame组合到一起,由于并不希望保留read_csv返回的原始行号,因此指定ignore_index=True

数据聚合

通过pivot_table对数据进行聚合,统计不同年份、不同性别的出生数

total_births = names.pivot_table('births', index='year',
                                 columns='sex', aggfunc=sum)
total_births.tail()

以sex,year分组,添加新项prop,计算指定名字的婴儿在总出生的比例。由于births是整数,因此需要在计算时将其转换为浮点数。

def add_prop(group):
    # Integer division floors
    births = group.births.astype(float)

    group['prop'] = births / births.sum()
    return group
names = names.groupby(['year', 'sex']).apply(add_prop)
names

对分组处理做有效性检查,验证prop总和是否为1.由于prop是浮点型,可以使用np.allclose检查分组总计值是否足够近似于1

np.allclose(names.groupby(['year', 'sex']).prop.sum(), 1)

为便于进一步分析,可以选取数据的一个子集:每对sex/year组合的前1000个名字

def get_top1000(group):
    return group.sort_index(by='births', ascending=False)[:1000]
grouped = names.groupby(['year', 'sex'])
top1000 = grouped.apply(get_top1000)

或者是使用手动方式

pieces = []
for year, group in names.groupby(['year', 'sex']):
    pieces.append(group.sort_index(by='births', ascending=False)[:1000])
top1000 = pd.concat(pieces, ignore_index=True)

建立索引

top1000.index = np.arange(len(top1000))

查看数据

top1000

分析命名趋势

将前1000个名字分为男女两部分

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

生成一张按year和name统计的总出生数透视表

total_births = top1000.pivot_table('births', index='year', columns='name',
                                   aggfunc=sum)
total_births

绘制'John', 'Harry', 'Mary', 'Marilyn'四个名字的曲线图

subset = total_births[['John', 'Harry', 'Mary', 'Marilyn']]
subset.plot(subplots=True, figsize=(12, 10), grid=False,
            title="Number of births per year")
plt.show()

评估命名多样性的增长

上图反映的降低情况可能意味着父母不太愿意给小孩起常见名字。

验证这个假设的方式是计算最流行的1000个名字所占的比例。

plt.figure()
table = top1000.pivot_table('prop', index='year',
                            columns='sex', aggfunc=sum)
table.plot(title='Sum of table1000.prop by year and sex',
           yticks=np.linspace(0, 1.2, 13), xticks=range(1880, 2020, 10))

plt.show()

从上图可以看出,名字多样性确实出现增长,因为前1000项的比例降低。

另一种验证方式,是计算占总出生人数前50%的不同名字的数量,这里以2010年男孩的名字为例

df = boys[boys.year == 2010]
df
prop_cumsum = df.sort_index(by='prop', ascending=False).prop.cumsum()
prop_cumsum[:10]
prop_cumsum.values.searchsorted(0.5)

由于数组索引是从0开始的,因此需要给这个结果加1,这里得到的最终结果是117

拿1900年的数组做比较,这个结果要小的多

df = boys[boys.year == 1900]
in1900 = df.sort_index(by='prop', ascending=False).prop.cumsum()
in1900.values.searchsorted(0.5) + 1

现在就可以对所有的year/sex组合执行上述计算

def get_quantile_count(group, q=0.5):
    group = group.sort_index(by='prop', ascending=False)
    return group.prop.cumsum().values.searchsorted(q) + 1

diversity = top1000.groupby(['year', 'sex']).apply(get_quantile_count)
diversity = diversity.unstack('sex')

绘制多样性图表

diversity.plot(title="Number of popular names in top 50%")
plt.show()

由图上我们可以看出,女孩的名字多样性要高于男孩,而且越来越高。

“最后一个字母”的变革

这里有一个研究:近百年来,男孩名字在最后一个字母上的分布发生显著变化。

为验证这个研究,首先对全部数组在年度、性别以及末尾字母进行聚合。

# extract last letter from name column
get_last_letter = lambda x: x[-1]
last_letters = names.name.map(get_last_letter)
last_letters.name = 'last_letter'

table = names.pivot_table('births', index=last_letters,
                          columns=['sex', 'year'], aggfunc=sum)

选出具有一定代表性的三年,输出前面几行

subtable = table.reindex(columns=[1910, 1960, 2010], level='year')
subtable.head()

对数据表进行规范化处理,计算出各性别各末尾字母所占总出生人数比例

subtable.sum()
letter_prop = subtable / subtable.sum().astype(float)

绘制各年度个性别条形图

import matplotlib.pyplot as plt

fig, axes = plt.subplots(2, 1, figsize=(10, 8))
letter_prop['M'].plot(kind='bar', rot=0, ax=axes[0], title='Male')
letter_prop['F'].plot(kind='bar', rot=0, ax=axes[1], title='Female',
                      legend=False)

从图上可知,以n结尾的男孩名字显著增长。

对完整的表进行上面的处理

letter_prop = table / table.sum().astype(float)

dny_ts = letter_prop.ix[['d', 'n', 'y'], 'M'].T
dny_ts.head()

变成女孩子的男孩子名(相反)

另一个趋势是,早年流行于男孩的名字,现在被女孩子使用。

在top1000数据集,查找以“lesl”开头的名字

all_names = top1000.name.unique()
mask = np.array(['lesl' in x.lower() for x in all_names])
lesley_like = all_names[mask]
lesley_like

利用这个结果过滤其他名字,并按名字分组计算出生数及相对频率

filtered = top1000[top1000.name.isin(lesley_like)]
filtered.groupby('name').births.sum()

按性别和年度进行聚合,并按年度进行规范化处理

table = filtered.pivot_table('births', index='year',
                             columns='sex', aggfunc='sum')
table = table.div(table.sum(1), axis=0)
table.tail()

绘制性别-年度曲线

table.plot(style={'M': 'k-', 'F': 'k--'})

results matching ""

    No results matching ""