大熊猫groupby中的有条件分配

时间:2020-05-04 13:27:21

标签: python pandas dataframe group-by

假设我下面有df

df = pd.DataFrame({
    'ID': ['a', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd', 'd'],
    'V': np.array(range(0,10))
})

我要groupby变量ID并将值赋给新列X,具体取决于(1)每个组的大小以及每行是否位于顶部({{ 1}}),“中间”(更像是在顶部和底部之间)(T)或底部(M)行。如果组中只有一行,则分配的值为B。在这种情况下,结果将如下所示:

N

我可以使用类似的方法(针对 ID V X 0 a 0 N 1 b 1 T 2 b 2 B 3 c 3 T 4 c 4 M 5 c 5 B 6 d 6 T 7 d 7 M 8 d 8 M 9 d 9 B )逐步进行此操作:

T

但这感觉是一个不好的解决方案。我宁愿一劳永逸。有什么想法吗?

2 个答案:

答案 0 :(得分:4)

这很容易理解:

groups = df.groupby('ID')
first = groups['V'].head(1).index
last = groups['V'].tail(1).index

# the default middle values
df['X'] = 'M'

# the top and bottom values
df.loc[first, 'X'] = 'T'
df.loc[last, 'X'] = 'B'

# the unique values
ones = groups['V'].transform('size') == 1
df.loc[ones, 'X'] = 'N'

输出:

  ID  V  X
0  a  0  N
1  b  1  T
2  b  2  B
3  c  3  T
4  c  4  M
5  c  5  B
6  d  6  T
7  d  7  M
8  d  8  M
9  d  9  B

答案 1 :(得分:2)

一种解决方法是将每一行的ID与它之前/之后的ID进行比较。

例如:

df["top"] = df.ID != df.shift().ID
df["bottom"] = df.ID != df.shift(-1).ID
df["mid"] = (df.ID == df.shift(-1).ID) & (df.ID == df.shift(1).ID)

结果是:

    ID  V   bottom  top mid
0   a   0   True    True    False
1   b   1   False   True    False
2   b   2   True    False   False
3   c   3   False   True    False
4   c   4   False   False   True
5   c   5   True    False   False
6   d   6   False   True    False
7   d   7   False   False   True
8   d   8   False   False   True
9   d   9   True    False   False

您现在可以使用想要创建T / B / M / N列的任何逻辑:

df.loc[df.bottom & (~df.mid), "V"] = "B"
df.loc[df.top & (~df.mid), "V"] = "T"
df.loc[df.mid, "V"] = "M"
df.loc[df.bottom & df.top, "V"] = "N"

df[["ID", "V"]]

导致:

    ID  V
0   a   N
1   b   T
2   b   B
3   c   T
4   c   M
5   c   B
6   d   T
7   d   M
8   d   M
9   d   B