在组对象上应用vs变换

考虑以下数据帧:

列=['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)})

applytransform

transformapplygroupby方法之间有两个主要区别

  • 输入:
  • apply将每个组的所有列作为数据帧隐式传递给自定义函数
  • transform则将每组的每一列作为系列分别传递给自定义函数
  • 输出:
  • 传递给apply的自定义函数可以返回标量、序列或数据帧(或numpy数组甚至列表)
  • 传递给变换的自定义函数必须返回与组长度相同的序列(一维序列、数组或列表)

因此,transform一次只对一个系列有效,apply一次对整个数据帧有效

检查自定义函数

检查传递给applytransform的自定义函数的输入会有很大帮助

例子

让我们创建一些示例数据并检查组,以便您可以看到我所说的内容:

将熊猫作为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))
提升

现在,让我们将此函数传递给groupbyapplytransform方法,以查看传递给它的对象:

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

发表评论