根据其他行的值有条件地标记数据框中的行

flag rows in a dataframe conditionally on values of other rows

考虑数据帧df_in = pd.DataFrame({'id': [0,1,2,2,3,4,4,4], 'channel': [0,1,0,1,3,2,3,1]})

> df_in

   id  channel
0   0        0
1   1        1
2   2        0
3   2        1
4   3        3
5   4        2
6   4        3
7   4        1

和一个 2 元组列表 allowed_channel_couples = ((0,1),(2,3))

我想获取一个新的数据框 df_out 作为:

   id  channel  flag
0   0        0     0
1   1        1     0
2   2        0     1
3   2        1     1
4   3        3     0
5   4        2     1
6   4        3     1
7   4        1     0

df_out 中的每一行等于 df_in 中的行。但是,df_out 包含一个名为 flag 的新列。 df_out 中某行的 flag 值等于 1 仅当存在具有相同 id 的另一行时,其通道值 'matches' 第一行的通道值,根据 allowed_channel_couples.

因此考虑 df_in 中索引为 5 的行。该行 id 等于 4,channel 等于 2。由于索引 6 处的行 id 等于 4,channel 值等于 3,因此 (2,3) 是在 allowed_channel 中,索引为 5 的行和索引为 6 的行都标有标志 1。 另一方面,df_in 的索引为 7 的行有 id 等于 4,channel 等于 1。在这种情况下允许标记的对是 (0,1)id 等于 4 且通道等于 0 的其他行不存在,因此索引为 7 的行标记为 0.

我在解决这个避免 python 慢循环的问题时遇到了麻烦。关于如何有效地获取标志列的任何想法?

想法:

   id    channel
0   0        [0]
1   1        [1]
2   2     [0, 1]
3   3        [3]
4   4  [2, 3, 1]

df_in.groupby('id', as_index=False).agg({'channel': lambda x: tuple(x)}).

您可以将值从一个元组值映射到另一个元组值,然后识别重复值:

d = dict(allowed_channel_couples)
# {0: 1, 2: 3}

df_in['flag'] = (df_in
 .assign(channel=df_in['channel'].map(d).fillna(df_in['channel']))
 .duplicated(['id', 'channel'], keep=False)
 .astype(int)
)

输出:

   id  channel  flag
0   0        0     0
1   1        1     0
2   2        0     1
3   2        1     1
4   3        3     0
5   4        2     1
6   4        3     1
7   4        1     0