用UNIX的head命令查看了其中一個文件的前10行
!head -n 10 datasets/babynames/yob1880.txt
不巧的Windows下無法執(zhí)行
!type -10 yob1880.txt

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

我們可以用births列的sex分組小計表示該年度的births總計
names1880.groupby('sex').births.sum()

由于該數(shù)據(jù)集按年度被分隔成了多個文件,所以第一件事情就是要將所有數(shù)據(jù)都組裝到一個 DataFrame里面,并加上一個year字段。使用pandas.concat即可達到這個目的:
years = range(1880, 2011)
pieces = []
columns = ['name', 'sex', 'births']
for year in years:
path = 'babynames/yob%d.txt' % year
frame = pd.read_csv(path, names=columns)
frame['year'] = year
pieces.append(frame)

這里需要注意幾件事情。第一,concat默認是按行將多個DataFrame組合到一起的;第二,必須指定ignore_index=True,因為我們不希望保留read_csv所返回的原始行號。
利用groupby或pivot_table在year和sex級別上對其進行聚合了
# Concatenate everything into a single DataFrame
names = pd.concat(pieces, ignore_index=True)
total_births = names.pivot_table('births', index='year',columns='sex', aggfunc=sum)

繪制出生嬰兒數(shù)量曲線統(tǒng)計
total_births.plot(title='Total births by sex and year')

插入一個prop列,用于存放指定名字的嬰兒數(shù)相對于總出生數(shù)的比例:
def add_prop(group):
group['prop'] = group.births / group.births.sum()
return group
names = names.groupby(['year', 'sex']).apply(add_prop)

在執(zhí)行這樣的分組處理時,一般都應(yīng)該做一些有效性檢查,比如驗證所有分組的prop的總和是否為1
names.groupby(['year', 'sex']).prop.sum()

工作完成。為了便于實現(xiàn)更進一步的分析,我需要取出該數(shù)據(jù)的一個子集:每對sex/year組合的前1000個名字。這又是一個分組操作
def get_top1000(group):
return group.sort_values(by='births', ascending=False)[:1000]
grouped = names.groupby(['year', 'sex'])
top1000 = grouped.apply(get_top1000)
# Drop the group index, not needed
top1000.reset_index(inplace=True, drop=True)

接下來的數(shù)據(jù)分析工作就針對這個top1000數(shù)據(jù)集
分析命名趨勢
首先將前1000個名字分為男女兩個部分
boys = top1000[top1000.sex == 'M']
girls = top1000[top1000.sex == 'F']
生成一張按year和name統(tǒng)計的總出生數(shù)透視表
total_births = top1000.pivot_table('births', index='year',columns='name',aggfunc=sum)
我們用DataFrame的plot方法繪制幾個名字的曲線圖
total_births.info()
subset = total_births[['John', 'Harry', 'Mary', 'Marilyn']]
subset.plot(subplots=True, figsize=(12, 10), grid=False, title="Number of births per year")

評估命名多樣性的增長
計算流行的1000個名字所占的比例,按year和sex進行聚合并繪圖
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))

從圖中可以看出,名字的多樣性確實出現(xiàn)了增長(前1000項的比例降低)
另一個辦法是計算占總出生人數(shù)前50%的不同名字的數(shù)量,這個數(shù)字不太好計算。我們只考慮 2010年男孩的名字:

在對prop降序排列之后,我們想知道前面多少個名字的人數(shù)加起來才夠50%。雖然編寫一個 for循環(huán)確實也能達到目的,但NumPy有一種更聰明的矢量方式。先計算prop的累計和 cumsum,然后再通過searchsorted方法找出0.5應(yīng)該被插入在哪個位置才能保證不破壞順序
prop_cumsum = df.sort_values(by='prop', ascending=False).prop.cumsum()

由于數(shù)組索引是從0開始的,因此我們要給這個結(jié)果加1,即終結(jié)果為117
def get_quantile_count(group, q=0.5):
group = group.sort_values(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()
從圖中可以看出,女孩名字的多樣性總是比男孩的高,而且還在變得越來越高

“最后一個字母”的變革
2007年,一名嬰兒姓名研究人員Laura Wattenberg在她自己的網(wǎng)站上指出(:近百年來,男孩名字在后一個字母上的分布發(fā)生了顯著的變化。
首先將全部出生數(shù)據(jù)在年度、性別以及末字母上進行了聚合。
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')

按總出生數(shù)對該表進行規(guī)范化處理,以便計算出各性別各末字母占總出生人數(shù)的比例
letter_prop = subtable / subtable.sum()

生成一張各年度各性別的條形圖
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)

可以看出,從20世紀60年代開始,以字母”n”結(jié)尾的男孩名字出現(xiàn)了顯著的增長。
回到之前創(chuàng)建的那個完整表,按年度和性別對其進行規(guī)范化處理,并在男孩名字中選取幾個字母,后進行轉(zhuǎn)置以便將各個列做成一個時間序列。
letter_prop = table / table.sum()
dny_ts = letter_prop.loc[['d', 'n', 'y'], 'M'].T

通過其plot方法繪制出一張趨勢圖

變成女孩名字的男孩名字
另一個有趣的趨勢是,早年流行于男孩的名字近年來“變性了”,例如Lesley或Leslie?;氐?top1000數(shù)據(jù)集,找出其中以”lesl”開頭的一組名字。
all_names = pd.Series(top1000.name.unique())
lesley_like = all_names[all_names.str.lower().str.contains('lesl')]

利用這個結(jié)果過濾其他的名字,并按名字分組計算出生數(shù)以查看相對頻率
filtered = top1000[top1000.name.isin(lesley_like)]
filtered.groupby('name').births.sum()

按性別和年度進行聚合,并按年度進行規(guī)范化處理
table = filtered.pivot_table('births', index='year',columns='sex', aggfunc='sum')
table = table.div(table.sum(1), axis=0)

繪制一張分性別的年度曲線圖了
