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

时间:2017-03-25 01:21:37

标签: python pandas random sampling

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

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)以这种方式从大型数据集中随机抽样是否有明显更快的方法?

2 个答案:

答案 0 :(得分:7)

在您的情况下,

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中报告为问题: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

答案 1 :(得分:4)

这看起来像是一个内部的问题。我相信pandas 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个数量级,而且大到正好三个数量级。这告诉我,numpy随机排序数组,然后取前10项。

当替换采样时,每个值都是独立选择的,因此时间相同。