Pandas 基于 groupby 创建新列并应用 lambda if 语句

Pandas create new column base on groupby and apply lambda if statement

我对 groupbyapply

有疑问
df = pd.DataFrame({'A': ['a', 'a', 'a', 'b', 'b', 'b', 'b'], 'B': np.r_[1:8]})

我想为每个组创建一个列 C,如果 B > z_score=2 则取值 1,否则取 0。代码:

from scipy import stats
df['C'] = df.groupby('A').apply(lambda x: 1 if np.abs(stats.zscore(x['B'], nan_policy='omit')) > 2 else 0, axis=1)

但是,我对代码不成功,无法找出问题所在

GroupBy.transform 与 lambda、函数一起使用,然后进行比较并将 True/False 转换为 1/0 转换为整数:

from scipy import stats

s = df.groupby('A')['B'].transform(lambda x: np.abs(stats.zscore(x, nan_policy='omit')))
df['C'] = (s > 2).astype(int)

或使用numpy.where:

df['C'] = np.where(s > 2, 1, 0)

您的解决方案中的错误是每个组:

from scipy import stats

df = df.groupby('A')['B'].apply(lambda x: 1 if np.abs(stats.zscore(x, nan_policy='omit')) > 2 else 0)

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

如果检查 pandas docs 中的陷阱:

pandas follows the NumPy convention of raising an error when you try to convert something to a bool. This happens in an if-statement or when using the boolean operations: and, or, and not.

因此,如果改用其中一种解决方案 if-else:

from scipy import stats

df = df.groupby('A')['B'].apply(lambda x: (np.abs(stats.zscore(x, nan_policy='omit')) > 2).astype(int))

print (df)
A
a       [0, 0, 0]
b    [0, 0, 0, 0]
Name: B, dtype: object

但随后需要转换为列,为避免此问题使用 groupby.transform

您可以使用 groupby + apply 函数来查找每个组中每个项目的 z 分数;展开结果列表;使用 gt 创建一个布尔系列并将其转换为 dtype int

df['C'] = df.groupby('A')['B'].apply(lambda x: stats.zscore(x, nan_policy='omit')).explode(ignore_index=True).abs().gt(2).astype(int)

输出:

   A  B  C
0  a  1  0
1  a  2  0
2  a  3  0
3  b  4  0
4  b  5  0
5  b  6  0
6  b  7  0