查找满足掩码条件的最后一行

Finding the last row that meets conditions of a mask

提问人:x_Amir_x 提问时间:11/5/2023 最后编辑:cottontailx_Amir_x 更新时间:11/6/2023 访问量:167

问:

这是我的数据帧:

df = pd.DataFrame({'a': [20, 21, 333, 444], 'b': [20, 20, 20, 20]})

我想使用这个掩码创建列:c

mask = (df.a >= df.b)

我想获取满足此条件的最后一行并创建列。我想要的输出如下所示:c

     a   b    c
0   20  20  NaN
1   21  20  NaN
2  333  20  NaN
3  444  20  x

我尝试了下面的代码,但没有用:

df.loc[mask.cumsum().gt(1) & mask, 'c'] = 'x'
Python Pandas 索引 重复项

评论

2赞 Nick ODell 11/5/2023
@mandy8055我认为用 NaN 填充 NaN 值有点有趣。:)但你应该把它作为一个答案。

答:

2赞 mandy8055 11/5/2023 #1

与其检查累积总和是否大于 1,不如检查累积总和是否等于最大累积总和值,这相当于获得 [@Onyambu建议]。mask.sum()

df.loc[(mask.cumsum() == mask.sum()) & mask, 'c'] = 'x'

代码演示

评论

1赞 Onyambu 11/5/2023
mask.cumsum() == mask.sum()
1赞 mandy8055 11/5/2023
@Onyambu哇,谢谢。所以基本上使用它的原因是它直接给了我们掩码中的值总数。如果我对你的观点的理解是错误的,请纠正我。我没想过。mask.sum()True
1赞 Nick 11/5/2023
这个答案是错误的。看看如果出现以下情况会发生什么df.loc[3, 'a'] = 8
2赞 Nick 11/5/2023
请注意,这是一个相当简单的修复程序:df.loc[(mask.cumsum() == mask.sum()) & mask, 'c'] = 'x'
2赞 cottontail 11/5/2023 #2

要使掩码标记满足条件的最后一个值,请使用 keep last。我们知道它最多由 2 个值 (True/False) 组成。如果我们可以创建另一个掩码,将这些值的最后一次出现标记为 True,那么我们可以将其与所需的掩码本身链接。这是通过以下方式实现的:因为除了最后一次出现之外,标记重复为 True,因此它的否定为我们提供了我们想要的东西。duplicated()maskmask~mask.duplicated(keep='last')mask.duplicated(keep='last')

df = pd.DataFrame({'a': [20, 21, 333, 444], 'b': [20, 20, 20, 20]})
mask = (df.a >= df.b)

df['c'] = pd.Series('x', df.index).where(mask & ~mask.duplicated(keep='last'))

如果要切片/分配,也可以使用此链接蒙版。

df.loc[mask & ~mask.duplicated(keep='last'), 'c'] = 'x'

@mandy8055 的答案的简短版本是调用以获取最高 cum sum 的索引(尽管这在 pandas 2.1.0 上显示 FutureWarning)。正如 @mozway 所指出的,只要 中至少有一个 True 值,这就可以工作。idxmax()mask

df.loc[mask.cumsum().idxmax(), 'c'] = 'x'

result

评论

1赞 Nick 11/5/2023
@Amir不用担心;请注意,另一个答案有一个简单的修复,请参阅我的评论。
0赞 mozway 11/5/2023
但是如果没有匹配;)也会失败idxmax
0赞 mozway 11/5/2023
是的,这会在第一行放置一个“x”
0赞 mozway 11/5/2023
@cottontail是的,不确定在这种情况下需要什么(对我来说似乎没有必要一列 Nan),如果需要,您可以添加一个else
1赞 mozway 11/5/2023 #3

您可以在调用 idxmax 以获取最后一个值之前还原掩码:

mask = (df.a >= df.b)

m = mask[mask][::-1]
if len(m):
    df.loc[m.idxmax(), 'c'] = 'x'
#else:
#    df['c'] = float('nan')

输出:

     a   b    c
0   20  20  NaN
1   21  20  NaN
2  333  20  NaN
3  444  20    x

评论

0赞 x_Amir_x 11/5/2023
我应该接受哪个答案?:)你是对的。我读了你的评论。
0赞 mozway 11/5/2023
接受你最喜欢的任何东西。我认为目前公认的答案中的第一种方法是完全可以的;)
0赞 x_Amir_x 11/5/2023
我想知道你的选择是什么:)
0赞 mozway 11/5/2023
这取决于您的真实数据。它有多大?“c”列是否预先存在?
1赞 mozway 11/5/2023
然后确定不同的方法,亲眼看看什么是最好的;)
2赞 Anna Andreeva Rogotulka 11/6/2023 #4

您可以使用 mask 的最后一个索引为 True,然后将此值更改为“x”at

import pandas as pd
import numpy as np

df = pd.DataFrame({'a': [20, 21, 333, 444], 'b': [20, 20, 20, 20]})

mask = (df.a >= df.b)
df['c'] = np.nan
last_index = df[mask].index[-1]
df.at[last_index, 'c'] = 'x'

print(df)