【问题标题】:Python performance of native data container vs Pandas DataFrame本机数据容器与 Pandas DataFrame 的 Python 性能
【发布时间】:2019-08-12 17:10:57
【问题描述】:

我想知道是否有人可以提供有关 python 的本机数据容器与 Pandas DataFrame 的速度/性能的任何输入 - 即执行子字符串查找。

几个月前,我发布了一个与操作有关的问题 (Performing a substring lookup between one dataframe and another)。本质上,我有一个名称列表(DataFrame 中的一列,长度> 2mm),并且想“标记”那些包含来自单独的粗俗单词列表(长度> 3000)的子字符串的名称。呈现给我的解决方案效果很好,我认为它是 DataFrame 最有效的选择。

然而,从那时起,我开始创建一个包含进度条的 GUI(使用 PyQt5)。进度条的问题是我需要某种形式的迭代来确定完成的进度百分比。此时,我将代码更改为仅使用本机 python 可迭代对象(没有 Pandas DataFrame),并在 forloop 中进行操作,让我有一个确定的进度条。

我认为这会慢得多,因为知道 DataFrame 的性能优势来自于向量化操作的能力。然而,令我惊讶的是,使用 python 的迭代方法约为 15%。

这是什么原因? pandas 方法是否没有真正矢量化,并且仍在幕后执行一些循环?或者与 DataFrame 相比,列表/集合/生成器是否更轻量级和更快?


这是我的两种方法的代码:

Pandas DataFrame 实现

import pandas as pd

df = pd.read_csv(source_file, names = ['ID', 'Fullname'])

vulgars = [line for line in open(vulgar_lookup_file, 'r')]

df['Vulgar Flag'] = df['Fullname'].str.contains('|'.join(vulgars))

原生 Python 迭代方法

vulgars = set(line for line in open(vulgar_lookup_file, 'r'))

# accessing second column of comma-delimited file (containing the fullname)
source = (line.split(',')[1] for line in open(source_file, 'r'))

vulgar_flag = []
for item in source:
    result = any(substr in item for substr in vulgars)
    vulgar_flag.append(result)

我知道迭代方法可以进一步简化为列表推导,它产生相同的结果比上面的 forloop 快约 12%。为了便于阅读,我只是把它放在循环形式中。

谢谢!

【问题讨论】:

  • “使用 python 的迭代方法约为 15%” 你能澄清一下迭代方法比数据框方法快 15% 还是慢 15%?
  • @SidaZhou 我的意思是它快了 15%

标签: python pandas loops dataframe iterator


【解决方案1】:

长话短说,不,str 方法没有向量化。

如果我们查看pandas代码,我们可以发现str方法最终委托给pandas._lib.lib.map_infer,其定义如下:

def map_infer(ndarray arr, object f, bint convert=1):
    """
    Substitute for np.vectorize with pandas-friendly dtype inference
    Parameters
    ----------
    arr : ndarray
    f : function
    Returns
    -------
    mapped : ndarray
    """
    cdef:
        Py_ssize_t i, n
        ndarray[object] result
        object val

    n = len(arr)
    result = np.empty(n, dtype=object)
    for i in range(n):
        val = f(arr[i])

        if cnp.PyArray_IsZeroDim(val):
            # unbox 0-dim arrays, GH#690
            # TODO: is there a faster way to unbox?
            #   item_from_zerodim?
            val = val.item()

        result[i] = val

    if convert:
        return maybe_convert_objects(result,
                                     try_float=0,
                                     convert_datetime=0,
                                     convert_timedelta=0)

    return result

我们可以看到它基本上是一个for 循环,尽管在 Cython 中是为了速度。

【讨论】:

  • 这太好了,谢谢!我想出于我的目的(基本上只比较两组数据),使用 DataFrame 无论如何都不会为我提供任何额外的功能。我会继续使用原生的python
猜你喜欢
  • 2021-04-14
  • 2019-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-14
相关资源
最近更新 更多