【问题标题】:Faster alternatives to Pandas pivot_tablePandas pivot_table 的更快替代品
【发布时间】:2019-08-19 15:19:15
【问题描述】:

我在大型数据集(1000 万行,6 列)上使用 Pandas pivot_table 函数。由于执行时间是最重要的,我试图加快这个过程。目前处理整个数据集大约需要 8 秒,这太慢了,我希望找到提高速度/性能的替代方法。

我当前的 Pandas 数据透视表:

df_pivot = df_original.pivot_table(index="industry", columns = "months",
                    values = ["orders", "client_name"],
                    aggfunc ={"orders": np.sum, "client_name": pd.Series.nunique})

df_original 包括所有数据(10m 行,从 csv 导入)。行业是客户的行业,月份是订单月份(1 月到 12 月),订单是订单数量。除订单数(int 数据类型)外,所有数据都转换为 categorical 数据。最初行业、月份和客户名称是字符串。

我尝试使用pandas.DataFrame.unstack - 速度更慢。我还尝试了Daskdask pivot_table 产生了一些改进(6 秒的执行时间 - 所以少了 2 秒)。但是,它仍然很慢。 有没有更快的替代方案(对于大型数据集)?也许用groupycrosstab、... 重新创建数据透视表...不幸的是,我根本没有其他方法可以工作,而且我对 Python 和 Pandas 还是很陌生... 期待您的建议。提前致谢!

更新:

我通过以下方式找到了分组方式:

df_new = df_original.groupby(["months", "industry"]).agg({"orders": np.sum, "client_name": pd.Series.nunique}).unstack(level="months").fillna(0)

现在速度要快得多,大约需要 2-3 秒。还有一些选项可以进一步提高速度吗?

【问题讨论】:

  • 你的执行环境是什么?多核、集群还是单台机器?大 RAM,还是大量快速磁盘访问?性能调优意味着知道您有哪些资源可供您使用。
  • 你是对的,对不起!!单机,16GB RAM,8 核(i7-8650U CPU @ 1.90GHz)。我在我的 IDE (Visual Studio Code) 中运行代码。理想情况下,我的模块也应该在 RAM 较少(例如 8GB)和 CPU 功率较少的机器上“快速”运行......
  • OK下一个问题,数据是有序的吗?即您能否依靠要在数据透视中创建的拆分来反映在基础数据中?通过计时一些相对简单的函数来找到期望的下限也很有趣,只是为了看看最好的结果可能是什么样子。例如在不进行任何额外处理的情况下,将文件的每一行读入内存需要多长时间?
  • 数据(即orders 的数量)未排序。应该是吗?它对性能有何帮助?我首先转换 csv 文件,然后将其转换为 HDF5 格式,以便在下次使用数据集时更快地加载。我不太担心将文件的每一行读入内存。也许我误解了你的问题......
  • 我找到了groupby 的方式:df_new = df_original.groupby(["months", "industry"]).agg({"orders": np.sum, "client_name": pd.Series.nunique}).unstack(level="months").fillna(0) 现在速度快了大约 2 秒。还有一些选项可以进一步提高速度吗?

标签: python pandas performance numpy dask


【解决方案1】:

将月份和行业列转换为分类列: https://pandas.pydata.org/pandas-docs/stable/user_guide/categorical.html 这样可以避免大量的字符串比较。

【讨论】:

  • 谢谢,我已经这样做了,执行时间大约是 8 秒。所有数据之前都转换为分类列,例如df_original["industry"] = df_original["industry"].astype("category")
  • 有什么想法为什么我在这样做之后出现 TypeError 然后 .reset_index() ?
【解决方案2】:

当您将 csv 文件读入 df 时,您可以传递一个转换函数(通过read_csv 参数converters),将client_name 转换为哈希并将orders 向下转换为适当的int输入,特别是一个无符号的。

此函数列出类型及其范围:

import numpy as np

def list_np_types():
    for k, v in np.sctypes.items():
        for i, d in enumerate(v):
            if np.dtype(d).kind in 'iu':
                # only int and uint have a definite range
                fmt = '{:>7}, {:>2}: {:>26}  From: {:>20}\tTo: {}'
                print(fmt.format(k, i, str(d),
                                 str(np.iinfo(d).min),
                                 str(np.iinfo(d).max)))

            else:
                print('{:>7}, {:>2}: {:>26}'.format(k, i, str(d)))


list_np_types()

输出:

    int,  0:       <class 'numpy.int8'>  From:                 -128 To: 127
    int,  1:      <class 'numpy.int16'>  From:               -32768 To: 32767
    int,  2:      <class 'numpy.int32'>  From:          -2147483648 To: 2147483647
    int,  3:      <class 'numpy.int64'>  From: -9223372036854775808 To: 9223372036854775807
   uint,  0:      <class 'numpy.uint8'>  From:                    0 To: 255
   uint,  1:     <class 'numpy.uint16'>  From:                    0 To: 65535
   uint,  2:     <class 'numpy.uint32'>  From:                    0 To: 4294967295
   uint,  3:     <class 'numpy.uint64'>  From:                    0 To: 18446744073709551615
  float,  0:    <class 'numpy.float16'>
  float,  1:    <class 'numpy.float32'>
  float,  2:    <class 'numpy.float64'>
complex,  0:  <class 'numpy.complex64'>
complex,  1: <class 'numpy.complex128'>
 others,  0:             <class 'bool'>
 others,  1:           <class 'object'>
 others,  2:            <class 'bytes'>
 others,  3:              <class 'str'>
 others,  4:       <class 'numpy.void'>

【讨论】:

  • 感谢您的建议。对uint 的失望会提高速度吗?将client_name 转换为哈希是什么意思?这背后的想法/改进是什么?
  • 我对@9​​87654332@ 的不好:它已经很明确了。要向下转换orders,您需要知道最大值或上限是多少;我的猜测是你不应该需要unit64(熊猫默认)。请参阅this 帖子。这个想法是,如果您希望您的代码在只有一半 RAM 的机器上运行得一样快,这应该会有所帮助。
  • 鉴于 OP 对我表示感谢,负分是超级陌生的。
【解决方案3】:

您可以使用稀疏矩阵。它们实施起来很快,但有点受限制。例如:您不能对 COO_matrix 进行索引

我最近需要训练一个推荐系统 (lightFM),它接受稀疏矩阵作为输入,这让我的工作轻松多了。看看它的实际效果:

row  = np.array([0, 3, 1, 0])
col = np.array([0, 3, 1, 2])
data = np.array([4, 5, 7, 9])
mat = sparse.coo_matrix((data, (row, col)), shape=(4, 4))
>>> print(mat)
  (0, 0)    4
  (3, 3)    5
  (1, 1)    7
  (0, 2)    9
>>> print(mat.toarray())
[[4 0 9 0]
 [0 7 0 0]
 [0 0 0 0]
 [0 0 0 5]]

如您所见,它会使用您拥有的数据的列和行自动为您创建一个数据透视表,并用零填充其余部分。您也可以将稀疏矩阵转换为数组和数据帧 (df = pd.DataFrame.sparse.from_spmatrix(mat, index=..., columns=...))

【讨论】:

    猜你喜欢
    • 2020-06-03
    • 1970-01-01
    • 2022-01-25
    • 1970-01-01
    • 2014-07-19
    • 2012-03-21
    • 2022-10-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多