根据另一个数据框中的点列表从 pandas 数据框中删除行

drop rows from a pandas dataframe based on list of points in another dataframe

我有两个数据框:

data = pd.DataFrame({"A": np.repeat(np.arange(1.,11.),50), 
                    "B": np.tile(np.repeat(np.arange(0.,5.),10),10), 
                    "C":np.arange(500)})
bad_data = pd.DataFrame({"A": [1., 2., 7., 9.], 
                           "B": [0., 3., 0., 2.], 
                           "points": [[0, 1],[0],[1],[0,1]]})
data.head(15)
bad_data
>>> data.head(15)
      A    B   C
0   1.0  0.0   0
1   1.0  0.0   1
2   1.0  0.0   2
3   1.0  0.0   3
4   1.0  0.0   4
5   1.0  0.0   5
6   1.0  0.0   6
7   1.0  0.0   7
8   1.0  0.0   8
9   1.0  0.0   9
10  1.0  1.0  10
11  1.0  1.0  11
12  1.0  1.0  12
13  1.0  1.0  13
14  1.0  1.0  14
>>> bad_data
     A    B  points
0  1.0  0.0  [0, 1]
1  2.0  3.0     [0]
2  7.0  0.0     [1]
3  9.0  2.0  [0, 1]

对于 data 的每一行,我想删除 bad_data 中具有相同 AB 的所有行,并按 [= 的值进行索引18=]。例如,bad_data 的第一行告诉我需要删除 data 的前两行:

      A    B   C
0   1.0  0.0   0
1   1.0  0.0   1

我该怎么做?我能够编造出这种恐怖,但读起来很难看。你能帮我写一个更Pythonic/readable的解决方案吗?

rows_to_remove = []
for A, B in zip(bad_data['A'], bad_data['B']):
    rows_in_data = (data['A'] == A) & (data['B'] == B)
    rows_in_bad_data = (bad_data['A'] == A) & (bad_data['B'] == B)
    bad_points = bad_data.loc[rows_in_bad_data, 'points'].values[0]
    indices = data[rows_in_data].index.values[bad_points]
    rows_to_remove.extend(indices)
    print(rows_to_remove)
data.drop(data.index[rows_to_remove], inplace=True)

如果我理解正确或者我的尝试是最优雅的方式,则不是 100%,所以请告诉我这是否适合您:

bad_indexes = []
labels = ['A', 'B']

for _, s in bad_data.iterrows():
    p = data.loc[s['points']]
    p = p[p[labels].eq(s[labels]).all(1)]
    bad_indexes.extend(p.index)

result = data.loc[data.index.difference(bad_indexes)]

我假定 data 的索引具有唯一值。

IIUC,你可以对展开的bad_data:

进行反向合并
data2 = (data
.assign(points=data.groupby(['A', 'B']).cumcount())  # get index per group (=points)
.merge(bad_data.explode('points'), on=['A', 'B', 'points'], # outer merge
       indicator=True, how='outer')
.loc[lambda d: d['_merge'].eq('left_only')]  # keep the rows unique to the left
.drop(columns=['points', '_merge'])          # remove helper columns
)

另一种选择是使用 GroupBy.apply:

# craft a Series of list of points indexed by A/B
s = bad_data.set_index(['A', 'B'])['points']
    # group by A/B
data2 = (data
     .groupby(['A', 'B'], as_index=False, group_keys=False)
     # get the real index names from "index" and drop if the key is present in s
     # else leave the group unchanged
     .apply(lambda g: g.drop(g.index[s.loc[g.name]]) if g.name in s else g)
)

这两种方法都会产生与您的自定义代码相同的数据帧。

输出形状:

data2.shape
# (494, 3)

第二个解决方案的详细信息:

  • 制作系列 s 如下:
A    B  
1.0  0.0    [0, 1]
2.0  3.0       [0]
7.0  0.0       [1]
9.0  2.0    [0, 1]
Name: points, dtype: object
  • 按A/B
  • 分组
  • 对于每个组,如果它存在于s的索引中(key是g.name),取值s.loc[g.name],从相对位置得到匹配的索引在群里:g.index[s.loc[g.name]],喂这个掉落。如果 A/B 索引不存在,return 组不变。