返回

Pandas中df.nunique()效率低怎么办?

python

如何突破 Pandas 中 df.nunique() 的效率瓶颈?

在数据分析领域,统计数据集中唯一值的个数是一项常见任务。Pandas 提供的 df.nunique() 方法为此类任务提供了便捷的解决方案。但随着数据量的增加,df.nunique() 的计算时间会线性增长,在处理大规模数据集时可能成为性能瓶颈,大幅拖慢分析效率。

本文将探讨如何突破这一瓶颈,并介绍几种更高效的替代方案,帮助你提升数据处理速度。

以一个包含五千万行数据的 DataFrame 为例,使用 df.nunique() 计算唯一值个数,耗时大约为 10 秒。当数据量翻倍时,执行时间也会相应地增加到 20 秒左右。

import numpy as np
import pandas as pd

def createList(r1, r2):
    """创建一个包含指定范围内数值的列表。
    例如:createList(1,3) == [1, 2, 3]
    """
    return np.arange(r1, r2+1, 1)

sample_df = pd.DataFrame(
data = {
    'a' : createList(1, 50_000_000),
    'b' : createList(1, 50_000_000),
    'c' : createList(1, 50_000_000),
    'd' : createList(1, 50_000_000),
    'e' : createList(1, 50_000_000),
    'f' : createList(1, 50_000_000),
    'g' : createList(1, 50_000_000),

}
)

# 使用%%timeit进行性能测试
%timeit sample_df.nunique()

为了提升效率,我们可以采用以下几种更高效的方法:

利用 numba 加速:

numba 是一个用于优化 Python 代码的库,它可以将 Python 函数编译成机器码,从而显著提高代码的执行速度。

from numba import jit

@jit(nopython=True)
def nunique_numba(df):
    result = np.zeros(len(df.columns), dtype=np.int64)
    for i in range(len(df.columns)):
        result[i] = len(np.unique(df.iloc[:, i]))
    return result

# 使用%%timeit进行性能测试
%timeit nunique_numba(sample_df)

通过使用 numba,我们可以将 df.nunique() 的执行速度提高数倍。

借助 set 数据结构:

Python 中的 set 数据结构可以高效地存储和查找唯一值。我们可以利用这一特性来计算唯一值的个数。

def nunique_set(df):
    result = []
    for col in df.columns:
        result.append(len(set(df[col])))
    return result

# 使用%%timeit进行性能测试
%timeit nunique_set(sample_df)

这种方法的效率略低于 numba,但仍然比 df.nunique() 快得多。

采用 dask 进行并行计算:

对于更大规模的数据集,我们可以考虑使用 dask 库进行并行计算。dask 可以将 DataFrame 分割成多个块,并在多个 CPU 核心上并行处理这些块,从而大幅度提高计算速度。

import dask.dataframe as dd

# 将Pandas DataFrame转换为Dask DataFrame
ddf = dd.from_pandas(sample_df, npartitions=4)

# 使用%%timeit进行性能测试
%timeit ddf.nunique().compute()

通过使用 dask,我们可以充分利用多核 CPU 的计算能力,进一步提升处理效率。

选择最优方案:

上述三种方法都能有效提高 df.nunique() 的执行效率,但最优方案需要根据具体的数据规模和硬件环境选择。

  • 如果你的数据集相对较小,numbaset 方法都是不错的选择;
  • 如果你的数据集非常庞大,dask 则是更理想的解决方案,能够充分发挥硬件性能。

常见问题解答:

  1. 为什么 df.nunique() 在大规模数据集上效率较低?

    df.nunique() 方法会遍历 DataFrame 的每一列,并统计唯一值的个数。这种计算方式在数据量较小时效率很高,但当数据量很大时,遍历操作会非常耗时。

  2. numbasetdask 三种方法各有哪些优缺点?

    • numba:优点是速度最快,缺点是需要安装额外的库,并且只适用于数值型数据。
    • set:优点是代码简洁,无需安装额外库,缺点是速度略慢于 numba
    • dask:优点是可以并行计算,适合处理超大规模数据集,缺点是需要安装额外库,并且代码相对复杂。
  3. 如何选择合适的 dask 分区数量?

    dask 分区数量应该根据数据量和 CPU 核心数进行调整。一般来说,分区数量应该大于等于 CPU 核心数,以充分利用 CPU 资源。

  4. 除了上述方法之外,还有哪些方法可以提高 df.nunique() 的效率?

    • 减少数据量:如果可以预先筛选数据,只保留必要的列和行,可以有效减少计算量。
    • 使用更高效的硬件:例如使用固态硬盘、增加内存容量等。
  5. 如何进一步优化 numbadask 的性能?

    • numba:可以使用 @jit(nopython=True) 装饰器,将 Python 代码编译成机器码,从而提高执行速度。
    • dask:可以使用 client.scheduler_info() 方法查看集群状态,并根据实际情况调整分区数量和 worker 数量。