在pandas dataframe中使用三元运算符的apply函数,不需要分组列

5

我该如何在pandas数据框的apply函数中使用三元运算符的lambda表达式?

首先,这段代码来自于R/plyr,这正是我想要的:

ddply(mtcars, .(cyl), summarise, sum(ifelse(carb==4,1,0))/sum(ifelse(carb %in% c(4,1),1,0)))

在上述函数中,我可以使用ifelse函数,R的三元运算符,来计算结果数据框。但是,当我想在Python/pandas中使用以下代码实现相同的功能时:
mtcars.groupby(["cyl"]).apply(lambda x: sum(1 if x["carb"] == 4 else 0) / sum(1 if x["carb"] in (4, 1) else 0))

出现以下错误:

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

那么我怎么才能计算并得到与R/plyr中相同的数据框呢?

需要说明的是,如果我在不对列进行分组的情况下使用三目运算符,例如:

mtcars.apply(lambda x: sum(1 if x["carb"] == 4 else 0) / sum(1 if x["carb"] in (4, 1) else 0), axis=1)

我可以出于某些原因获得结果数据框(但这不是我想要做的)。

谢谢。

[更新]

抱歉,原始示例在使用三元运算符时并不好,因为它使用了可以用作二进制的10。因此,更新后的R/plyr代码如下:

ddply(mtcars, .(cyl), summarise, sum(ifelse(carb==4,6,3))/sum(ifelse(carb %in% c(4,1),8,4)))

在这种情况下使用三元运算符是否可行?
3个回答

4
我认为您的代码可以翻译成这样:
mtcars.groupby(["cyl"])['carb'].apply(lambda x: sum((x == 4).astype(float)) / sum(x.isin((4, 1))))

玩具例子:

>>> mtcars = pd.DataFrame({'cyl':[8,8,6,6,6,4], 'carb':[4,3,1,5,4,1]})
>>> mtcars
   carb  cyl
0     4    8
1     3    8
2     1    6
3     5    6
4     4    6
5     1    4
>>> mtcars.groupby(["cyl"])['carb'].apply(lambda x: sum((x == 4).astype(float)) / sum(x.isin((4, 1))))
cyl
4      0.0
6      0.5
8      1.0
dtype: float64

更新

在更复杂的情况下,您可以使用numpy.where()函数:

>>> import numpy as np
>>> mtcars.groupby(["cyl"])['carb'].apply(lambda x: sum(np.where(x == 4,6,3).astype(float)) / sum(np.where(x.isin((4,1)),8,4)))
cyl
4      0.375
6      0.600
8      0.750
dtype: float64

你可以将(x == 4).astype(float)替换为(x == 4)*1,在我的测试中对于大型数组速度更快,但我不确定它的普适性。 - cwharland
谢谢。但我刚意识到我在原帖中的例子并不是一个好例子,因为它可以完全不考虑三元运算符而完成,因为它只使用了 10,这可以很容易地转换为 true/false 二进制数。所以我更新了原帖。抱歉。 - Blaszard
太棒了!非常感谢。 - Blaszard

2
我认为x['carb']是一个numpy数组(或其子类)。在这种情况下,x['carb'] == 4返回一个布尔数组。当值等于4时,数组中的相应位置为True,否则为False。这是numpy的一个非常方便的特性,但在这种情况下可能会令人感到烦恼(因为人们通常会期望==运算符返回一个布尔结果)。
解决方法是在结果上调用.all()
(x['carb'] == 4).all()

只有当(x ['carb'] == 4)中的所有元素都是True时,才会返回True


0
Pandas的apply()是针对行进行操作的。因此,三元运算符可以用作如下:
df.apply(lambda row: [1 if x>0 else 0 for x in row])

在 OP 的例子中:

mtcars.groupby(["cyl"])['carb'].apply(lambda r: sum([1 if x==4 else 0 for x in r])/sum([1 if x in [1,4] else 0 for x in r]))

(虽然这个例子可能需要捕获除以零的情况...)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接