数据转换
除了前面介绍的聚合操作以外,Pandas还提供了其它操作应用到分组运算中,比如,transform()和apply()方法,它们能够执行更多其他的分组运算。
前面使用agg()方法进行聚合运算时,返回的数据集的形状(shape)与被分组数据集的形状是不同的,如果希望保持与原数据集形状相同,那么可以通过transfrom()方法实现。transfrom()方法的语法格式如下:
transform(func, *args, **kwargs)
上述方法中只有一个func参数,表示操作Pandas对象的函数。transform()方法返回的结果有两种,一种是可以广播的标量值(np.mean),另一种可以是与分组大小相同的结果数组。
通过transfrom()方法操作分组时,transfrom()方法会把func函数应用各个分组中,并且将结果放在适当的位置上。
假设现在有一个5行6列的表格,从左边数前5列的数据都是数值,而最后一列的数据是字符串,具体如图1所示。
a | b | c | d | e | key | |
---|---|---|---|---|---|---|
0 | 0 | 1 | 2 | 3 | 4 | A |
1 | 1 | 2 | 3 | 4 | 5 | A |
2 | 6 | 7 | 8 | 9 | 10 | B |
3 | 10 | 11 | 12 | 13 | 14 | B |
4 | 3 | 4 | 4 | 5 | 3 | B |
图1 表格结构示例
按照图1中表格的key列进行分组,可以将表格划分为A、B两组,之后通过transform()方法对这两组执行求平均值的操作。
首先,创建一个与图1中表格结构相同的DataFrame对象,代码如下:
In [24]: import pandas as pd
df = pd.DataFrame({'a': [0, 1, 6, 10, 3],
'b': [1, 2, 7, 11, 4],
'c': [2, 3, 8, 12, 4],
'd': [3, 4, 9, 13, 5],
'e': [4, 5, 10, 14, 3],
'key': ['A', 'A', 'B', 'B', 'B']})
df
Out[24]:
a b c d e key
0 0 1 2 3 4 A
1 1 2 3 4 5 A
2 6 7 8 9 10 B
3 10 11 12 13 14 B
4 3 4 4 5 3 B
上述示例中,创建了一个5行6列的DataFrame对象df,其中,df对象的key列中的数据都是字符串。
以key列进行分组,可以将df对象拆分成A、B两组,将mean()方法应用到每个分组中,计算每个分组中每列的平均值,具体代码如下。
In [25]: data_group = df.groupby('key').transform('mean')
In [26]: data_group
Out[26]:
a b c d e
0 0.500000 1.500000 2.5 3.5 4.5
1 0.500000 1.500000 2.5 3.5 4.5
2 6.333333 7.333333 8.0 9.0 9.0
3 6.333333 7.333333 8.0 9.0 9.0
4 6.333333 7.333333 8.0 9.0 9.0
从输出结果可以看出,每列的数据是各分组求得的平均数。以A组为例,在A组中a列原来的数据0与1的均值为0.5(0.5为一个标量值),这个均值在A组的a列数据上进行了广播,使得所有A组a列的数据都变成了0.5。
上述示例中,原始数据的列数与最终结果的列数是不一样的。假设现在有另外一个5行4列的表格,该表格中每列的数据都是数值类型的,如图2所示。
A | B | C | D | |
---|---|---|---|---|
0 | 2 | 4 | 9 | 3 |
1 | 3 | 2 | 7 | 4 |
2 | 3 | 3 | 0 | 8 |
3 | 4 | 6 | 7 | 6 |
4 | 2 | 6 | 8 | 10 |
图2 表格结构示例
如果希望图2中的表格进行转换后,返回具有相同形状的结果,那么可以创建一个Series对象,该对象的长度与表格中的行数是相同的,按照这个Series对象进行分组,便可以得到一个形状相同的对象。
首先,创建与图2中表格结构相同的DataFrame对象,代码如下:
In [27]: import pandas as pd
df = pd.DataFrame({'A': [2, 3, 3, 4, 2],
'B': [4, 2, 3, 6, 6],
'C': [9, 7, 0, 7, 8],
'D': [3, 4, 8, 6, 10]})
df
Out[27]:
A B C D
0 2 4 9 3
1 3 2 7 4
2 3 3 0 8
3 4 6 7 6
4 2 6 8 10
然后,创建一个列表key,key的长度需要与df对象的行数是一样的,我们把key当做分组键,将df对象按照key列表进行分组,然后同样对每个分组执行求平均值的操作,具体代码如下:
In [28]: # 以key为分组依据,对df对象进行分组
key = ['one','one','two',' two',' two']
df.groupby(key).transform('mean')
Out[28]:
A B C D
0 2.5 3 8 3.5
1 2.5 3 8 3.5
2 3.0 5 5 8.0
3 3.0 5 5 8.0
4 3.0 5 5 8.0
通过比较转换前与转换后的结果,发现两次输出的结果具有相同的大小。在这里,计算出的平均值不仅在每组每列中进行了广播,而且也保证了返回的结果与原数据的形状相同。
在进行数据转换时,使用内置方法与自定义函数的方式是一样的,只需要将定义好的函数名称作为func参数传入到transform()方法中即可,这里就不再举例了。