美文网首页呆鸟的Python数据分析
pandas分析-----pandas聚合分析居然还可以这么用

pandas分析-----pandas聚合分析居然还可以这么用

作者: 九日照林 | 来源:发表于2019-08-24 12:01 被阅读0次

    数据聚合与分组操作

    pandas的聚合与分组操作是日常进行数据分析要用到的常用操作,相信很多人都很熟悉了。但是聚合分析能做到的远不止只是简单地计算组内的平均值,中位数等统计学数值。groupby可以对组内执行其他变换,比如计算分位数的排名,标准化,线性回归,排位和子集选择,计算分位数分析和区间分析等等,只要你能想到的,返回pandas对象或者标量的操作都可以。

    pandas聚合分析的原理

    解析

    pandas执行groupby操作以后就会按照键进行分组,从上面那个图可以看到,执行按照key字段进行分组后,相同的key就会分到一组,这个时候组内进行某个聚合函数aggfunc的操作后再将得到的结果纵向拼接到一起,得到最后的结果。因此,明白了groupby的过程之后,我们大概也就明白,自定义的函数只要是能够对一个dataframe进行操作,并且返回一个pandas对象或者一个标量的,都是可以用这样一个函数的。

    df = pd.DataFrame({'key': ['a', 'b', 'c'] * 4,
                       'value': numpy.arange(12.)})
    df
    
    g=df.groupby('key')
    

    很多人不知道的是,pandas聚合后的对象其实是一个键值对的对象。

    # 我们试一下
    for i, v in g:
        print(i, v)
    
    a   key  value  value_1
    0   a    0.0       44
    3   a    3.0       15
    6   a    6.0       20
    9   a    9.0        6
    b    key  value  value_1
    1    b    1.0        4
    4    b    4.0       29
    7    b    7.0       10
    10   b   10.0       40
    c    key  value  value_1
    2    c    2.0       12
    5    c    5.0        7
    8    c    8.0       38
    11   c   11.0        0
    

    a, bc分别是各个分组的分组名,对应的值是各个分组的dataframe

    自定义一个函数进行组内和组间的计算

    # 新建一个dataframe
    df = pd.DataFrame({'key1' : ['a', 'a', 'b', 'b', 'a'],
                       'key2' : ['one', 'two', 'one', 'two', 'one'],
                       'data1' : numpy.random.randn(5),
                       'data2' : numpy.random.randn(5)})
    df
    

    我们新建一个函数用作后面对group对象进行的计算。

    def get_result(group):
        return (group['data1'].sum()-group['data2'].mean())/group['data1'].sum()
    

    按照key1进行分组。

    df.groupby('key1').apply(get_result)
    

    如果apply里面的函数本身就有聚合操作返回一个标量的话,那么聚合应用该函数后返回的就是具体的值,而不是扩展到所有行,比如上面的sum是在轴方向进行加和,也就是组内进行操作,而不是单个操作,因此apply后返回的是数值。

    如果我们要做的计算是对分组内每个单元格进行的,那么返回的就是跟原本的dataframe一样大小的。

    def get_result_single(group):
        return (group['data1']-group['data2'].mean())/group['data1'].sum()
        
        
    df.groupby('key1').apply(get_result_single)
    

    在apply中传入自定义的函数的参数

    很多时候我们总是习惯在group后对某一列进行操作,但很多时候我们忘了,其实我们可以直接对group后的对象进行一个复杂函数操作,这个时候建议先把函数写好,后面再调用,且记得apply里面可以传入该函数的参数

    tips = pd.read_csv('examples/tips.csv')
    tips.head(3)
    

    在这里我们尝试按照smoker这一列进行分组,并且排序找出分组后的total_bill前5位最高的记录,为了更加熟悉apply该函数可以传入自定义函数的参数的这个功能,我们自定义函数可以设定返回第几位降序或者逆序
    def top(group, col, n=5, ascending=True):
        return group.sort_values(by=col,ascending=ascending)[:n]
        
    tips.groupby('smoker').apply(top, col='total_bill', n=5, ascending=True)
    

    当然,根据自定义函数,我们还可以修改不同的参数,这样构建好一个函数,我们可以重复调用,这样既提高了效率,也增加了代码的可读性。

    tips.groupby('smoker').apply(top, col='tip', n=3, ascending=False)
    

    以上我们就按照组内的tip列进行降序排列,并筛选出前3行。

    分组内自定义填充缺失值

    我们有很多时候需要根据分组后不同的组别进行填充相应的值,在这里我们可以用自定义函数字典的方式进行自定义。
    我们把之前的df的2, 4行改成缺失值,我们尝试用分组内的平均值来对应填充,使用fillna方法

    df.iloc[[1,3],[-2,-1]]=numpy.nan
    df
    
    def fill_mean(group):
        return group.fillna(df.mean())
    
    df.groupby('key1').apply(fill_mean)
    

    以上我们填充的是组内的平均数,也就是调用了mean()方法,但是如果我们要填充任意自定义的值的话,需要指定分组名填充值的字典,然后用group对象name属性去字典取出对应的值。

    fill_val={'a':-1, 'b':12}
    fill_med=lambda g:g.fillna(fill_val[g.name])
    df.groupby('key1').apply(fill_med)
    

    [站外图片上传中...(image-959b14-1566618910459)]

    g.name相当于是分组后的索引的分组名,然后我们在字典中根据分组名去填充对应的值。

    分箱后进行聚合

    使用pd.cutpd.qcut后,得到的是一个categorical对象,这个对象就像是一个列表或者series,可以传入给group对象后面进行分组的依据。

    tips.head()
    

    如果我们尝试对total_bill进行分箱,然后分组

    bins=pd.cut(tips.total_bill,bins=10)#返回一个series,这个series可以传入到groupby当中作为一个分组的依据
    tips.groupby(bins).agg({'tip':'mean'})
    

    按照字典或者列表来聚合

    people = pd.DataFrame(numpy.random.randn(5, 5),
                          columns=['a', 'b', 'c', 'd', 'e'],
                          index=['Joe', 'Steve', 'Wes', 'Jim', 'Travis'])
    people.iloc[2:3, [1, 2]] = numpy.nan # Add a few NA values
    people
    

    我们还可以传入一个字典,字典的名对应列名或者索引名,值代表新的分类,按照新的分类进行聚合。

    mapping={'a':'good', 'b':'good', 'c':'bad', 'd':'good', 'e':'bad'}
    people.groupby(mapping, axis=1).agg('mean')
    

    当然我们也可以传入列表作为聚合的依据,类似于前面的categorical操作,需要注意的是,列表的长度需要和长度或者宽度一致。比如我们这里有5行,我们按照行方向进行聚合的话,创建一个5个元素的列表,按照这个列表里面的值进行聚合。

    label=['west']*3+['east']*2
    label
    
    people.groupby(label).agg({'a':'mean'})
    

    两个例子

    聚合后计算相关性

    如果有这种情况,我们希望能够分组,并且分组后计算其他列与某列的相关性,比如在下面,我们想计算其他变量total_billsizetip的相关性大小,我们可以考虑下怎么做。

    tips.head()
    

    通常来说,seriesseries的单个相关性可以用series的内置方法corr来计算,如下:
    tips['total_bill'].corr(tips['tip'])
    

    dataframeseries的单个相关性可以用dataframe的内置方法corrwith来计算。

    tips[['total_bill', 'tip']].corrwith(tips['size'])
    

    现在加上分组

    tips.groupby('smoker').apply(lambda x:x[['total_bill', 'tip']].corrwith(x['size']))
    

    聚合后计算线性回归

    正如之前所说,只要是能够返回pandas对象的函数都是可以用到apply函数中的,statsmodels的线性回归模块也不例外,返回的结果的params参数就是一个series

    import statsmodels.api as sm
    
    def regression(group, x_val, y_val):
        x=group[x_val]
        x['intercept']=1
        Y=group[y_val]
        model=sm.OLS(Y,x)
        results=model.fit()
        return results.params
    
    tips.groupby('smoker').apply(regression, x_val=['total_bill'], y_val='tip')
    

    在列方向进行聚合

    我们还可以在列方向进行聚合,如果是多层索引,指定索引名,按照对应索引名进行聚合。

    columns = pd.MultiIndex.from_arrays([['US', 'US', 'US', 'JUPYTER NOTEBOOK', 'JUPYTER NOTEBOOK'],
                                        [1, 3, 5, 1, 3]],
                                        names=['cty', 'tenor'])
    hier_df = pd.DataFrame(numpy.random.randn(4, 5), columns=columns)
    hier_df
    
    hier_df.groupby(level='cty',axis=1).mean()
    
    hier_df.groupby(level='tenor',axis=1).mean()
    

    总结

    虽然以上似乎介绍了很多pandas聚合操作的用法,但我细细想起来,其实是再简单不过的操作罢了,关于想要玩好pandas的聚合操作,有这么以下几点记住即可。

    1. pandas分组后的是有name属性的分组对象,聚合函数是对各个分组的dataframe进行操作的,只要是能够返回一个pandas对象或者标量的函数都是可以应用到groupby以后的聚合里面的。
    2. pandas传入groupby里面的参数可以是series, list, dictcategorical。前面都比较好理解,categorical是pandas的另外一种类型,目前主要应用在分桶上。
    3. pandas既可以按照组内的值进行聚合,也可以按照多层索引的层级进行聚合,传入的参数是level='level_name';既可以在行方向进行聚合(默认),也可以在列方向进行聚合(传入axis=1)的操作。
    4. 对于一个复杂一些的函数,可以先把函数和相关的参数写好,建议把常用的函数直接保存到剪贴板增强工具里面,后面再进行重复的调用,这样效率很高,而且代码很整洁。

    只要理解上面这么几点,基本上都可以把groupby用得比较遛了。个人到现在觉得,之所以要把各种数据分析和数据挖掘的函数用法钻研得深透,并不是为了geek而geek,相反,是为了更好更快地探索数据,需要用的时候几行代码就实现,丝毫察觉不到这个过程实现的曲折,不需要使用的时候再疯狂去谷歌,这样效率和体验都很差。

    相关文章

      网友评论

        本文标题:pandas分析-----pandas聚合分析居然还可以这么用

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