【问题标题】:Why is R's data.table so much faster than pandas?为什么 R 的 data.table 比 pandas 快这么多?
【发布时间】:2018-08-25 14:19:51
【问题描述】:

我有一个 1200 万行数据集,其中 3 列作为唯一标识符,另外 2 列带有值。我正在尝试做一个相当简单的任务:
- 按三个标识符分组。这会产生大约 260 万个独特的组合
- 任务 1:计算列 Val1
的中位数 - 任务 2:在给定 Val2 的某些条件下计算列 Val1 的平均值

这是我的结果,使用pandasdata.table(目前都是最新版本,在同一台机器上):

+-----------------+-----------------+------------+
|                 |      pandas     | data.table |
+-----------------+-----------------+------------+
| TASK 1          | 150 seconds     | 4 seconds  |
| TASK 1 + TASK 2 |  doesn't finish | 5 seconds  |
+-----------------+-----------------+------------+

我认为我可能对 pandas 做错了 - 将 Grp1Grp2 转换为类别并没有太大帮助,在 .agg.apply 之间切换也没有太大帮助。有什么想法吗?

以下是可重现的代码。
数据框生成:

import numpy as np
import pandas as pd
from collections import OrderedDict
import time

np.random.seed(123)
list1 = list(pd.util.testing.rands_array(10, 750))
list2 = list(pd.util.testing.rands_array(10, 700))
list3 = list(np.random.randint(100000,200000,5))

N = 12 * 10**6 # please make sure you have enough RAM
df = pd.DataFrame({'Grp1': np.random.choice(list1, N, replace = True),
                   'Grp2': np.random.choice(list2, N, replace = True),
                   'Grp3': np.random.choice(list3, N, replace = True),
                   'Val1': np.random.randint(0,100,N),
                   'Val2': np.random.randint(0,10,N)}) 


# this works and shows there are 2,625,000 unique combinations
df_test = df.groupby(['Grp1','Grp2','Grp3']).size()
print(df_test.shape[0]) # 2,625,000 rows

# export to feather so that same df goes into R
df.to_feather('file.feather')

Python 中的任务 1:

# TASK 1: 150 seconds (sorted / not sorted doesn't seem to matter)
df.sort_values(['Grp1','Grp2','Grp3'], inplace = True)
t0 = time.time()
df_agg1 = df.groupby(['Grp1','Grp2','Grp3']).agg({'Val1':[np.median]})
t1 = time.time()
print("Duration for complex: %s seconds ---" % (t1 - t0))

Python 中的任务 1 + 任务 2:

# TASK 1 + TASK 2: this kept running for 10 minutes to no avail
# (sorted / not sorted doesn't seem to matter)
def f(x):
    d = OrderedDict()
    d['Median_all'] = np.median(x['Val1'])
    d['Median_lt_5'] = np.median(x['Val1'][x['Val2'] < 5])
    return pd.Series(d)

t0 = time.time()
df_agg2 = df.groupby(['Grp1','Grp2','Grp3']).apply(f)
t1 = time.time()
print("Duration for complex: %s seconds ---" % (t1 - t0)) # didn't complete

等效的R代码:

library(data.table)
library(feather)

DT = setDT(feater("file.feather"))
system.time({
DT_agg <- DT[,.(Median_all = median(Val1),
                Median_lt_5 = median(Val1[Val2 < 5])  ), by = c('Grp1','Grp2','Grp3')]
}) # 5 seconds

【问题讨论】:

  • 使用函数:%timeit在python中做计时
  • data.table 在您在列上设置键时进行排序。对排序列的操作通常更快。
  • @Wen DT = setDT(feater("file.feather")) 不相关,数据帧来自SQL数据库,SQL提取时间大致相同。我使用feather 只是为了确保 R 和 Python 都处理完全相同的数据,并且该示例是可重现的。稍后我将使用%timeit 更新帖子,但我认为这并不重要 - 处理时间真的很长,有时甚至没有完成
  • @SergeyBushmanov 对数据框进行排序后,python 中的性能并没有提高。我更新了我的代码以反映这一点。
  • "为什么 R 的 data.table 比 pandas 快这么多?"因为马修·道尔很聪明。 data.table 中的众多优化之一是,当使用 by 语句时,内存只分配给最大的组并重复使用。这为那些需要为每个组分配和释放内存的任务节省了大量时间。

标签: r pandas data.table


【解决方案1】:

我无法重现你的 R 结果,我修正了你拼错羽毛的错字,但我得到以下信息:

Error in `[.data.table`(DT, , .(Median_all = median(Val1), Median_lt_5 = median(Val1[Val2 <  : 
column or expression 1 of 'by' or 'keyby' is type NULL. Do not quote column names. Usage: DT[,sum(colC),by=list(colA,month(colB))] 

对于python的例子,如果你想得到val2小于5的每个组的中位数,那么你应该先过滤,如:

 df[df.Val2 < 5].groupby(['Grp1','Grp2','Grp3'])['Val2'].median()

在我的 macbook pro 上,这在 8 秒内完成。

【讨论】:

  • 我不确定为什么 R 代码会抛出该错误,仔细查看它似乎很好 - 我今天晚些时候会实际运行它,也许我在复制粘贴时犯了错误。至于您建议的 Python 代码 - 这很好,但目的是一步完成。在不同的过滤器Val2&lt;10Val2&lt;15 等上还需要其他几个聚合,并且代码可能难以阅读。在这种情况下,data.table 的另一个优点
  • 所以我认为在这种情况下,您的问题的真正答案是 Python 速度较慢,因为 df.groupby() 调用必须先创建组(全部 250 万个),然后才能调用 apply -- 毕竟它只是在评估一个 Python 表达式。而(我在这里猜测)data.table 将这两个操作作为参数,并且能够优化顺序,因此它首先进行过滤,然后进行聚合,这很有意义,并使组的数量更小。我很想知道是否可以在 Pandas 中找到更快的替代方案。
猜你喜欢
  • 2012-10-08
  • 2014-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-17
  • 2020-01-26
  • 2014-04-11
相关资源
最近更新 更多