Pandas学习笔记七——累计与分组

累计与分组

在对比较大的数据进行分析时,一项基本的工作就是有效的数据累计(summarization):计算累计(aggragation)指标,如sum()、mean()、median()、min()和max(),其中每一项指标都呈现出大数据集的特征。

一、简单的累计计算

简单的累计计算是对DataFrame或者Series对象使用累计函数:sum()、mean()、median()、min()和max()等。设数据集为df,那么简单的累计计算语法为df.agg_func(),pandas为Dataframe提供了describe()方法可以一次性计算出每一列上的若干常用统计值。

#数据:Seaborn数据库中的行星数据
import pandas as pd
import seaborn as sns
import numpy as np
planets = sns.load_dataset('planets')
planets.describe()

Out[79]: 
            number  orbital_period     ...          distance         year
count  1035.000000      992.000000     ...        808.000000  1035.000000
mean      1.785507     2002.917596     ...        264.069282  2009.070531
std       1.240976    26014.728304     ...        733.116493     3.972567
min       1.000000        0.090706     ...          1.350000  1989.000000
25%       1.000000        5.442540     ...         32.560000  2007.000000
50%       1.000000       39.979500     ...         55.250000  2010.000000
75%       2.000000      526.005000     ...        178.500000  2012.000000
max       7.000000   730000.000000     ...       8500.000000  2014.000000

[8 rows x 5 columns]

这些简单的累计方法默认都是对列进行统计,如果想要统计行的数据的特性,可以加上参数axis=1

#在列的方向上聚合
df.agg_func(axis=1)

二、高阶的累计方法:GroupBy

GroupBy简单来看就是Pandas为DataFrame提供的一个分组的工具,通过df.groupby('key')可以得到一个DataFrameGroupBy对象,你可以将它看作是一种特殊形式的DataFrame,里面隐藏着若干组数据,但是在没有应用聚合操作之前不会计算。这种“延迟计算”的方法使得大多数常见的累计计算操作可以通过一种对用户而言几乎透明的方式非常高效的实现。

GroupBy的过程如下:

  • 分割:将DataFrame按照指定的键分割成若干组
  • 应用:对每个组应用聚合函数,通常是累计、转换或过滤函数
  • 组合:将每个组的结果合并成一个输出数组
DataFrameGroupBy对象的操作
  • 对分组后指定列的数据使用简单累计函数

    
    #对数据集在method列上进行分组,然后求各组中行星轨道周期的中位数
    
    planets.groupby('method')['orbital_period'].median()
    
    
    #输出结果:
    
    method
    Astrometry                         631.180000
    Eclipse Timing Variations         4343.500000
    Imaging                          27500.000000
    Microlensing                      3300.000000
    Orbital Brightness Modulation        0.342887
    Pulsar Timing                       66.541900
    Pulsation Timing Variations       1170.000000
    Radial Velocity                    360.200000
    Transit                              5.714932
    Transit Timing Variations           57.011000
    Name: orbital_period, dtype: float64
  • 按组迭代

    
    #获取分组后各组的信息
    
    for (method, group) in planets.groupby('method'):
      print("{0:30s} shape={1}".format(method, group.shape))
    
    
    #输出结果:
    
    Astrometry                     shape=(2, 6)
    Eclipse Timing Variations      shape=(9, 6)
    Imaging                        shape=(38, 6)
    Microlensing                   shape=(23, 6)
    Orbital Brightness Modulation  shape=(3, 6)
    Pulsar Timing                  shape=(5, 6)
    Pulsation Timing Variations    shape=(1, 6)
    Radial Velocity                shape=(553, 6)
    Transit                        shape=(397, 6)
    Transit Timing Variations      shape=(4, 6)
  • 调用方法

    借用python类的魔力(@classmethod),可以让GroupBy对象不直接实现的方法应用到每一组,比如你可以对每组数据都调用DataFrame对象才能使用的describe()方法。

    planets.groupby('method')['year'].describe()
    
    
    #输出结果:
    
                                 count         mean   ...        75%     max
    method                                              ...                   
    Astrometry                       2.0  2011.500000   ...    2012.25  2013.0
    Eclipse Timing Variations        9.0  2010.000000   ...    2011.00  2012.0
    Imaging                         38.0  2009.131579   ...    2011.00  2013.0
    Microlensing                    23.0  2009.782609   ...    2012.00  2013.0
    Orbital Brightness Modulation    3.0  2011.666667   ...    2012.00  2013.0
    Pulsar Timing                    5.0  1998.400000   ...    2003.00  2011.0
    Pulsation Timing Variations      1.0  2007.000000   ...    2007.00  2007.0
    Radial Velocity                553.0  2007.518987   ...    2011.00  2014.0
    Transit                        397.0  2011.236776   ...    2013.00  2014.0
    Transit Timing Variations        4.0  2012.500000   ...    2013.25  2014.0
    
    [10 rows x 8 columns]
