在pandas数据框中替换少见值

12 投票
2 回答
5102 浏览
提问于 2025-04-17 20:59

我正在用pandas处理一个中等大小的csv文件,大约有60,000行和15列。每一行代表一个人,里面包含个人数据。我想让这些数据变得匿名。其中一种方法是替换某一列中那些比较少见的值。我最开始是这样尝试的:

def clean_data(entry):
    if df[df.column_name == entry].index.size < 10:
        return 'RARE_VALUE'
    else:
        return entry

df.new_column_name = df.column_name.apply(clean_data)

但是每次运行这个代码都会导致我的系统卡住。这让我没有办法得到有用的调试信息。有没有人知道正确的做法是什么?这一列里有字符串和空值。

2 个回答

3

@Andy Hayden 提出了几种解决这个问题的方法。不过,我建议你使用 管道(pipelines) 来处理这类任务。虽然下面的做法看起来可能有点复杂,但如果你想把整个流程保存为一个对象,或者需要在测试集上进行更一般化的预测,这种方法会很有用:

class RemoveScarceValuesFeatureEngineer:

    def __init__(self, min_occurences):
        self._min_occurences = min_occurences
        self._column_value_counts = {}

    def fit(self, X, y):
        for column in X.columns:
            self._column_value_counts[column] = X[column].value_counts()
        return self

    def transform(self, X):
        for column in X.columns:
            X.loc[self._column_value_counts[column][X[column]].values
                  < self._min_occurences, column] = "RARE_VALUE"

        return X

    def fit_transform(self, X, y):
        self.fit(X, y)
        return self.transform(X)

你可以在这里找到更多信息: Pandas 替换稀有值

12

我觉得你想要按列名进行分组:

g = df.groupby('column_name')

你可以使用过滤器,比如说,只返回那些在 column_name 列中出现超过10次的行:

g.filter(lambda x: len(x) >= 10)

如果你想把某一列的值改成 'RARE_VALUE',可以使用 transform(这个方法会对每个分组计算一次结果,然后把结果分配到合适的位置):

df.loc[g[col].transform(lambda x: len(x) < 10).astype(bool), col] = 'RARE_VALUE'

正如 DSM 指出的,下面这个技巧快得多

df.loc[df[col].value_counts()[df[col]].values < 10, col] = "RARE_VALUE"

这里有一些 timeit 的信息(用来展示 DSM 的解决方案有多厉害!):

In [21]: g = pd.DataFrame(np.random.randint(1, 100, (1000, 2))).groupby(0)

In [22]: %timeit g.filter(lambda x: len(x) >= 10)
10 loops, best of 3: 67.2 ms per loop

In [23]: %timeit df.loc[g[1].transform(lambda x: len(x) < 10).values.astype(bool), 1]
10 loops, best of 3: 44.6 ms per loop

In [24]: %timeit df.loc[df[1].value_counts()[df[1]].values < 10, 1]
1000 loops, best of 3: 1.57 ms per loop

撰写回答