# 累计和分组 对大数据进行分析的时候,一项基本工作就是数据累计(summarization),通常包括: - sum: 求和 - mean:平均数 - median:中位数 - min:最小值 - max:最大值 - count:计数 - first/last: 第一项和最后一项 - std: 标准差 - var:方差 - mad:均值绝对方差 - prod:所有项乘积 ```python # 准备数据 # 我们这次准备数据时候用seaborn提供的行星数据,包括天文学家观测到的围绕恒星运行的行星数据 # 下载地址: # https://github.com/mwaskom/seaborn-data # Github的原因,下载很慢,可能会出现传输链接错误,需要多试几遍才行 import seaborn as sns # Planets是行星观测数据,包括观测方法,数量,轨道周期等待 planets = sns.load_dataset('planets') print(planets.shape) # 显示数据头部 print(planets.head()) ``` (1035, 6) method number orbital_period mass distance year 0 Radial Velocity 1 269.300 7.10 77.40 2006 1 Radial Velocity 1 874.774 2.21 56.95 2008 2 Radial Velocity 1 763.000 2.60 19.84 2011 3 Radial Velocity 1 326.030 19.40 110.62 2007 4 Radial Velocity 1 516.220 10.50 119.47 2009 ## describe函数 describe函数计算每一列的常用统计值,给出一个比较概括性的数值。 通过下面describe计算的数据,我们可以对数据总体做出一个大概的判断,比如各个列的均值,最大值,均值等等。 在统计之前,我们需要删除掉缺失值以免影响最后结果。 ```python d = planets.dropna().describe() print(d) ``` number orbital_period mass distance year count 498.00000 498.000000 498.000000 498.000000 498.000000 mean 1.73494 835.778671 2.509320 52.068213 2007.377510 std 1.17572 1469.128259 3.636274 46.596041 4.167284 min 1.00000 1.328300 0.003600 1.350000 1989.000000 25% 1.00000 38.272250 0.212500 24.497500 2005.000000 50% 1.00000 357.000000 1.245000 39.940000 2009.000000 75% 2.00000 999.600000 2.867500 59.332500 2011.000000 max 6.00000 17337.500000 25.000000 354.000000 2014.000000 ## GroupBy GroupBy借用的是SQL的命令,通过GroupBy可以对数据进行灵活的几乎各种操作, 其核心思想是: - split:分割 - applay:应用 - combine:组合 通过运用GroupBy命令和不同的累计函数进行组合使用,对某些标签或索引进行累计分析。 ```python # 小案例 import pandas as pd df = pd.DataFrame({"name":list("ABCABC"), "data":range(100,106)}, columns=["name", "data"]) print("df = \n", df) # 按name列进行分组,然后求魅族的平均数 a = df.groupby("name").mean() print("\n a = \n", a) ``` df = name data 0 A 100 1 B 101 2 C 102 3 A 103 4 B 104 5 C 105 a = data name A 101.5 B 102.5 C 103.5 ## GroupBy对象 GroupBy返回的结果是一个抽象类型,可以看组是一个DataFrame的集合。 ### 按列取值返回的结果 ```python # 按method列进行组合 gb = planets.groupby("method") # gb是一个抽象数据类型 print(gb) # 对GroupBy结果还可以再次抽取数 print(gb["orbital_period"]) ``` ```python # 下面句子相当于按找method进行组合,然后选取orbital_period列,选取后求每个组的中位数 a = planets.groupby("method")['orbital_period'].median() print("组合后统计结果: \n", a) ``` 组合后统计结果: 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 ### 按组迭代 GroupBy对象支持进行迭代,返回每一组都是Series或者DataFrame数据。 ```python # 迭代回来的数据大概是每组以method作为名称,多行六列的一个DataFrame for (method, group) in planets.groupby("method"): print(method, group.shape) ``` Astrometry (2, 6) Eclipse Timing Variations (9, 6) Imaging (38, 6) Microlensing (23, 6) Orbital Brightness Modulation (3, 6) Pulsar Timing (5, 6) Pulsation Timing Variations (1, 6) Radial Velocity (553, 6) Transit (397, 6) Transit Timing Variations (4, 6) ### 调用方法 借助于Python强大的类方法(@classmethod), 可以直接对GroupBy的每一组对象添加功能,无论是DataFrame或者Series都可以使用。 ```python a = planets.groupby("method")['year'].describe().unstack() print(a) ``` method count Astrometry 2.000000 Eclipse Timing Variations 9.000000 Imaging 38.000000 Microlensing 23.000000 Orbital Brightness Modulation 3.000000 Pulsar Timing 5.000000 Pulsation Timing Variations 1.000000 Radial Velocity 553.000000 Transit 397.000000 Transit Timing Variations 4.000000 mean Astrometry 2011.500000 Eclipse Timing Variations 2010.000000 Imaging 2009.131579 Microlensing 2009.782609 Orbital Brightness Modulation 2011.666667 Pulsar Timing 1998.400000 Pulsation Timing Variations 2007.000000 Radial Velocity 2007.518987 Transit 2011.236776 Transit Timing Variations 2012.500000 std Astrometry 2.121320 Eclipse Timing Variations 1.414214 Imaging 2.781901 Microlensing 2.859697 Orbital Brightness Modulation 1.154701 Pulsar Timing 8.384510 Pulsation Timing Variations NaN Radial Velocity 4.249052 Transit 2.077867 Transit Timing Variations 1.290994 ... 50% Astrometry 2011.500000 Eclipse Timing Variations 2010.000000 Imaging 2009.000000 Microlensing 2010.000000 Orbital Brightness Modulation 2011.000000 Pulsar Timing 1994.000000 Pulsation Timing Variations 2007.000000 Radial Velocity 2009.000000 Transit 2012.000000 Transit Timing Variations 2012.500000 75% Astrometry 2012.250000 Eclipse Timing Variations 2011.000000 Imaging 2011.000000 Microlensing 2012.000000 Orbital Brightness Modulation 2012.000000 Pulsar Timing 2003.000000 Pulsation Timing Variations 2007.000000 Radial Velocity 2011.000000 Transit 2013.000000 Transit Timing Variations 2013.250000 max Astrometry 2013.000000 Eclipse Timing Variations 2012.000000 Imaging 2013.000000 Microlensing 2013.000000 Orbital Brightness Modulation 2013.000000 Pulsar Timing 2011.000000 Pulsation Timing Variations 2007.000000 Radial Velocity 2014.000000 Transit 2014.000000 Transit Timing Variations 2014.000000 Length: 80, dtype: float64 ## 累计,过滤和转换 GroupBy基本功能是分组,但分组之后相应的操作,为数据分析提供了很多高效的方法。 此类方法大概分为: - aggregate:累计 - filter:过滤 - transform:变换 - apply:应用 ```python ## 数据准备 import numpy as np import pandas as pd rng = np.random.RandomState(0) df = pd.DataFrame({'key':list("ABCABC"), 'data_1':range(100,106), 'data_2': rng.randint(0,10, 6)}, columns=['key', 'data_1', 'data_2']) print("df = \n", df) ``` df = key data_1 data_2 0 A 100 5 1 B 101 0 2 C 102 3 3 A 103 3 4 B 104 7 5 C 105 9 ### 累计 相比较于sum和median之类的功能,累计(aggregate)能实现比较复杂的操作,代表操作的参数可以是字符串,函数或者函数列表,并且能一次性计算所有累计值。 ```python # 统计min,median,max # 作为代表功能的参数,都放入一个列表中,可以是字符串,函数名等 rst = df.groupby('key').aggregate(['min', np.median, np.max]) print(rst) ``` data_1 data_2 min median amax min median amax key A 100 101.5 103 3 4.0 5 B 101 102.5 104 0 3.5 7 C 102 103.5 105 3 6.0 9 ```python # 或者还有其他的方法,对每列数据用不同的统计函数 rst = df.groupby('key').aggregate({'data_1':'min', 'data_2':'max'}) print(rst) ``` data_1 data_2 key A 100 5 B 101 7 C 102 9 ### 过滤 通过过了功能,可以保留我们需要的值,把不需要的去掉。 ```python # 过滤函数 def my_filter(x): return x['data_2'].std() > 1.5 rst = df.groupby('key').std() print('df.std = \n', rst) # 使用过滤函数,把不符合要求的过滤掉 rst = df.groupby('key').filter(my_filter) print("\n rst.filter_func = \n", rst) ``` df.std = data_1 data_2 key A 2.12132 1.414214 B 2.12132 4.949747 C 2.12132 4.242641 rst.filter_func = key data_1 data_2 1 B 101 0 2 C 102 3 4 B 104 7 5 C 105 9 ### 转换 累计操作把数据集合进行了裁剪和选择,而转换操作是把全量数据进行加工,得到的数据格式与输入数据一致,常见的操作是对数据减去均值,实现数据标准化。 ```python # 对数据进行标准化 rst = df.groupby('key').transform(lambda x: x - x.mean()) print(rst) ``` data_1 data_2 0 -1.5 1.0 1 -1.5 -3.5 2 -1.5 -3.0 3 1.5 -1.0 4 1.5 3.5 5 1.5 3.0 ### 应用 apply可以让人在每个数据上应用任意方法,这个函数让输入一个DataFrane,返回的结果可以是pandas对象或者标量。 ```python # 求每一项的百分比 def norm_data_1(x): # 求百分比 x['data_1'] = x['data_1'] / x['data_1'].sum() return x rst = df.groupby('key').apply(norm_data_1) print(rst) ``` key data_1 data_2 0 A 0.492611 5 1 B 0.492683 0 2 C 0.492754 3 3 A 0.507389 3 4 B 0.507317 7 5 C 0.507246 9 ## 设置分割的键 对DataFrame的分割可以根据列来,还可以有其他的方法,本节主要介绍对DataFrame的各种分割方法。 ### 将列表,数组,Series或者索引作为分组键 此时分组键可以是与DaytaFrame匹配的任意Series或者列表。 ```python # 普通数组作为分组键 L = [0,1,1,0,2,1] a = df.groupby(L).sum() print(a) ``` data_1 data_2 0 203 8 1 308 12 2 104 7 ```python # 直接用键值也可以,比较啰嗦 a = df.groupby(df['key']).sum() print(a) ``` data_1 data_2 key A 203 8 B 205 7 C 207 12 ### 用字典或者Series将索引映射到分组的名称 提供字典,按照字典的键值进行分组,最后结果使用字典键值映射的值。 要求索引必须跟字典的键值匹配。 ```python # 利用字典分组 D_mapping = {'A':"One", 'B':"Two", 'C':"Three"} df = df.set_index('key') a = df.groupby(D_mapping).sum() print(a) ``` data_1 data_2 One 203 8 Three 207 12 Two 205 7 ### 使用任意Python函数 我们还可以把Python函数传入groupby,与前面的内容类似,然后得到新的分组。 ```python # 传入任意分组 a = df.groupby(str.lower).mean() print(a) ``` data_1 data_2 a 101.5 4.0 b 102.5 3.5 c 103.5 6.0 ### 多个有效的键组成的列表 有效的键值可以组合起来,从而返回一个多级索引的分组结果。 ```python # 利用上面定义的字典,我们可以组合成多级索引 a = df.groupby([str.lower, D_mapping]).mean() print(a) ``` data_1 data_2 a One 101.5 4.0 b Two 102.5 3.5 c Three 103.5 6.0