为什么随机抽样与数据集一起缩放而不是样本量?(pandas .sample()示例)

c_l*_*ton 8 python random sampling pandas

当从不同大小的分布中随机抽样时,我惊讶地发现执行时间似乎主要是根据从中采样的数据集的大小而不是被采样的值的数量来缩放.例:

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)))
Run Code Online (Sandbox Code Playgroud)

输出是:

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
Run Code Online (Sandbox Code Playgroud)

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

我的问题有两个:(1)这是大熊猫的根本问题还是实施的怪癖?(2)以这种方式从大型数据集中随机抽样是否有明显更快的方法?

Joh*_*nck 7

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

rs = np.random.RandomState()
locs = rs.choice(axis_length, size=n, replace=False)
return self.take(locs)
Run Code Online (Sandbox Code Playgroud)

缓慢的部分是rs.choice():

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

生成一个随机数大约需要10秒钟!如果将第一个参数除以10,则大约需要1秒.那很慢!

如果你使用replace=True它超级快.如果你不介意在结果中有重复的条目,那么这就是你的一种解决方法.

NumPy文档choice(replace=False)说:

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

这几乎解释了这个问题 - 它会生成大量可能的值,将它们混洗,然后取第一个N.这是导致性能问题的根本原因,并且已在NumPy中报告为问题: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)
Run Code Online (Sandbox Code Playgroud)

这提供了更快的时间:

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
Run Code Online (Sandbox Code Playgroud)