【问题标题】:Down-sampling with numpy使用 numpy 进行下采样
【发布时间】:2014-05-05 19:11:36
【问题描述】:

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

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

我正在尝试编写一个函数 sample(A, N) 来生成一个数组 B ,其中包含通过从 A 中随机抽取元素生成的 N 个元素(保持类别):

>>> 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 函数? 编辑:不清楚:它必须没有替换!!!

【问题讨论】:

  • 也许我在这里完全错了,但是你有没有理由不只是使用 random.multinomial(N, A/A.sum()) 而不是你的整个功能?
  • 保留类别到底是什么意思?另外,看起来您正在绘制 没有 替换?
  • 您的代码看起来不能以这种方式工作。我假设您想用Z[drawn] += 1 替换Z = Z + drawn
  • @eickenberg Z[drawn] += 1 会将 1 添加到 Z 的第一个和第二个元素。Z = Z + drawn 工作正常。 Z[drawn == 1] += 1 也可以使用
  • 啊,真的,我的错——我认为它会返回索引。忘记我最后的评论

标签: python numpy statistics scipy sampling


【解决方案1】:

据我所知,比其他人更快。但可能会占用更多内存。

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

【讨论】:

  • 对我不起作用...我收到错误消息! random.sample 是 numpy.random.random_sample 函数吗?
  • 不,它来自标准库中的随机数。这是(众多)原因之一,为什么你应该避免from numpy import *
  • 非常快!这就是我一直在寻找的解决方案!!!还有一件事:有没有办法使用惰性生成器而不是distribution = [i for i, j in enumerate(A) for _ in xrange(j)] 来进一步优化内存?
  • @Gioelelm 据我所知,我尝试使用生成器表达式进行分发。但是random.sample 似乎无法将生成器作为输入。
【解决方案2】:

这可能不是最优雅的解决方案,但它的速度大约是原来的 3 倍。它使用numpy.random.choice,它有一个布尔替换选项(在这种情况下设置为False - 即没有替换)。剩下的代码是:

  • 设置选择数组,其中包含索引nA[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

【讨论】:

  • 类似于我的解决方案!如果您将第二个循环换成np.histogram,会有帮助吗?
  • 这个只对小A比较快。试试和A = random.randint(0, 100, 3000)比较
  • 是的,你是对的。似乎 M4rtini 对于更大的阵列可能有更好的解决方案。
【解决方案3】:

我会这样做:

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 大且 sum(A) 小,和/或您需要对 A 多次采样以获得固定 A,这应该会更好。您要做的是在函数调用之外构建与 A 相对应的人口,并将 sample(population, N) 定义为上面的最后两行。

【讨论】:

  • 另一个问题是,如果现实世界 A 中的数字很大,那么就内存而言,您的人口将变得过于昂贵。请注意。
  • 这个函数不符合我的要求。例如,如果我做A = array([1,8,2,5,10,32,0,0,1,0])sample(A, 20) 我可以得到:array([ 2, 6, 0, 1, 0, 11, 0, 0, 0, 0]) 看看第一个元素!!!它已创建
  • 那么你需要更清楚你想要什么。我从我的电话中得到array([ 0 4 0 3 2 11 0 0 0 0]),以随机种子为模,这似乎与请求的行为相同。
  • 那你是要求抽样不换吗???这是问题的关键部分。
  • 然后用np.random.choice切换np.random.randint,就大功告成了。
猜你喜欢
  • 2022-10-03
  • 1970-01-01
  • 2013-12-17
  • 1970-01-01
  • 2019-04-17
  • 1970-01-01
  • 1970-01-01
  • 2015-10-27
  • 2017-12-19
相关资源
最近更新 更多