美文网首页numpy_pandas数据分析python自学大数据学习+数据库知识
数据分析案例(1880-2010年间全美婴儿姓名)

数据分析案例(1880-2010年间全美婴儿姓名)

作者: GHope | 来源:发表于2018-11-18 11:13 被阅读369次

    用UNIX的head命令查看了其中一个文件的前10行

    !head -n 10 datasets/babynames/yob1880.txt 
    

    不巧的Windows下无法执行

    !type -10 yob1880.txt 
    
    Windows下貌似只能查看全部
     names1880 = pd.read_csv('yob1880.txt', names=['name', 'sex', 'births'])
    
    加载1880的婴儿信息

    我们可以用births列的sex分组小计表示该年度的births总计

    names1880.groupby('sex').births.sum() 
    
    用births列的sex分组小计表示该年度的births总计

    由于该数据集按年度被分隔成了多个文件,所以第一件事情就是要将所有数据都组装到一个 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)
    
    多个文件中的数据使用for循环加载

    这里需要注意几件事情。第一,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)
    
    简单聚合

    绘制出生婴儿数量曲线统计

    total_births.plot(title='Total births by sex and year')
    
    绘制统计结果

    插入一个prop列,用于存放指定名字的婴儿数相对于总出生数的比例:

    def add_prop(group):
        group['prop'] = group.births / group.births.sum()
        return group 
    
    names = names.groupby(['year', 'sex']).apply(add_prop)
    
    指定名字的婴儿数相对于总出生数的比例

    在执行这样的分组处理时,一般都应该做一些有效性检查,比如验证所有分组的prop的总和是否为1

    names.groupby(['year', 'sex']).prop.sum() 
    
    有效性验证——比例和为一

    工作完成。为了便于实现更进一步的分析,我需要取出该数据的一个子集:每对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)
    
    创建每年每个性别的前一千条数据

    接下来的数据分析工作就针对这个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)
    

    我们用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))
    
    命名多样性的增长

    从图中可以看出,名字的多样性确实出现了增长(前1000项的比例降低)

    另一个办法是计算占总出生人数前50%的不同名字的数量,这个数字不太好计算。我们只考虑 2010年男孩的名字:

    考虑 2010年男孩

    在对prop降序排列之后,我们想知道前面多少个名字的人数加起来才够50%。虽然编写一个 for循环确实也能达到目的,但NumPy有一种更聪明的矢量方式。先计算prop的累计和 cumsum,然后再通过searchsorted方法找出0.5应该被插入在哪个位置才能保证不破坏顺序

    prop_cumsum = df.sort_values(by='prop', ascending=False).prop.cumsum()
    
    前面50%的人名个数

    由于数组索引是从0开始的,因此我们要给这个结果加1,即终结果为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在她自己的网站上指出(:近百年来,男孩名字在后一个字母上的分布发生了显著的变化。

    首先将全部出生数据在年度、性别以及末字母上进行了聚合。

    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')
    
    输出具有一定代表性前面几行

    按总出生数对该表进行规范化处理,以便计算出各性别各末字母占总出生人数的比例

    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”结尾的男孩名字出现了显著的增长。

    回到之前创建的那个完整表,按年度和性别对其进行规范化处理,并在男孩名字中选取几个字母,后进行转置以便将各个列做成一个时间序列。

    letter_prop = table / table.sum()
    
    dny_ts = letter_prop.loc[['d', 'n', 'y'], 'M'].T
    
    转置时间序列

    通过其plot方法绘制出一张趋势图


    绘制趋势图

    变成女孩名字的男孩名字

    另一个有趣的趋势是,早年流行于男孩的名字近年来“变性了”,例如Lesley或Leslie。回到 top1000数据集,找出其中以”lesl”开头的一组名字。

    all_names = pd.Series(top1000.name.unique())
    
    lesley_like = all_names[all_names.str.lower().str.contains('lesl')]
    
    以”lesl”开头的一组名字

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

    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)
    
    聚合性别和年度,进行规范化处理

    绘制一张分性别的年度曲线图了

    性别年度曲线图

    相关文章

      网友评论

      • db7361a5e4ef:大神啊,不明觉厉
        GHope:@亚素 其实很菜的👻
      • 先生的怀孕日记::+1::+1::+1:
        GHope:@先生的怀孕日记 🤓
      • 春木sky:这个数据说明了啥?
        GHope:@春木_简 每一张生成的数据表说明一种情况,总体而言就是在这个时间跨度中美国婴儿的命名整体呈多样化趋势,且女性的多样化趋势高于男性。最后一个比较恶搞,就是原来美国有很多叫蕾丝的男性,后来没有了,随之出现了很多叫蕾丝的女性。其实数据本身没什么实际意义,就是pandas的作者写的例子,我拿过来练练手👻👻👻
      • 乐健君:支持:+1:
        GHope:@乐健君 🤓
      • linwood:不明觉厉🙏🙏😂
        GHope:@linwood 🤓

      本文标题:数据分析案例(1880-2010年间全美婴儿姓名)

      本文链接:https://www.haomeiwen.com/subject/llgefqtx.html