根据另一个数据框中的点列表从 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
中具有相同 A
和 B
的所有行,并按 [= 的值进行索引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 组不变。
我有两个数据框:
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
中具有相同 A
和 B
的所有行,并按 [= 的值进行索引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 组不变。