【问题标题】:Why does random sampling scale with the dataset not the sample size? (pandas .sample() example)为什么随机抽样随数据集而不是样本大小进行缩放? (熊猫 .sample() 示例)
【发布时间】:2023-03-03 01:29:01
【问题描述】:

当从不同大小的分布中随机采样时,我惊讶地发现执行时间似乎主要与被采样的数据集的大小相关,而不是被采样的值的数量。示例:

import pandas as pd
import numpy as np
import time as tm

#generate a small and a large dataset
testSeriesSmall = pd.Series(np.random.randn(10000))
testSeriesLarge = pd.Series(np.random.randn(10000000))

sampleSize = 10
tStart = tm.time()
currSample = testSeriesLarge.sample(n=sampleSize).values
print('sample %d from %d values: %.5f s' % (sampleSize, len(testSeriesLarge), (tm.time() - tStart)))

tStart = tm.time()
currSample = testSeriesSmall.sample(n=sampleSize).values
print('sample %d from %d values: %.5f s' % (sampleSize, len(testSeriesSmall), (tm.time() - tStart)))

sampleSize = 1000
tStart = tm.time()
currSample = testSeriesLarge.sample(n=sampleSize).values
print('sample %d from %d values: %.5f s' % (sampleSize, len(testSeriesLarge), (tm.time() - tStart)))

tStart = tm.time()
currSample = testSeriesSmall.sample(n=sampleSize).values
print('sample %d from %d values: %.5f s' % (sampleSize, len(testSeriesSmall), (tm.time() - tStart)))

输出是:

sample 10 from 10000 values: 0.00126 s
sample 10 from 10000000 values: 1.10504 s
sample 1000 from 10000 values: 0.00122 s
sample 1000 from 10000000 values: 1.15000 s

这似乎违反直觉。也许我很密集,但这个问题似乎类似于生成一个随机索引列表,我预计采样值的数量很重要,而数据集的大小并不重要。我已经尝试了另一种或两种类似结果的实现,但我开始觉得我只是错过了一个基本问题。

我的问题有两个:(1)这是一个基本问题还是熊猫实施的一个怪癖? (2) 有没有一种更快的方法可以通过这种方式从大型数据集中随机采样?

【问题讨论】:

    标签: python pandas random sampling


    【解决方案1】:

    pandas.Series.sample() 在您的情况下归结为:

    rs = np.random.RandomState()
    locs = rs.choice(axis_length, size=n, replace=False)
    return self.take(locs)
    

    慢的部分是rs.choice():

    %timeit rs.choice(100000000, size=1, replace=False)
    1 loop, best of 3: 9.43 s per loop
    

    生成一个随机数大约需要10秒!如果将第一个参数除以 10,大约需要 1 秒。太慢了!

    如果您使用replace=True,它的速度非常快。如果您不介意结果中有重复条目,那么这是您的一种解决方法。

    choice(replace=False) 的 NumPy 文档说:

    这相当于 np.random.permutation(np.arange(5))[:3]

    这几乎可以解释问题 - 它会生成大量可能的值,将它们打乱,然后取第一个 N。这是性能问题的根本原因,并且已在 NumPy 中报告为问题 here :https://github.com/numpy/numpy/pull/5158

    这显然很难在 NumPy 中修复,因为当使用相同的随机种子值时,人们依赖 choice() 的结果不变(在 NumPy 版本之间)。

    由于您的用例非常狭窄,您可以这样做:

    def sample(series, n):
        locs = np.random.randint(0, len(series), n*2)
        locs = np.unique(locs)[:n]
        assert len(locs) == n, "sample() assumes n << len(series)"
        return series.take(locs)
    

    这会带来更快的时间:

    sample 10 from 10000 values: 0.00735 s
    sample 10 from 1000000 values: 0.00944 s
    sample 10 from 100000000 values: 1.44148 s
    sample 1000 from 10000 values: 0.00319 s
    sample 1000 from 1000000 values: 0.00802 s
    sample 1000 from 100000000 values: 0.01989 s
    sample 100000 from 1000000 values: 0.05178 s
    sample 100000 from 100000000 values: 0.93336 s
    

    【讨论】:

    【解决方案2】:

    这看起来是一个内部 numpy 问题。我相信熊猫sample 方法调用numpy.random.choice。让我们看看 numpy 在各种数组大小和样本大小下的表现。

    创建数组

    large = np.arange(1000000)
    small = np.arange(1000)
    

    在不替换的情况下对样本进行计时

    %timeit np.random.choice(large, 10, replace=False)
    10 loops, best of 3: 27.4 ms per loop
    
    %timeit np.random.choice(small, 10, replace=False)
    10000 loops, best of 3: 41.4 µs per loop
    

    用替换来计时

    %timeit np.random.choice(large, 10, replace=True)
    100000 loops, best of 3: 11.7 µs per loop
    
    %timeit np.random.choice(small, 10, replace=True)
    100000 loops, best of 3: 12.2 µs per loop
    

    非常有趣的是,在不进行替换的情况下进行采样时,大型阵列花费了将近 3 个数量级的时间,而它正好是 3 个数量级。这向我表明 numpy 正在对数组进行随机排序,然后取前 10 项。

    当有放回抽样时,每个值都是独立选择的,所以时间是相同的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-17
      • 2018-03-18
      • 2018-05-27
      • 1970-01-01
      • 2018-05-02
      相关资源
      最近更新 更多