【问题标题】:How to remove consecutive bad data points in Pandas如何在 Pandas 中删除连续的坏数据点
【发布时间】:2018-05-26 15:03:38
【问题描述】:

我有一个看起来像这样的 Pandas 数据框:

import pandas as pd
import numpy as np
df = pd.DataFrame({"Dummy_Var": [1]*12, 
                   "B": [6, 143.3, 143.3, 143.3, 3, 4, 93.9, 93.9, 93.9, 2, 2, 7],
                   "C": [4.1, 23.2, 23.2, 23.2, 4.3, 2.5, 7.8, 7.8, 2, 7, 7, 7]})


    B       C       Dummy_Var
0   6.0     4.1     1
1   143.3   23.2    1
2   143.3   23.2    1
3   143.3   23.2    1
4   3.0     4.3     1
5   4.0     2.5     1
6   93.9    7.8     1
7   93.9    7.8     1
8   93.9    2.0     1
9   2.0     7.0     1
10  2.0     7.0     1
11  7.0     7.0     1

当相同的数字连续出现 3 次或更多时,该数据应替换为 NAN。所以结果应该是:

    B       C       Dummy_Var
0   6.0     4.1     1
1   NaN     NaN     1
2   NaN     NaN     1
3   NaN     NaN     1
4   3.0     4.3     1
5   4.0     2.5     1
6   NaN     7.8     1
7   NaN     7.8     1
8   NaN     2.0     1
9   2.0     NaN     1
10  2.0     NaN     1
11  7.0     NaN     1

我已经写了一个函数来做到这一点:

def non_sense_remover(df, examined_columns, allowed_repeating):
    def count_each_group(grp, column):
        grp['Count'] = grp[column].count()
        return grp
    for col in examined_columns:
        sel = df.groupby((df[col] != df[col].shift(1)).cumsum()).apply(count_each_group, column=col)["Count"] > allowed_repeating
        df.loc[sel, col] = np.nan

    return df

df = non_sense_remover(df, ["B", "C"], 2)

但是,我的真实数据框有 200 万行和 18 列!在 2M 行上运行这个函数非常非常慢。有没有更有效的方法来做到这一点?我错过了什么吗?提前致谢。

【问题讨论】:

    标签: python pandas time-series sensors


    【解决方案1】:

    在这种情况下构造布尔掩码将比基于apply() 的解决方案高效得多,尤其是对于大型数据集。这是一种方法:

    cols = df[['B', 'C']]
    mask = (cols.shift(-1) == cols) & (cols.shift(1) == cols)
    
    df[mask | mask.shift(1).fillna(False) | mask.shift(-1).fillna(False)] = np.nan
    

    编辑:

    对于更通用的方法,将长度为 N 的序列替换为 NaN,您可以执行以下操作:

    from functools import reduce
    from operator import or_, and_
    
    def replace_sequential_duplicates_with_nan(df, N):
        mask = reduce(and_, [cols.shift(i) == cols.shift(i + 1)
                             for i in range(N - 1)])
        full_mask = reduce(or_, [mask.shift(-i).fillna(False)
                                 for i in range(N)])
        df[full_mask] = np.nan
    

    【讨论】:

    • 这是一个快速的解决方案。如果接受的限制是 9 怎么办?即连续 9 个连续点是可以的,但只要有 10 个或更多,我们就会将 NaN 分配给它们。我们可以概括这种方法吗?
    • 感谢您的编辑。这种方法需要 22 秒才能完成我的 2M 行数据帧(有 18 列)的任务。它非常优雅,我从中学到了很多。
    【解决方案2】:

    我们使用groupby + mask

    m=df[['B','C']]
    df[['B','C']]=m.mask(m.apply(lambda x : x.groupby(x.diff().ne(0).cumsum()).transform('count'))>2)
    df
    Out[1245]: 
          B    C  Dummy_Var
    0   6.0  4.1          1
    1   NaN  NaN          1
    2   NaN  NaN          1
    3   NaN  NaN          1
    4   3.0  4.3          1
    5   4.0  2.5          1
    6   NaN  7.8          1
    7   NaN  7.8          1
    8   NaN  2.0          1
    9   2.0  NaN          1
    10  2.0  NaN          1
    11  7.0  NaN          1
    

    【讨论】:

    • 这种方法需要 7 秒才能完成我的 2M 行数据帧(有 18 列)的任务。如果您能详细说明为什么我的初始功能如此缓慢,那就太好了。
    • @ahoosh for 循环是时间成本函数,如果您正在使用 pandas,请不要在此处使用 for 循环
    【解决方案3】:

    从这个link 看来,使用apply/transform(在你的情况下,apply)是这里最大的瓶颈。我引用的链接更详细地说明了这是为什么以及如何解决它

    【讨论】:

      猜你喜欢
      • 2021-04-30
      • 2020-10-02
      • 1970-01-01
      • 2012-10-09
      • 1970-01-01
      • 2018-03-14
      • 1970-01-01
      • 1970-01-01
      • 2020-12-14
      相关资源
      最近更新 更多