【发布时间】:2017-06-20 14:24:34
【问题描述】:
我目前有一个基于 R 的算法,它按日期对 data.table 进行排序,然后找到最近的非 NA / 非空值。我在以下方面取得了一些成功 StackOverflow 问题对一些比较大的数据集实现回填算法:
Computing the first non-missing value from each column in a DataFrame
我已经在 Python 和 R 中实现了一个解决方案,但我的 Python 解决方案似乎运行得非常非常慢。
library(data.table)
library(microbenchmark)
test_values <- rnorm(100000)
test_values[sample(1:length(test_values), size = 10000)] <- NA
test_values_2 <- rnorm(100000)
test_values_2[sample(1:length(test_values), size = 10000)] <- NA
test_ids <- rpois(100000, lambda = 100)
random_timestamp <- sample(x = seq(as.Date('2000-01-01'), as.Date('2017-01-01'), by = 1), size = 100000, replace = TRUE)
dt <- data.table(
'id' = test_ids,
'date' = random_timestamp,
'v1' = test_values,
'v2' = test_values_2
)
# Simple functions for backfilling
backfillFunction <- function(vector) {
# find the vector class
colClass <- class(vector)
if (all(is.na(vector))) {
# return the NA of the same class as the vector
NA_val <- NA
class(NA_val) <- colClass
return(NA_val)
} else {
# return the first non-NA value
return(vector[min(which(!is.na(vector)))])
}
}
print(microbenchmark(
dt[order(-random_timestamp), lapply(.SD, backfillFunction), by = 'id', .SDcols = c('v1', 'v2')]
))
Unit: milliseconds
expr min lq
dt[order(-random_timestamp), c(lapply(.SD, backfillFunction), list(.N)), by = "id", .SDcols = c("v1", "v2")] 9.976708 12.29137
mean median uq max neval
15.4554 14.47858 16.75997 112.9467 100
还有 Python 解决方案:
import timeit
setup_statement = """
import numpy as np
import pandas as pd
import datetime
start_date = datetime.datetime(2000, 1, 1)
end_date = datetime.datetime(2017, 1, 1)
step = datetime.timedelta(days=1)
current_date = start_date
dates = []
while current_date < end_date:
dates.append(current_date)
current_date += step
date_vect = np.random.choice(dates, size=100000, replace=True)
test_values = np.random.normal(size=100000)
test_values_2 = np.random.normal(size=100000)
na_loc = [np.random.randint(0, 100000, size=10000)]
na_loc_2 = [np.random.randint(0, 100000, size=10000)]
id_vector = np.random.poisson(100, size=100000)
for i in na_loc:
test_values[i] = None
for i in na_loc_2:
test_values_2[i] = None
DT = pd.DataFrame(
data={
'id': id_vector,
'date': date_vect,
'v1': test_values,
'v2': test_values_2
}
)
GT = DT.sort_values(['id', 'date'], ascending=[1, 0]).groupby('id')
"""
print(timeit.timeit('{col: GT[col].apply(lambda series: series[series.first_valid_index()] if series.first_valid_index() else None) for col in DT.columns}', number=100, setup=setup_statement)*1000/100)
66.5085821699904
我在 Python 上的平均时间是 67 毫秒,但对于 R 来说只有 15 毫秒,尽管方法看起来比较相似(在组内的每一列上应用一个函数)。为什么我的 R 代码比我的 Python 代码快得多,如何在 Python 中实现类似的性能?
【问题讨论】:
-
分析您的代码。
-
你能避免两次调用
series.first_valid_index()吗? -
@Roland 注意到分析 - 我在 Python 代码上做过,但不确定如何加速慢速部分。
标签: python r pandas data.table