考虑以下数据帧:
列=['A','B','C','D']
记录=[
[foo',one',0.162003,0.087469],
[bar'、-1.156319、-1.526271999999],
[foo',two',0.833892,-1.666304],
[bar',[three',-2.026673,-0.322057000000000004],
[foo',two',0.411452000000000004,-0.954370999999],
[bar',two',0.765878,-0.095968],
[foo',one',-0.65489,0.678091],
[foo',three',-1.789842,-1.130922]
]
df=pd.DataFrame.from_记录(记录,列=列)
&引用&引用&引用;
A、B、C、D
0 foo one 0.162003 0.087469
1巴1-1.156319-1.526272
2 foo two 0.833892-1.666304
三巴-2.026673-0.322057
4两个0.411452-0.954371
5巴两个0.765878-0.095968
6富一-0.654890 0.678091
7福三-1.789842-1.130922
&引用&引用&引用;
以下命令起作用:
df.groupby('A').apply(lambda x:(x['C']-x['D']))
df.groupby('A').apply(lambda x:(x['C']-x['D']).mean())
但以下工作都没有:
df.groupby('A').transform(lambda x:(x['C']-x['D']))
#KeyError或ValueError:无法将输入数组从形状(5)广播到形状(5,3)
df.groupby('A').transform(lambda x:(x['C']-x['D']).mean())
#KeyError或TypeError:无法连接非NDFrame对象
为什么?文档中的示例似乎表明,对组调用transform可以进行行操作处理:
#注意,以下建议行操作(x.mean是列平均值)
zscore=lambda x:(x-x.mean())/x.std()
transformed=ts.groupby(key).transform(zscore)
换句话说,我认为转换本质上是一种特定类型的apply(不聚合)。我错在哪里
以下是上述原始数据帧的构造,仅供参考:
df=pd.DataFrame({'A':['foo','bar','foo','bar',',
“foo”,“bar”,“foo”,“foo'],
“B”:一、一、二、三、,
“二”、“二”、“一”、“三”],
“C”:randn(8),“D”:randn(8)})
apply和transform
transform和applygroupby方法之间有两个主要区别
- 输入:
apply将每个组的所有列作为数据帧隐式传递给自定义函数- 而
transform则将每组的每一列作为系列分别传递给自定义函数 - 输出:
- 传递给
apply的自定义函数可以返回标量、序列或数据帧(或numpy数组甚至列表) - 传递给变换的自定义函数必须返回与组长度相同的序列(一维序列、数组或列表)
因此,transform一次只对一个系列有效,apply一次对整个数据帧有效
检查自定义函数
检查传递给apply或transform的自定义函数的输入会有很大帮助
例子
让我们创建一些示例数据并检查组,以便您可以看到我所说的内容:
将熊猫作为pd导入
将numpy作为np导入
df=pd.DataFrame({'State':['Texas','Texas','Florida','Florida'],
‘a’:[4,5,1,3],‘b’:[6,10,3,11]})
a国b国
0德克萨斯州4 6
德克萨斯州1 5 10
佛罗里达州2 1 3
3佛罗里达3 11
让我们创建一个简单的自定义函数,它打印出隐式传递对象的类型,然后引发一个错误,以便停止执行
def检查(x):
打印(类型(x))
提升
现在,让我们将此函数传递给groupbyapply和transform方法,以查看传递给它的对象:
df.groupby(’State’)。应用(检查)
<;类“pandas.core.frame.DataFrame”>;
<;类“pandas.core.frame.DataFrame”>;
访问违例
如您所见,数据帧被传递到inspect函数中。您可能想知道为什么DataFrame类型打印了两次。熊猫在第一组跑了两次。它这样做是为了确定是否有一种快速的方法来完成计算。这是一个你不应该担心的小细节
现在,让我们对transform
df.groupby(’State’).transform(检查)
<;类别“熊猫.核心.系列.系列”>;
<;类别“熊猫.核心.系列.系列”>;
访问违例
它被传递一个系列-一个完全不同的对象
因此,transform一次只能处理单个序列。它不可能同时作用于两列。因此,如果我们尝试从自定义函数的b中减去a列,我们将得到transform错误。见下文:
def减去2(x):
返回x['a']-x['b']
df.groupby('State').transform(减二)
KeyError:('a','发生在索引a')
当pandas试图查找不存在的系列索引a时,我们得到一个keyrerror。您可以使用apply完成此操作,因为它具有整个数据帧:
df.groupby(’State’)。应用(减去两个)
状态
佛罗里达2-2
3 -8
德克萨斯州0-2
1 -5
数据类型:int64
输出是一个系列,由于保留了原始索引,所以有点混乱,但我们可以访问所有列
显示传递的对象
在自定义函数中显示整个pandas对象会有更大的帮助,因此您可以准确地看到正在使用的操作。您可以使用print语句,我喜欢使用IPython.display模块中的display功能,以便在jupyter笔记本中以HTML格式很好地输出数据帧:
从IPython.display导入显示
def减去2(x):
显示器(x)
返回x[‘a’]-x[‘b’]
截图:
Transform必须返回与组大小相同的一维序列
另一个区别是transform必须返回与组大小相同的一维序列。在这个特定的实例中,每个组有两行,因此transform必须返回两行的序列。如果没有,则会引发错误:
def返回\u三(x):
返回np.array([1,2,3])
df.groupby('State').transform(返回三个)
ValueError:转换必须为每个组返回标量值
错误消息并不能真正描述问题。必须返回与组长度相同的序列。因此,这样的函数可以工作:
def rand\u group\u len(x):
返回np.rand.rand(len(x))
df.groupby('State').transform(rand\u group\u len)
a b
0 0.962070 0.151440
1 0.440956 0.782176
2 0.642218 0.483257
3 0.056047 0.238208
返回单个标量对象也适用于transform
如果仅从自定义函数返回一个标量,则transform将对组中的每一行使用它:
定义组总和(x):
返回x.sum()
df.groupby('State').transform(组和)
a b
0 9 16
1 9 16
2 4 14
3 4 14