我有一个来自两个来源的重复条目的数据框,所有值都应该是唯一的,但是一列的格式不同,因此我应该在一列中删除具有不同名称的副本,但仅当名称在一个清单。
从技术上讲,如果存在另一行具有相同A
和B
值的行,我想删除pandas数据框中的行,但仅当此行的Z
值为{时{1}}和另一个' Z'是'bar'
。
示例可能更清晰:
我有给定的数据框'foo'
df
我想得到
A B Z
'a' 'a' 'foo'
'a' 'a' 'bar'
'b' 'a' 'bar'
'c' 'c' 'foo'
'd' 'd' 'blb'
请注意:
A B Z
'a' 'a' 'foo'
'b' 'a' 'bar'
'c' 'c' 'foo'
'd' 'd' 'blb'
列中除'foo'
和'bar'
以外的其他值的行。Z
和'foo'
保持不变,这一点并不重要,因为之后它们会更改为相同的值。'bar'
和'foo'
概括为一个列表会很棒。到目前为止尝试: 这是我最好的猜测,它虽然不起作用...我不太了解groupby返回的内容。此外,我确信有一些神奇的大熊猫单线,我找不到。
'bar'
谢谢!
答案 0 :(得分:1)
我认为你可以通过连接原始数据帧的两个子集来获得预期的结果:
foo
也不是bar
A
和B
重复的内容这是一个给出预期输出的例子:
data = """ A B Z
a a foo
a a bar
b a bar
c c foo
d d blb"""
df = pd.read_csv(StringIO(data),sep='\s+')
ls = ['foo','bar']
df1 = pd.concat((df.loc[~(df.Z.isin(ls))], # no foos or bars here
df.loc[ df.Z.isin(ls)].drop_duplicates(subset=['A','B'])
)).sort_index()
更简单的选项可能是在foo
中的bar
替换Z
,然后只删除重复项:
df1 = df.replace({'Z':{'foo':'bar'}}).drop_duplicates()
您甚至可以将foo
和bar
替换为您实际要使用的其他值:
df1 = df.replace({'Z':{'foo':'xyz', 'bar':'xyz'}}).drop_duplicates()
答案 1 :(得分:0)
我会使用groupby
来模拟检查重复项(正如您所想的那样)。相反,您将对A
和B
进行分组,然后使用每个单独的分组DF来检查foo
和bar
是否在Z
。
import pandas as pd
df = pd.DataFrame()
df['A'] = ['a', 'a', 'b', 'c', 'd']
df['B'] = ['a', 'a', 'a', 'c', 'd']
df['Z'] = ['foo', 'bar', 'bar', 'foo', 'blib']
VALUES_PRESENT_TO_DROP = ['foo', 'bar']
# Simulate `df.duplicated, keep=False`
grouped = df.groupby(['A', 'B'])
# Start a list of the final DFs to keep, will append to this
dfs_to_keep = []
# PLEASE SEE EDIT BELOW
# We're not interested in the values of thr group, just each df
for _, grouped_df in grouped:
values_in_col = grouped_df['Z'].unique()
# Check that all the required values to drop are present
if all((val in values_in_col for val in VALUES_PRESENT_TO_DROP)):
# Append just the first row
dfs_to_keep.append(grouped_df.iloc[[0]])
else:
dfs_to_keep.append(grouped_df)
# Combine all into final, deduped DF
df_final = pd.concat(dfs_to_keep).sort_index()
df_final
A B Z
0 a a foo
2 b a bar
3 c c foo
4 d d blib
编辑:刚刚意识到这一行:“不应触及Z列中除'foo'和'bar'之外的其他值的行。”
此修改需要对逻辑进行一些更改。请参阅下面的插槽:
# We're not interested in the values of the group, just each df
for _, grouped_df in grouped:
mask_possibly_edit = grouped_df['Z'].isin(VALUES_PRESENT_TO_DROP)
# Always keep those that do not have the specified valued in `Z`
dfs_to_keep.append(grouped_df[~mask_possibly_edit])
df_possibly_dedupe = grouped_df[mask_possibly_edit]
values_in_col = df_possibly_dedupe['Z'].unique()
# Check that all the required values to drop are present
if all((val in values_in_col for val in VALUES_PRESENT_TO_DROP)):
# Append just the first row
dfs_to_keep.append(df_possibly_dedupe.iloc[[0]])
else:
dfs_to_keep.append(df_possibly_dedupe)