我已经提供了一个样本数据集,并希望从原始样本数据集中选择多个样本,例如1000个样本块,每个样本块包含来自原始样本数据的500个数据点。我在python中编写了这个小函数:
import timeit
import pandas as pd
import numpy as np
sample_data = np.random.randn(10000, 15)
index = pd.date_range("20000101", periods=10000, freq='B')
sample_data_df = pd.DataFrame(sample_data, index=index)
def f(n, sample_data_df, f):
s = (1+sample_data_df).resample(f, axis=0)
r = s.prod()-1
out = r.sample(n, replace=True)
# out_index = pd.date_range(start=sample_data_df.index[0],
# periods=len(out.index),
# freq=f)
# out.index = output_index
return out
start_time = timeit.default_timer()
N = 1000
a = [f(500, sample_data_df, 'BM') for i in range(N)]
elapsed = timeit.default_timer() - start_time
print(elapsed)
如果我运行此代码,则需要35.8964748383秒。但是,我想在每个块上附加一个索引,我将取消注释函数中的行,即
def f(n, sample_data_df, f):
s = (1+sample_data_df).resample(f, axis=0)
r = s.prod()-1
out = r.sample(n, replace=True)
out_index = pd.date_range(start=sample_data_df.index[0],
periods=len(out.index),
freq=f)
out.index = output_index
return out
现在该功能需要 72.2418179512 。疯了吧。如果需要在每个输出中都有这样的索引,我怎样才能加快这个速度?我知道生成一次索引并将其随后附加到每个输出。但是,我想在其他情况下使用该函数,以便在索引的分配在函数内完成时将非常感激。
此外,除了索引还有其他来源可以提高速度吗?因为即使没有索引35.8964748383也是很长时间。
答案 0 :(得分:3)
编辑:
问题不在于重新采样或索引的速度,如果我们看一下时机:
%timeit (1+sample_data_df).resample('BM', axis=0).prod()-1
21.7 ms ± 170 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit pd.date_range(start="20000101", periods=500, freq='BM')
21.4 ms ± 272 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
考虑到我们正在对150'000元素进行重新采样和减少,22毫秒对我来说似乎并不坏。
你的问题来自1000,在你的情况下没有必要(因为你做的完全相同)。 如果要在函数中保留重新采样,可以执行的操作是缓存重新采样的结果。不幸的是,缓存函数结果(lru_cache)的标准方法是无法处理可变对象(如dfs,lists ......)。所以我的解决方案是将重新采样包装在一个函数中,该函数创建哈希并使用哈希作为参数调用实际函数:
from functools import lru_cache
class Sampler():
def __init__(self, df):
self.df = df
def get_resampled_sample(self, n, freq):
resampled = self._wraper_resample_prod(freq)
return resampled.sample(n, replace=True)
def _wraper_resample_prod(self, freq):
hash_df = hash(self.df.values.tobytes())
return self._resample_prod(hash_df, freq)
@lru_cache(maxsize=1)
def _resample_prod(self, hash_df, freq):
return (self.df+1).resample(freq, axis=0).prod()-1
现在,只要不更改df值的哈希值,就会缓存重新采样的结果。这意味着我们可以更快地采样。
%timeit [sampler.get_resampled_sample(500, 'BM') for i in range(1000)]
881 ms ± 10.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
您可以对索引执行相同的操作,但是在这里您不需要创建自定义哈希,因为pd.date_range的所有参数都是不可变对象。
class Sampler():
def __init__(self, df):
self.df = df
def update_df(self, df):
self.df = df
def get_resampled_sample(self, n, freq):
resampled = self._wraper_resample_prod(freq)
df = resampled.sample(n, replace=True)
df.index = self._create_date_range(self.df.index[0], n, freq)
return df
def _wraper_resample_prod(self, freq):
hash_df = hash(self.df.values.tobytes())
return self._resample_prod(hash_df, freq)
@lru_cache(maxsize=1)
def _resample_prod(self, hash_df, freq):
return (self.df+1).resample(freq, axis=0).prod()-1
@lru_cache(maxsize=1)
def _create_date_range(self, start, periods, freq):
return pd.date_range(start=start, periods=periods, freq=freq)
时序:
%timeit [sampler.get_resampled_sample(500, 'BM') for i in range(1000)]
1.11 s ± 43.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)