微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

python – pandas range_date极其缓慢地降低了功能

我给出了一个样本数据集,并希望从原始样本数据集中选择多个样本,例如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也是很长时间.

解决方法:

编辑:

>添加了创建新日期索引的时间
>添加了缓存函数以创建新索引

问题不在于重新采样或索引的速度,如果我们看一下时间:

%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 ms对我来说似乎并不坏.

你的问题来自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)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