【问题标题】:Delete rows of a data frame based on quantity of data in another data frame根据另一个数据框中的数据量删除数据框的行
【发布时间】:2018-06-22 19:49:21
【问题描述】:

我有两个熊猫数据框ABB 是 A 的子集。

如果A在B中,我想从A中删除所有数字。但是,如果一个数字在A中出现两次,在B中出现1次,那么它只会从A中删除1次出现的数字.

这是我的示例数据集:

df_A                df_B
[Test]              [Test]
1                   1
2                   2         
3                   5 
2                   5 
4
5
5

操作后我想要新的数据框c作为

df_C
[Test]
3
2
4

你能建议怎么做吗?

suggested duplicate 会删除 A 中的所有匹配项(如果存在于B),而不仅仅是前 N 个匹配项。

【问题讨论】:

标签: python pandas


【解决方案1】:

我可能会做点什么(窃取 SR 的设置):

dfA = pd.DataFrame({'A': [1, 2, 3, 2, 4, 5, 5]})
dfB = pd.DataFrame({'B': [1, 2, 5, 5]})

counts = dfA.groupby('A').cumcount()
limits = dfB['B'].value_counts().reindex(dfA.A).fillna(0).values
dfC = dfA.loc[counts >= limits]

这给了我

In [121]: dfC
Out[121]: 
   A
2  3
3  2
4  4

这通过使用 groupby 来获取 A 中给定值之前出现的次数:

In [124]: dfA.groupby('A').cumcount()
Out[124]: 
0    0
1    0
2    0
3    1
4    0
5    0
6    1
dtype: int64

并使用value_counts 获取限制,然后我们重新索引以匹配计数:

In [139]: dfB['B'].value_counts()
Out[139]: 
5    2
2    1
1    1
Name: B, dtype: int64

In [140]: dfB['B'].value_counts().reindex(dfA.A)
Out[140]: 
A
1    1.0
2    1.0
3    NaN
2    1.0
4    NaN
5    2.0
5    2.0
Name: B, dtype: float64

【讨论】:

    【解决方案2】:

    如果您创建一些包含每个值的出现次数的中间值,那么您可以使用pandas.Series.isin() 创建要排除数据帧的哪些行的逻辑索引,例如:

    代码:

    from collections import Counter
    
    def occurrences_number(column):
    
        def occurrence_number(value, accumulator):
            """ tuple of value and occurrence number of value """
            accumulator[value] += 1
            return value, accumulator[value]
    
        occurrences = Counter()
        return column.apply(lambda x: occurrence_number(x, occurrences))
    
    def find_not_in_by_occurrence_number(data, not_in):
        not_in_indices = ~occurrences_number(data).isin(occurrences_number(not_in))
        return data[not_in_indices].reset_index()
    

    测试代码:

    import pandas as pd
    
    dfA = pd.DataFrame({'A': [1, 2, 3, 2, 4, 5, 5]})
    dfB = pd.DataFrame({'B': [1, 2, 5, 5]})
    print(dfA)
    print(dfB)
    
    dfC = find_not_in_by_occurrence_number(dfA.A, dfB.B).A
    
    print (dfC)
    

    结果:

       A
    0  1
    1  2
    2  3
    3  2
    4  4
    5  5
    6  5
    
       B
    0  1
    1  2
    2  5
    3  5
    
    0    3
    1    2
    2    4
    Name: A, dtype: int64
    

    【讨论】:

      【解决方案3】:

      在这个问题中,你可以使用Counterdrop。对于 drop,您需要知道要丢弃的行的索引。

      import itertools
      from collections import Counter
      df = pd.DataFrame({'Test': {0: 1, 1: 2, 2: 3, 3: 2, 4: 4, 5: 5, 6: 5}})
      df2 = pd.DataFrame({'Test': {0: 1, 1: 2, 2: 5, 3: 5}})
      c_df2 = Counter(df2.Test)
      
      indexes_to_remove_2d = [df.index[df['Test'] == k].tolist()[:v] 
                              for k, v in c_df2.items()]
      # [[0], [1], [5, 6]]
      merged = list(itertools.chain(*indexes_to_remove_2d))
      # [0, 1, 5, 6]
      df.drop(merged)
      
          Test
      2   3
      3   2
      4   4
      

      indexes_2d 使用df.index[df['Test'] == k] 生成与计数器中的值k 匹配的索引,并具有[:v] 来限制我们从中获取的索引的大小。

      然后,我们将这些indexesitertools.chain 合并。最后删除带有这些索引的行。

      感谢 Stephan Ranch 指出订单问题。

      【讨论】:

      • @StephenRauch 是的!会强调它。
      猜你喜欢
      • 1970-01-01
      • 2019-02-04
      • 1970-01-01
      • 1970-01-01
      • 2016-11-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-13
      • 1970-01-01
      相关资源
      最近更新 更多