肥乡企业做网站推广,百度推广手机网站检测,快速优化网站建设,apache网站拒绝访问文章目录 14.3 US Baby Names 1880–2010#xff08;1880年至2010年美国婴儿姓名#xff09;1 Analyzing Naming Trends#xff08;分析命名趋势#xff09;评价命名多样性的增长“最后一个字母”的变革变成女孩名字的男孩名字#xff08;以及相反的情况#xff09; 14.3… 文章目录 14.3 US Baby Names 1880–20101880年至2010年美国婴儿姓名1 Analyzing Naming Trends分析命名趋势评价命名多样性的增长“最后一个字母”的变革变成女孩名字的男孩名字以及相反的情况 14.3 US Baby Names 1880–20101880年至2010年美国婴儿姓名
这个数据是从1880年到2010年婴儿名字频率数据。我们先看一下这个数据长什么样子
个数据集可以用来做很多事例如
计算指定名字的年度比例计算某个名字的相对排名计算各年度最流行的名字以及增长或减少最快的名字分析名字趋势元音、辅音、长度、总体多样性、拼写变化、首尾字母等分析外源性趋势圣经中的名字、名人、人口结构变化等
之后的教程会涉及到其中一些。另外可以去官网直接下载姓名数据Popular Baby Names。
下载National data之后会得到names.zip文件解压后可以看到一系列类似于yob1880.txt这样名字的文件说明这些文件是按年份记录的。这里使用Unix head命令查看一下文件的前10行
!head -n 10 ../datasets/babynames/yob1880.txt由于这是一个非常标准的以逗号隔开的格式即CSV文件所以可以用pandas.read_csv将其加载到DataFrame中
import pandas as pd# Make display smaller
pd.options.display.max_rows 10names1880 pd.read_csv(../datasets/babynames/yob1880.txt, names[names, sex, births])names1880namessexbirths0MaryF70651AnnaF26042EmmaF20033ElizabethF19394MinnieF1746............1995WoodieM51996WorthyM51997WrightM51998YorkM51999ZachariahM5
2000 rows × 3 columns
这些文件中仅含有当年出现超过5次以上的名字。为了简单化我们可以用births列的sex分组小计表示该年度的births总计
names1880.groupby(sex).births.sum()sex
F 90993
M 110493
Name: births, dtype: int64由于该数据集按年度被分割成了多个文件所以第一件事情就是要将所有数据都组装到一个DataFrame里面并加上一个year字段。使用pandas.concat可以做到
# 2010是最后一个有效统计年度
years range(1880, 2011)pieces []
columns [name, sex, births]for year in years:path ../datasets/babynames/yob%d.txt % yearframe pd.read_csv(path, namescolumns)frame[year] yearpieces.append(frame)# 将所有数据整合到单个DataFrame中
names pd.concat(pieces, ignore_indexTrue)这里要注意几件事。
第一concat默认是按行将多个DataFrame组合到一起的第二必须指定ignore_indexTrue因为我们不希望保留read_csv所返回的原始索引。
现在我们得到了一个非常大的DataFrame它含有全部的名字数据。现在names这个DataFrame看上去是
namesnamesexbirthsyear0MaryF706518801AnnaF260418802EmmaF200318803ElizabethF193918804MinnieF17461880...............1690779ZymaireM520101690780ZyonneM520101690781ZyquariusM520101690782ZyranM520101690783ZzyzxM52010
1690784 rows × 4 columns
有了这些数据后我们就可以利用groupby或pivot_table在year和sex界别上对其进行聚合了
total_births names.pivot_table(births, indexyear,columnssex, aggfuncsum)total_births.tail()sexFMyear200618964682050234200719168882069242200818836452032310200918276431973359201017590101898382
import seaborn as sns
%matplotlib inlinetotal_births.plot(titleTotal births by sex and year, figsize(15, 8))下面我们来插入一个prop列用于存放指定名字的婴儿数相对于总出生数的比列。prop值为0.02表示每100名婴儿中有2名取了当前这个名字。因此我们先按year和sex分组然后再将新列加到各个分组上
def add_prop(group): group[prop] group.births / group.births.sum()return group
names names.groupby([year, sex]).apply(add_prop)namesnamesexbirthsyearprop0MaryF706518800.0776431AnnaF260418800.0286182EmmaF200318800.0220133ElizabethF193918800.0213094MinnieF174618800.019188..................1690779ZymaireM520100.0000031690780ZyonneM520100.0000031690781ZyquariusM520100.0000031690782ZyranM520100.0000031690783ZzyzxM520100.000003
1690784 rows × 5 columns
在执行这样的分组处理时一般都应该做一些有效性检查sanity check比如验证所有分组的prop的综合是否为1。由于这是一个浮点型数据所以我们应该用np.allclose来检查这个分组总计值是否够近似于可能不会精确等于1
names.groupby([year, sex]).prop.sum()year sex
1880 F 1.0M 1.0
1881 F 1.0M 1.0
1882 F 1.0...
2008 M 1.0
2009 F 1.0M 1.0
2010 F 1.0M 1.0
Name: prop, Length: 262, dtype: float64这样就算完活了。为了便于实现进一步的分析我们需要取出该数据的一个子集每对sex/year组合的前1000个名字。这又是一个分组操作
def get_top1000(group):return group.sort_values(bybirths, ascendingFalse)[:1000]grouped names.groupby([year, sex])
top1000 grouped.apply(get_top1000)# Drop the group index, not needed
top1000.reset_index(inplaceTrue, dropTrue)如果喜欢DIY的话也可以这样
pieces []
for year, group in names.groupby([year, sex]):pieces.append(group.sort_values(bybirths, ascendingFalse)[:1000])top1000 pd.concat(pieces, ignore_indexTrue)top1000namesexbirthsyearprop0MaryF706518800.0776431AnnaF260418800.0286182EmmaF200318800.0220133ElizabethF193918800.0213094MinnieF174618800.019188..................261872CamiloM19420100.000102261873DestinM19420100.000102261874JaquanM19420100.000102261875JaydanM19420100.000102261876MaxtonM19320100.000102
261877 rows × 5 columns
接下来针对这个top1000数据集我们就可以开始数据分析工作了
1 Analyzing Naming Trends分析命名趋势
有了完整的数据集和刚才生成的top1000数据集我们就可以开始分析各种命名趋势了。首先将前1000个名字分为男女两个部分
boys top1000[top1000.sexM]
girls top1000[top1000.sexF]这是两个简单的时间序列只需要稍作整理即可绘制出相应的图标比如每年叫做John和Mary的婴儿数。我们先生成一张按year和name统计的总出生数透视表
total_births top1000.pivot_table(births, indexyear, columnsname, aggfuncsum)total_birthsnameAadenAaliyahAaravAaronAarushAbAbagailAbbAbbeyAbbie...ZoaZoeZoeyZoieZolaZollieZonaZoraZulaZuriyear1880NaNNaNNaN102.0NaNNaNNaNNaNNaN71.0...8.023.0NaNNaN7.0NaN8.028.027.0NaN1881NaNNaNNaN94.0NaNNaNNaNNaNNaN81.0...NaN22.0NaNNaN10.0NaN9.021.027.0NaN1882NaNNaNNaN85.0NaNNaNNaNNaNNaN80.0...8.025.0NaNNaN9.0NaN17.032.021.0NaN1883NaNNaNNaN105.0NaNNaNNaNNaNNaN79.0...NaN23.0NaNNaN10.0NaN11.035.025.0NaN1884NaNNaNNaN97.0NaNNaNNaNNaNNaN98.0...13.031.0NaNNaN14.06.08.058.027.0NaN..................................................................2006NaN3737.0NaN8279.0NaNNaN297.0NaN404.0440.0...NaN5145.02839.0530.0NaNNaNNaNNaNNaNNaN2007NaN3941.0NaN8914.0NaNNaN313.0NaN349.0468.0...NaN4925.03028.0526.0NaNNaNNaNNaNNaNNaN2008955.04028.0219.08511.0NaNNaN317.0NaN344.0400.0...NaN4764.03438.0492.0NaNNaNNaNNaNNaNNaN20091265.04352.0270.07936.0NaNNaN296.0NaN307.0369.0...NaN5120.03981.0496.0NaNNaNNaNNaNNaNNaN2010448.04628.0438.07374.0226.0NaN277.0NaN295.0324.0...NaN6200.05164.0504.0NaNNaNNaNNaNNaN258.0
131 rows × 6868 columns
接下来使用DataFrame中的plot方法
total_births.info()class pandas.core.frame.DataFrame
Int64Index: 131 entries, 1880 to 2010
Columns: 6868 entries, Aaden to Zuri
dtypes: float64(6868)
memory usage: 6.9 MBsubset total_births[[John, Harry, Mary, Marilyn]]subset.plot(subplotsTrue, figsize(12, 10), gridFalse,titleNumber of births per year)array([matplotlib.axes._subplots.AxesSubplot object at 0x1132a4828,matplotlib.axes._subplots.AxesSubplot object at 0x116933080,matplotlib.axes._subplots.AxesSubplot object at 0x117d24710,matplotlib.axes._subplots.AxesSubplot object at 0x117d70b70], dtypeobject)评价命名多样性的增长
上图反应的降低情况可能意味着父母愿意给小孩起常见的名字越来越少。这个假设可以从数据中得到验证。一个办法是计算最流行的1000个名字所占的比例我们按year和sex进行聚合并绘图
import numpy as nptable top1000.pivot_table(prop, indexyear,columnssex, aggfuncsum)table.plot(titleSum of table1000.prop by year and sex,yticksnp.linspace(0, 1.2, 13), xticksrange(1880, 2020, 10),figsize(15, 8))从图中可以看出名字的多样性确实出现了增长前1000项的比例降低。另一个办法是计算占总出生人数前50%的不同名字的数量这个数字不太好计算。我们只考虑2010年男孩的名字
df boys[boys.year 2010]dfnamesexbirthsyearprop260877JacobM2187520100.011523260878EthanM1786620100.009411260879MichaelM1713320100.009025260880JaydenM1703020100.008971260881WilliamM1687020100.008887..................261872CamiloM19420100.000102261873DestinM19420100.000102261874JaquanM19420100.000102261875JaydanM19420100.000102261876MaxtonM19320100.000102
1000 rows × 5 columns
对prop降序排列后我们想知道前面多少个名字的人数加起来才够50%。虽然编写一个for循环也能达到目的但NumPy有一种更聪明的矢量方式。先计算prop的累计和cumsum然后再通过searchsorted方法找出0.5应该被插入在哪个位置才能保证不破坏顺序
prop_cumsum df.sort_values(byprop, ascendingFalse).prop.cumsum()prop_cumsum[:10]260877 0.011523
260878 0.020934
260879 0.029959
260880 0.038930
260881 0.047817
260882 0.056579
260883 0.065155
260884 0.073414
260885 0.081528
260886 0.089621
Name: prop, dtype: float64prop_cumsum.searchsorted(0.5)array([116])由于数组索引是从0开始的因此我们要给这个结果加1即最终结果为117。拿1900年的数据来做个比较这个数字要小得多
df boys[boys.year 1900]
in1900 df.sort_values(byprop, ascendingFalse).prop.cumsum()
in1900[-10:]41853 0.979223
41852 0.979277
41851 0.979330
41850 0.979383
41849 0.979436
41848 0.979489
41847 0.979542
41846 0.979595
41845 0.979648
41876 0.979702
Name: prop, dtype: float64in1900.searchsorted(0.5) 1array([25])现在就可以对所有year/sex组合执行这个计算了。按这两个字段进行groupby处理然后用一个函数计算各分组的这个值
def get_quantile_count(group, q0.5):group group.sort_values(byprop, ascendingFalse)return group.prop.cumsum().searchsorted(q) 1diversity top1000.groupby([year, sex]).apply(get_quantile_count)
diversity diversity.unstack(sex)现在这个diversity有两个时间序列每个性别各一个按年度索引。通过IPython可以看到其内容还可以绘制图标
diversity.head()sexFMyear1880[38][14]1881[38][14]1882[38][15]1883[39][15]1884[39][16]
可以看到上面表格中的值为list如果不加diversitydiversity.astype(float)的话会报错显示“no numeric data to plot” error。通过加上这句来更改数据类型就能正常绘图了
diversity diversity.astype(float)
diversitysexFMyear188038.014.0188138.014.0188238.015.0188339.015.0188439.016.0.........2006209.099.02007223.0103.02008234.0109.02009241.0114.02010246.0117.0
131 rows × 2 columns
diversity.plot(titleNumber of popular names in top 50%, figsize(15, 8))从图中可以看出女孩名字的多样性总是比男孩高而且还变得越来越高。我们可以自己分析一下具体是什么在驱动这个多样性比如拼写形式的变化。
“最后一个字母”的变革
一位研究人员指出近百年来男孩名字在最后一个字母上的分布发生了显著的变化。为了了解具体的情况我们首先将全部出生数据在年度、性别以及末字母上进行了聚合
# 从name列中取出最后一个字母
get_last_letter lambda x: x[-1]
last_letters names.name.map(get_last_letter)
last_letters.name last_lettertable names.pivot_table(births, indexlast_letters,columns[sex, year], aggfuncsum)print(type(last_letters))
print(last_letters[:5])class pandas.core.series.Series
0 y
1 a
2 a
3 h
4 e
Name: last_letter, dtype: object然后我们选出具有一个代表性的三年并输出前几行
subtable table.reindex(columns[1910, 1960, 2010], levelyear)
subtable.head()sexFMyear191019602010191019602010last_lettera108376.0691247.0670605.0977.05204.028438.0bNaN694.0450.0411.03912.038859.0c5.049.0946.0482.015476.023125.0d6750.03729.02607.022111.0262112.044398.0e133569.0435013.0313833.028655.0178823.0129012.0
接下来我们需要安总出生数对该表进行规范化处理一遍计算出个性别各末字母站总出生人数的比例
subtable.sum()sex year
F 1910 396416.01960 2022062.02010 1759010.0
M 1910 194198.01960 2132588.02010 1898382.0
dtype: float64letter_prop subtable / subtable.sum()
letter_propsexFMyear191019602010191019602010last_lettera0.2733900.3418530.3812400.0050310.0024400.014980bNaN0.0003430.0002560.0021160.0018340.020470c0.0000130.0000240.0005380.0024820.0072570.012181d0.0170280.0018440.0014820.1138580.1229080.023387e0.3369410.2151330.1784150.1475560.0838530.067959.....................vNaN0.0000600.0001170.0001130.0000370.001434w0.0000200.0000310.0011820.0063290.0077110.016148x0.0000150.0000370.0007270.0039650.0018510.008614y0.1109720.1525690.1168280.0773490.1609870.058168z0.0024390.0006590.0007040.0001700.0001840.001831
26 rows × 6 columns
有了这个字母比例数据后就可以生成一张各年度各性别的条形图了
import matplotlib.pyplot as pltfig, axes plt.subplots(2, 1, figsize(10, 8))
letter_prop[M].plot(kindbar, rot0, axaxes[0], titleMale)
letter_prop[F].plot(kindbar, rot0, axaxes[1], titleFemal, legendFalse)从上图可以看出来从20世纪60年代开始以字母n结尾的男孩名字出现了显著的增长。回到之前创建的那个完整表按年度和性别对其进行规范化处理并在男孩名字中选取几个字母最后进行转置以便将各个列做成一个时间序列
letter_prop table / table.sum()
letter_prop.head()sexF...Myear1880188118821883188418851886188718881889...2001200220032004200520062007200820092010last_lettera0.3455870.3434400.3387640.3412510.3385500.3412700.3397030.3352580.3327640.328706...0.0201620.0200190.0191770.0195050.0184810.0176350.0167470.0161890.0159270.014980bNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN...0.0262560.0254180.0243680.0231710.0216450.0207780.0203570.0196550.0196930.020470cNaNNaN0.0000460.000045NaNNaNNaNNaNNaNNaN...0.0139720.0140480.0140420.0135140.0130830.0129910.0129830.0124580.0121860.012181d0.0066930.0066010.0068060.0072110.0071000.0064780.0069670.0070350.0072660.007703...0.0313520.0287940.0270690.0261180.0254200.0250750.0244510.0235740.0233980.023387e0.3668190.3706160.3745820.3731590.3727220.3728960.3728020.3723240.3736750.373736...0.0749270.0746030.0733960.0717100.0707990.0697480.0694450.0693620.0686630.067959
5 rows × 262 columns
dny_ts letter_prop.loc[[d, n, y], M].T
dny_ts.head()last_letterdnyyear18800.0830550.1532130.07576018810.0832470.1532140.07745118820.0853400.1495600.07753718830.0840660.1516460.07914418840.0861200.1499150.080405
有了这个时间序列的DataFrame后就可以通过其plot方法绘制出一张趋势图
dny_ts.plot(figsize(10, 8))变成女孩名字的男孩名字以及相反的情况
另一个有趣的趋势是早年流行于男孩的名字近年来“变性了”列入Lesley或Leslie。回到top1000数据集找出其中以lesl开头的一组名字
all_names pd.Series(top1000.name.unique())
lesley_like all_names[all_names.str.lower().str.contains(lesl)]
lesley_like632 Leslie
2294 Lesley
4262 Leslee
4728 Lesli
6103 Lesly
dtype: object然后利用这个结果过滤其他的名字并按名字分组计算出生数以查看相对频率
filtered top1000[top1000.name.isin(lesley_like)]
filtered.groupby(name).births.sum()name
Leslee 1082
Lesley 35022
Lesli 929
Leslie 370429
Lesly 10067
Name: births, dtype: int64接下来我们按性别和年度进行聚合并按年度进行规范化处理
table filtered.pivot_table(births, indexyear,columnssex, aggfuncsum)table table.div(table.sum(1), axis0)tablesexFMyear18800.0919540.90804618810.1067960.89320418820.0656930.93430718830.0530300.94697018840.1071430.892857.........20061.000000NaN20071.000000NaN20081.000000NaN20091.000000NaN20101.000000NaN
131 rows × 2 columns
现在我们可以轻松绘制一张分性别的年度曲线图了
table.plot(style{M: k-, F: k--}, figsize(10, 8))matplotlib.axes._subplots.AxesSubplot at 0x11f0640b8