Pandas:按列分组并将错误值从最底部节点传播到最上面

问题描述 投票:0回答:2

这是数据框:

import pandas as pd
import numpy as np

df = pd.DataFrame({
                   "node": np.repeat([0, 1, 2, 3], 3),
                   "type": np.tile(['A', 'B', 'C'], 4),
                   "flag": [True, True, True, True, True, False, True, True, True, False, True, True]
                  })

df.sort_values(by=['type', 'node'], ascending=True, inplace=True)

看起来像:

  node type flag
0   0   A   True
3   1   A   True
6   2   A   True
9   3   A   False
1   0   B   True
4   1   B   True
7   2   B   True
10  3   B   True
2   0   C   True
5   1   C   False
8   2   C   True
11  3   C   True

我想要做的是查找任何类型中是否有

False
标志,如果有则获取最底部的节点,并将所有上层节点的标志设置为
False
。结果会是这样的:

  node type flag
0   0   A   False
3   1   A   False
6   2   A   False
9   3   A   False
1   0   B   True
4   1   B   True
7   2   B   True
10  3   B   True
2   0   C   False
5   1   C   False
8   2   C   True
11  3   C   True

在本例中,输入

A
在节点
False
处得到了
3
,因此节点
1, 2
都应设置为
False
;输入
B
没有
False
,跳过;输入
C
在节点
False
处得到了
1
,因此节点
0
应设置为
False

我现在所做的是

gp = df.groupby(['type'])
result_indices = []
for name, group in gp:
    false_index = group[group['flag'] == False].index
    if len(false_index) > 0:
        result_indices.extend(group.index[group.index < false_index[-1]])

df.loc[result_indices, 'flag'] = False

有什么更好的解决方案可以避免for循环吗? (为了提高效率),我曾经尝试过像

mask
where
这样的方法,但不知道如何编写如此复杂的过滤器。 我可以像这样提取
False
索引:

df.sort_values(by=['type', 'node'], ascending=[True, False], inplace=True)
gp = df[df['flag'] == False].groupby('type').head(1).index

但不知道如何转发。

python pandas numpy
2个回答
0
投票

您可以(错误)使用

cumsum()
进行传播:通过累积和,您可以从上到下传播
True
值;例如,
np.cumsum([False, True, True, False]).astype(bool)
生成
[False,  True,  True,  True]
。您的情况的不同之处在于您想要从下到上传播
False
值。不过我们仍然可以使用
cumsum()

  1. 否定感兴趣的价值观。
  2. 反转值的顺序。
  3. 计算
    cumsum()
  4. 重新颠倒值的顺序。
  5. 转换回
    bool
    并对结果求反。

这可能如下所示:

import pandas as pd
import numpy as np

df = pd.DataFrame({
                   "node": np.repeat([0, 1, 2, 3], 3),
                   "type": np.tile(['A', 'B', 'C'], 4),
                   "flag": [True, True, True, True, True, False,
                            True, True, True, False, True, True]
                  })

df.sort_values(by=['type', 'node'], ascending=True, inplace=True)

# Reverse order of values
df_rev_neg = df.iloc[::-1]
# Negate values of interest
df_rev_neg["flag"] = ~df_rev_neg["flag"]
# Calculate grouped `cumsum()`, convert back to `bool`, negate values again
df["propagated"] = ~df_rev_neg.groupby("type")["flag"].cumsum().astype(bool)
print(df)

哪个打印:

    node type   flag  propagated
0      0    A   True       False
3      1    A   True       False
6      2    A   True       False
9      3    A  False       False
1      0    B   True        True
4      1    B   True        True
7      2    B   True        True
10     3    B   True        True
2      0    C   True       False
5      1    C  False       False
8      2    C   True        True
11     3    C   True        True

这里我们没有明确需要重新反转值的顺序(步骤 4),因为我们始终携带帧的索引。


0
投票

这是一种方法:

  • df
    反转您的
    [::-1]
    ,在“类型”上应用
    df.groupby
    ,为“标记”应用
    groupby.cummin
    ,然后反转回来。
df['flag'] = df[::-1].groupby('type')['flag'].cummin()[::-1]

df

    node type   flag
0      0    A  False
3      1    A  False
6      2    A  False
9      3    A  False
1      0    B   True
4      1    B   True
7      2    B   True
10     3    B   True
2      0    C  False
5      1    C  False
8      2    C   True
11     3    C   True
© www.soinside.com 2019 - 2024. All rights reserved.