Python 如果一列不同但在给定列表中,则删除数据帧中的重复项

Python 如果一列不同但在给定列表中,则删除数据帧中的重复项,python,pandas,dataframe,duplicates,Python,Pandas,Dataframe,Duplicates,我有一个数据框,其中有来自两个源的重复条目,所有值都应该是唯一的,但有一列的格式不同,因此我应该删除一列中具有不同名称的重复项,但仅当名称在列表中时 从技术上讲,如果存在具有相同a和B值的另一行,但仅当这一行的Z值为'bar'而另一行的'Z'为'foo'时,我希望删除pandas数据帧中的一行 一个例子可能更清楚: 我有给定的数据帧df A B Z 'a' 'a' 'foo' 'a' 'a' 'bar' 'b' 'a' 'bar' 'c' 'c'

我有一个数据框,其中有来自两个源的重复条目,所有值都应该是唯一的,但有一列的格式不同,因此我应该删除一列中具有不同名称的重复项,但仅当名称在列表中时

从技术上讲,如果存在具有相同
a
B
值的另一行,但仅当这一行的
Z
值为
'bar'
而另一行的'Z'为
'foo'
时,我希望删除pandas数据帧中的一行

一个例子可能更清楚:

我有给定的数据帧
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'
请注意:

  • 不应触摸
    Z
    列中除
    'foo'
    'bar'
    以外的值的行
  • 如果
    'foo'
    'bar'
    保持不变,这并不重要,因为它们将在以后更改为相同的值
  • 将这两个“foo”和“bar”概括为一个列表会很好
迄今为止的尝试: 这是我最好的猜测,但它不起作用……我不太明白groupby的回报是什么。还有,我肯定有一些神奇的熊猫,我就是找不到

new_df = []
for row in df.groupby('A'):
    if rowloc['Z'].isin('foo'):
         if not row['Z'].isin('bar'):        
            new_df.append(row)

谢谢

我认为您可以通过连接原始数据帧的两个子集来获得预期的结果:

  • Z值既不是
    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()
一个更简单的选择可能是用
Z
中的
bar
替换
foo
,然后简单地删除重复项:

df1 = df.replace({'Z':{'foo':'bar'}}).drop_duplicates()
您甚至可以用实际要使用的其他值替换
foo
bar

df1 = df.replace({'Z':{'foo':'xyz', 'bar':'xyz'}}).drop_duplicates()

我会使用
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)

可能重复以上答案是更好的顺便说一句,没有不必要的分组!不过感谢您花时间,我还是从您的示例中学到了一些功能:)不客气,(由
drop\u duplicates使用)函数通常非常有用。如果一行是重复的,则可以生成一个布尔值
序列。可以使用子集(如上所述),并且可以选择不将第一个/最后一个实例标记为重复,或者只标记全部。非常感谢,我没有意识到您可以使用子集来使用drop\u重复。祝你有愉快的一天。