使用numpy降采样

3 投票
3 回答
2207 浏览
提问于 2025-04-18 05:27

我有一个一维数组A,这个数组表示的是分类数据(每个条目是某个类别的元素数量):

A = array([ 1, 8, 2, 5, 10, 32, 0, 0, 1, 0])

我想写一个函数sample(A, N),这个函数可以生成一个包含N个元素的数组B,这些元素是从A中随机抽取的(保持类别不变):

>>> sample(A, 20)
array([ 1, 3, 0, 1, 4, 11, 0, 0, 0, 0])

我写了这个:

def sample(A, N):
    AA = A.astype(float).copy()
    Z = zeros(A.shape)
    for _ in xrange(N):
        drawn = random.multinomial(1, AA/AA.sum())
        Z = Z + drawn
        AA = AA - drawn
    return Z.astype(int)

可能这个方法有点简单,有没有更好或更快的方法呢?也许可以用一些快速的numpy函数?
补充说明:必须是无放回抽样!!!

3 个回答

0

这是我会做的事情:

def sample(A, N):
        population = np.zeros(sum(A))
        counter = 0
        for i, x in enumerate(A):
                for j in range(x):
                        population[counter] = i
                        counter += 1

        sampling = population[np.random.randint(0, len(population), N)]
        return np.histogram(sampling, bins = np.arange(len(A)+1))[0]

我们要做的是根据直方图 A 来构建一个人群,然后从中随机抽样。如果现实情况中 N 很大,而 A 的总和又很小,或者你需要对固定的 A 多次抽样,这种方法应该会更好。你可以在函数调用外部构建与 A 对应的人群,然后把 sample(population, N) 定义为上面代码的最后两行。

2

这个方法可能不是最优雅的解决方案,但速度快了大约三倍。它使用了 numpy.random.choice,这个函数有一个布尔值的替换选项(在这里设置为 False,也就是说不重复选择)。接下来的代码是为了:

  • 设置选择的数组,这个数组包含了 A[n] 次数的索引 n,例如对于 A=[2,0,3,1],你会得到 choices=[0,0,2,2,2,3]。注意,这些选择的概率是相等的,所以不需要创建概率数组。
  • 把通过 numpy 函数调用选出的值转换成所需的输出数组。vals 数组中的每个元素都是从 choices 数组中选出的索引,因此你需要对每个选中的索引在 B 中相应的元素加 1。

希望这样解释能让你明白!下面是代码:

def sample_2(A, N):
    # Create array of choices (indicies)
    choices = []
    for n in xrange(len(A)):
        for _ in xrange(A[n]):
            choices.append(n)
    # Randomly choose from these indicies
    vals = numpy.random.choice(choices, N, False)
    # Count up the chosen indicies
    B = numpy.zeros(len(A), dtype=int)
    for index in xrange(N):
        B[vals[index]] += 1
    return B

对每个函数调用 10000 次的速度测试结果:

Original: 3.0517 s
Method_2: 0.9968 s
2

从我能看到的情况来看,这个方法比其他的快。不过,它可能会使用更多的内存。

import random 
from collections import Counter

def sample2(A,N):
    distribution = [i for i, j in enumerate(A) for _ in xrange(j)]
    sample = Counter(random.sample(distribution, N))
    return [sample[i] for i in xrange(len(A))]


In [52]: A = np.random.randint(0, 100, 500)

In [53]: %timeit sample(A, 100) #Original
100 loops, best of 3: 2.71 ms per loop

In [54]: %timeit sample2(A, 100) #my function
1000 loops, best of 3: 914 µs per loop

In [55]: %timeit sample3(A, 100) #sftd function
100 loops, best of 3: 8.33 ms per loop

撰写回答