zoukankan      html  css  js  c++  java
  • Pandas_分组与聚合

    # 分组统计是数据分析中的重要环节:
    
    # 1-数据分组:GroupBy的原理和使用方法;
    # 2-聚合运算:学会分组数据的聚合运算方法和函数使用; 类似于 SQL思想
    # 3-分组运算:重点 apply方法的使用
    # 4-数据透视表:学会构建数据透视表和交叉表
    
    # 一,GroupBy:
    # 步骤:split-apply-combine
    # 举例:小费 tips
    import pandas as pd
    import numpy as np
    import seaborn as sns
    from pandas import Series,DataFrame
    
    # tips=sns.load_dataset('tips')  # 从 github仓库里下载 https://github.com/mwaskom/seaborn-data
    tips=pd.read_csv('tips.csv')
    tips.head(20)
    
    total_bill tip sex smoker day time size
    0 16.99 1.01 Female No Sun Dinner 2
    1 10.34 1.66 Male No Sun Dinner 3
    2 21.01 3.50 Male No Sun Dinner 3
    3 23.68 3.31 Male No Sun Dinner 2
    4 24.59 3.61 Female No Sun Dinner 4
    5 25.29 4.71 Male No Sun Dinner 4
    6 8.77 2.00 Male No Sun Dinner 2
    7 26.88 3.12 Male No Sun Dinner 4
    8 15.04 1.96 Male No Sun Dinner 2
    9 14.78 3.23 Male No Sun Dinner 2
    10 10.27 1.71 Male No Sun Dinner 2
    11 35.26 5.00 Female No Sun Dinner 4
    12 15.42 1.57 Male No Sun Dinner 2
    13 18.43 3.00 Male No Sun Dinner 4
    14 14.83 3.02 Female No Sun Dinner 2
    15 21.58 3.92 Male No Sun Dinner 2
    16 10.33 1.67 Female No Sun Dinner 3
    17 16.29 3.71 Male No Sun Dinner 3
    18 16.97 3.50 Female No Sun Dinner 3
    19 20.65 3.35 Male No Sat Dinner 3
    # 将序列按照单个分组键分组,并聚合计算:
    # 1,groupby()可以被 Series调用,也可以被 DataFrame调用。
    grouped=tips['tip'].groupby(tips['sex'])  # 对 tip Series,按照 sex序列的值进行分组
    grouped=tips.groupby(tips['sex'])         # 对 tips DataFrame,按照 sex序列的值进行分组
    grouped  # <pandas.core.groupby.generic.SeriesGroupBy object at 0x0000026FF174BC08>  GroupBy对象 可迭代对象
    grouped.mean()  # 平均值  聚合运算
    grouped.sum()   # 求和
    # 将序列按照多个分组键分组,并聚合计算:
    data_mean=tips['tip'].groupby([tips['sex'],tips['time']]).mean()
    data_mean  # type(data_mean)  # 多层索引的序列 pandas.core.series.Series
    data_mean.plot(kind='barh')   # 男性晚餐时候小费给的平均值高
    # GroupBy对象是可迭代对象,其构造为一组二元元组:
    for name,group in tips['tip'].groupby(tips['sex']):
        print(name)
        print(group)
        
    # 可查看各分组的大小
    tips.groupby(tips['sex']).size()
    
    
    
    
    # 2,分组方式:
    
    # 2-1)按序列分组:
    grouped=tips['tip'].groupby(tips['sex'])  # 对 tip Series,按照 sex序列的值进行分组
    grouped=tips.groupby(tips['sex'])         # 对 tips DataFrame,按照 sex序列的值进行分组
    
    # 2-2)按列名分组:
    smoker_mean=tips.groupby('smoker').mean()
    smoker=tips.groupby('somker',group_keys=False)['tip']
    smoker_mean=tips.groupby(['sex','smoker']).mean()  # 按多个列名组合分组
    smoker_mean=tips.groupby(['sex','smoker'],as_index=False).mean()  # 按多个列名组合分组   as_index=False:不以分组键作为索引
    
    size_mean1=tips.groupby('size')['tip'].mean()  # 等效于:
    size_mean2=tips['tip'].groupby(tips['size']).mean()
    size_mean1==size_mean2  # 返回全是True的布尔数组
    
    # 2-3) 按行索引分组:
    df=DataFrame(np.arange(16).reshape(4,4),index=['a','b','a','b'])
    df.groupby(df.index).mean() 
    
    # 2-4)按列表或元组分组: # 相当于先给索引按照列表重命名,然后按行索引分组。
    df=DataFrame(np.arange(16).reshape(4,4))
    list1=['a','b','a','b']
    df.groupby(list1).mean() 
    
    # 2-5)按照字典分组:  # 相当于先给dataframe的索引重命名,再按新索引分组。
    # 当要分组的列或行索引的值不明确时,需要使用字典指定
    df=DataFrame(np.arange(16).reshape(4,4),index=['a','b','A','B'])
    dict1={
        'a':'one',
        'A':'one',
        'b':'two',
        'B':'two'
    }
    df.groupby(dict1).mean()
    
    # 2-6)按函数分组:
    # 原理类似于字典,通过映射关系进行分组,但更灵活。
    df=DataFrame(np.random.randn(4,4))
    df
    df.groupby(df[3].map(lambda x:'a' if x>=0 else 'b')).sum()  #看结果就明白了
    
    # 2-7) 对于层次化索引,按 level级别进行分组:
    df=DataFrame(np.arange(16).reshape(4,4),
                index=[['one','one','two','two'],['a','b','a','b']],
                columns=[['apple','apple','orange','orange'],['red','green','red','green']])
    df
    # df.groupby(level=1).sum()
    # df.groupby(level=1,axis=1).sum()
    # df.groupby(level=0).sum()
    df.groupby(level=0,axis=1).sum()
    
    
    
    
    # 3,聚合运算              注:空值不参与计算!  返回聚合后的序列
    # 对分组后的数据进行计算,产生标量值的转换过程叫聚合运算,上面的 mean(),sum()都是。
    
    # 3-1) 常用的聚合函数:
    # count       计数
    # sum         求和
    # mean        求平均值
    # median      求算术中位数
    # std         求标准差
    # var         求方差
    # min,max    求最小值,最大值
    # prod        求积
    # first,last  求第一个值,求最后一个值
    # quantile    分位数计算
    
    max_tip=tips.groupby('sex')['tip'].max()
    max_tip  # 序列
    max_tip.plot(kind='bar')
    
    # 3-2) 自定义聚合函数: grouped.aggregate(f) 或 grouped.agg(f)     aggregate--聚合
    # se.agg(f)与 se.map(f)的不同点在于:agg是Grupby对象的聚合函数,而 map是Series的矢量化函数。
    # 因而,agg里的函数 f一般也是由聚合函数组成的。
    
    def get_range(x):
        '''接收一个数字序列,或数字列表,得到数字的范围'''
        return x.max()-x.min()
    
    # tips_range=tips.groupby('sex')['tip'].agg(get_range)
    tips_range=tips.groupby('sex')['tip'].agg(lambda x:x.max()-x.min())
    tips_range
    
    
    # 4,多函数应用:
    # 4-1) 一列多函数  agg([f1,f2,f3...])
    # get_range=lambda x:x.max()-x.min()
    def get_range(x):
        '''接收一个数字序列,或数字列表,得到数字的范围'''
        return x.max()-x.min()
    
    tips.groupby(['sex','smoker'])['tip'].agg(['mean','std',get_range])  #自定义的函数不能使用'', 默认列名为函数名
    tips.groupby(['sex','smoker'])['tip'].agg([('tip_mean','mean'),('range',get_range)])  #指定列名
    
    # 4-2) 多列多函数:  产生列的层次化索引
    tips.groupby(['day','time'])['tip','total_bill'].agg([('tip_mean','mean'),('range',get_range)])  # 将来版本要用列表替代元组?
    
    
    # 4-3) 不同列,不同函数:   使用字典映射
    tips.groupby(['day','time'])['total_bill','tip'].agg({'total_bill':['sum','mean'],'tip':'mean'}) # 将要被废弃
    
    
    
    
    # 5, 分组运算: 
    
    # 运行下面代码,体会 transform 和 apply的不同效果:
    tips.groupby('sex').transform('mean')
    tips.groupby('sex')['tip'].transform('mean')
    tips.groupby('sex').apply(lambda x:x.mean()) 
    tips.groupby('sex')['tip'].apply(lambda x:x.mean())
           
    # 速记:
    tips.groupby('sex').transform('mean')                         # 只计算可以计算的列,比如3个可计算列,并返回新的 由3个列组成的 DataFrame
    tips.groupby('sex')['tip'].transform('mean')                  # 只计算 tip列,返回一个 Series.
    df.groupby('sex').apply(lambda x:x.fillna(x.mean()))          # 返回一个 对【所有数值列】缺失值填充后的 DataFrame  
    df.groupby('sex')['math'].apply(lambda x:x.fillna(x.mean()))  # 返回一个 只对【Math】列缺失值填充后的Series
    
    # grouped.perform(),grouped.apply(),都不改变原数据结构,如果想要做改变,可以赋值,比如:
    tips['tip_mean_by_sex']=tips.groupby('sex')['tip'].transform('mean')   # 增加一个新的列 tip_mean_by_sex
    
    
    # 5-1) tips.groupby('sex')['tip'].transform('mean').transform()方法:   返回
    # 对于小费数据集 tips,新建一列用于存放男性和女性小费的平均值。
    # 1)常用方法是,先分组聚合运算,再 merge按 sex键合并,出现一个新的列。
    tip_mean_by_sex=tips.groupby('sex')['tip'].mean()
    tip_mean_by_sex
    tip_mean_by_sex_df=DataFrame(tip_mean_by_sex)  # 注意,参数是 tips.groupby('sex')['tip'].mean() 不是 tips.groupby('sex')['tip']
    tip_mean_by_sex_df  # 此时行索引的 name为 sex,行索引的值为[Female,Male]
    
    new_tips=pd.merge(tips,tip_mean_by_sex_df,left_on='sex',right_index=True,suffixes=('','_mean_by_sex'),how='left')  # 指定left,tips的索引顺序就不会改变
    new_tips.head(10)
    
    # 2) 使用 transform('mean')方法将参数里的函数运算结果分布到每一行,非聚合函数,dataframe的矢量化函数:
    new_tips=tips.copy()
    new_tips['tip_mean_by_sex']=tips.groupby('sex')['tip'].transform('mean')   # 增加一个新的列 tip_mean_by_sex
    new_tips.head(10)
    
    
    # 5-2)df.groupby('sex').apply()方法:   返回DataFrame数据
    # 更加强大:
    # 计算根据性别分组后的小费金额前 5名的 DataFrame数据:默认结果以分组键作为行索引,可以用 groupby()里使用group_keys=False来改变:
    tips.groupby('sex').apply(lambda x:x.sort_values(by='tip',ascending=False)[:5])
    tips.groupby('sex',group_keys=False).apply(lambda x:x.sort_values(by='tip',ascending=False)[:5])
    # groupby()的 group_keys=False 参数只有在调用 apply()的时候好使。
    
    
    # 对缺失值的处理,前面数据处理那部分,有使用平均值进行填充缺失值的例子,如:
    data={
        'name':['张三','李四',np.nan,'王五','小明','马六'],
        'sex':['female','female','male','male','male','female'],
        'math':[67,77,np.nan,82,90,np.nan],
        'English':[67,77,np.nan,82,90,np.nan]
    }
    
    df=DataFrame(data)
    df.fillna(df['math'].mean())  # 使用 math列的平均值填充缺失值
    df.fillna(df['English'].mean())  # 使用 English列的平均值填充缺失值
    df.groupby('sex').apply(lambda x:x.fillna(x.mean()))  # 根据性别计算每个性别的 math和 english的平均数,来填充不同性别对应的缺失值.
    df.groupby('sex')['math'].apply(lambda x:x.fillna(x.mean()))  # 根据性别计算每个性别的 math的平均数,来填充不同性别对应的缺失值.
    
    
    
    
    
    # 6,数据透视表
    # 关于 Excel数据透视表的使用教程,请见: https://www.zhihu.com/question/24341252
    
    # df.pivot(values=要计算的列名, index=行名, columns=列名, aggfunc='sum', margins=True)  # 由 df调用
    # pd.cross_table(index=tips['day'],columns=tips['size'])                              # 由 pd调用,传入 df的两个整列作为行列索引
    
    
    # 6-1)透视表:
    # pandas里也有数据透视表功能,pivot_table 函数。
    tips.pivot_table(values='tip',index='sex',columns='smoker')                 # 默认计算 mean()
    tips.pivot_table(values='tip',index='sex',columns='smoker',aggfunc='sum')   # 默认计算 mean(), aggfunc='sum' 指定计算 sum
    tips.pivot_table(values='tip',index='sex',columns='smoker',aggfunc='sum',margins=True)   # margins=True 对其他列做小计
    
    # 用 groupby()来实现:
    tips.groupby(['sex','smoker'])['tip'].mean().unstack()  # 按多个列名组合分组  unstack()的默认参数为 1,表示操作内层行索引。
    tips.groupby(['sex','smoker'])['tip'].sum().unstack()   #  sum
    
    sex_smoker=tips.groupby(['sex','smoker'])['tip'].sum().unstack()   #  sum
    sex_smoker['All']=sex_smoker['No']+sex_smoker['Yes']    #增加一列
    
    sex_smoker=sex_smoker.append({'No':sex_smoker['No'].sum(),'Yes':sex_smoker['Yes'].sum()},ignore_index=True)  # 增加一行
    sex_smoker.index.name='Sex'   # 恢复行索引名称
    sex_smoker.index=['Female','Male','All']  # 恢复原来的行索引
    sex_smoker
    # 所以,使用透视表更简单些
    
    
    # 6-1)交叉表:
    # 用于计算分组频次与频率的特殊透视表
    cross_table=pd.cross_table(index=tips['day'],columns=tips['size'])  # 频次
    cross_table
    df=cross_table.div(cross_table.sum(axis=1),axis=0)                  # 频率   每行的和为1
    df
    # 频率频次堆积图:
    df.plot(kind='bar',stacked=True)
    
  • 相关阅读:
    Python3 tkinter基础 Radiobutton variable 默认选中的按钮
    oracle函数NVL,NVL2和NULLIF之间的区别和使用
    js如何返回两个数的商的整数和余数部分?
    解决win10打开组策略弹出管理模板对话框问题
    asp.net mvc中动作方法的重定向
    asp.net mvc如何获取url的相关信息
    vs2015如何使用附加进程调试发布在IIS上项目
    未能加载文件或程序集“Oracle.DataAccess”或它的某一个 依赖项。如何解决?
    C#中使用SqlBulkCopy的批量插入和OracleBulkCopy的批量插入
    oracle中计算两个日期的相差天数、月数、年数、小时数、分钟数、秒数等
  • 原文地址:https://www.cnblogs.com/Collin-pxy/p/13038468.html
Copyright © 2011-2022 走看看