GroupBy对象的高级方法
  • 累计(aggregate)

    GroupBy对象的aggregate()方法可以支持复杂的累计操作,比如字符串、函数或者函数列表,并且能一次性计算出所有累计值。

    
    #数据
    
    df = pd.DataFrame({'key':['A','B','C','A','B','C'],
                     'data1':range(6),
                     'data2':np.random.randint(0,10,6)},
                    columns = ['key', 'data1', 'data2'])
    
    
    #aggregate()函数的参数可以支持字符串、函数或者函数列表
    
    df.groupby('key').aggregate(['min', np.median, max])
    
    
    #输出结果:
    
      data1            data2           
        min median max   min median max
    key                                  
    A       0    1.5   3     3    5.5   8
    B       1    2.5   4     7    7.5   8
    C       2    3.5   5     0    2.0   4
  • 过滤(filter)

    过滤操作可以让你按照分组的属性丢弃若干数据,例如,我们可能只需要保留标准差超过某个阀值的组:

    
    #定义过滤函数
    
    def filter_func(x):
      return x['data2'].std()>2
    
    #假如某组的经过过滤函数后的返回值为False的话就将其过滤
    
    df.groupby('key').filter(filter_func)
    
    
    #输出结果
    
    key  data1  data2
    0   A      0      8
    2   C      2      0
    3   A      3      3
    5   C      5      4
  • 转换

    累计操作返回的是对组内数据缩减过得结果,而转换操作会返回一个新的全量数据。常见的操作是将每一组的样本数据减去各组的均值,实现数据标准化

    df.groupby('key').transform(lambda x:x-x.mean())
    
    
    #输出结果:
    
    Out[101]: 
     data1  data2
    0   -1.5    2.5
    1   -1.5    0.5
    2   -1.5   -2.0
    3    1.5   -2.5
    4    1.5   -0.5
    5    1.5    2.0
  • apply()方法

    apply()方法让你可以在每个组上那个应用任意方法。这个函数输入一个DataFrame,返回一个Pandas对象(DataFrame或者Series)或一个标量(Scalar,单个数值)。组合操作会适应返回结果。

    
    #将第一列数据以第二列的和为基数进行标准化
    
    def norm_by_data2(x):
      #x是一个分组数据的DataFrame
      x['data1'] /= x['data2'].sum()
      return x
    df.groupby('key').apply(norm_by_data2)
    
    
    #输出结果:
    
    Out[102]: 
    key     data1  data2
    0   A  0.000000      8
    1   B  0.066667      8
    2   C  0.500000      0
    3   A  0.272727      3
    4   B  0.266667      7
    5   C  1.250000      4
设置分割的键

分组的键除了DataFrame的列之外,还有需要其他的方法。

  • 将列表、数组、Series或索引作为分组键

    
    #将列表作为分组键,下面列表中,所有0、1、2对应的索引所在行将被分到一组
    
    L = [0, 1, 0, 1, 2, 0]
    df.groupby(L).sum()
    
    
    #输出结果:
    
    Out[103]: 
     data1  data2
    0      7     12
    1      4     11
    2      4      7
    
    #对列的内容处理后作为分组键,下面代码目的是获取不同方法和不同年份发现的行星数量
    
    decade = 10 * (planets['year'] // 10)
    decade = decade.astype(str) + 's'
    decade.name = 'decade'
    planets.groupby(['method', decade])['number'].sum().unstack().fillna(0)
    
    
    #输出结果:
    
    decade                         1980s  1990s  2000s  2010s
    method                                                   
    Astrometry                       0.0    0.0    0.0    2.0
    Eclipse Timing Variations        0.0    0.0    5.0   10.0
    Imaging                          0.0    0.0   29.0   21.0
    Microlensing                     0.0    0.0   12.0   15.0
    Orbital Brightness Modulation    0.0    0.0    0.0    5.0
    Pulsar Timing                    0.0    9.0    1.0    1.0
    Pulsation Timing Variations      0.0    0.0    1.0    0.0
    Radial Velocity                  1.0   52.0  475.0  424.0
    Transit                          0.0    0.0   64.0  712.0
    Transit Timing Variations        0.0    0.0    0.0    9.0
  • 用字典或Series将索引映射到分组名称

    
    #将索引映射到分组键
    
    df2 = df.set_index('key')
    mapping = {'A':'vowel', 'B':'consonant', 'C':'consonant'}
    df2.groupby(mapping).sum()
    
    
    #输出结果:
    
             data1  data2
    consonant     12     19
    vowel          3     11
  • 任意的python函数

    
    #与前面的字典映射类似,你可以将任意python函数传入groupby,函数映射到索引,然后新的分组输出
    
    df2.groupby(str.lower).mean()
    
    
    #输出结果:
    
     data1  data2
    a    1.5    5.5
    b    2.5    7.5
    c    3.5    2.0
  • 多个有效键构成的列表

    
    #任意之前的有效的键可以组合起来进行分组,从而返回一个多级索引的分组结果
    
    df2.groupby([str.lower, mapping]).mean()
    
    
    #输出结果:
    
               data1  data2
    a vowel        1.5    5.5
    b consonant    2.5    7.5
    c consonant    3.5    2.0

猜你喜欢

转载自blog.csdn.net/jasonzhoujx/article/details/81672237