Python的列表推导式用于Numpy

3
我正在寻找一种在Numpy中使用列表推导方法或类似方法来消除使用for循环的方式。例如,index_values是一个Python字典列表,其中包含各种不同长度的索引值列表,而s是一个numpy向量:
for i in range(33):
    s[index_values[i]] += 4.1

有没有一种方法可以消除 for 循环?

我认为没有比使用Python的列表推导更通用的方法了...但是如果你正在做一些特定的事情,比如特定的数学运算,可能有其他方法。你想做什么? - Emil Stenström
2个回答

4

我不完全理解index_values是什么类型的对象。但如果它是一个ndarray,或者可以转换成一个ndarray,那么您只需这样做:

>>> s = numpy.arange(20)
>>> index_values = (numpy.random.random((3, 3)) * 20).astype('i')
>>> s[index_values] = 4
>>> s
array([ 0,  1,  4,  4,  4,  5,  6,  4,  8,  4,  4, 11, 12, 
       13,  4, 15,  4,  4,  4, 19])

编辑:但在这种情况下似乎行不通。根据您的编辑和评论,我想出了一种可能适合您的方法。一个随机的列表,其中包含长度不同的子列表...

>>> index_values = [list(range(x, x + random.randrange(1, 5)))
...                 for x in [random.randrange(0,50) for y in range(33)]]

把它转换成数组并不难:

>>> index_value_array = numpy.fromiter(itertools.chain(*index_values), 
                                       dtype='i')

如果您知道数组的长度,请指定 count 以获得更好的性能:

>>> index_value_array = numpy.fromiter(itertools.chain(*index_values), 
                                       dtype='i', count=83)

由于您的编辑表明您想要类似直方图的行为,因此简单的索引将不起作用,正如Robert Kern所指出的那样。所以请使用numpy.histogram

>>> hist = numpy.histogram(index_value_array, bins=range(0, 51))

直方图通常用于浮点数的统计,这意味着bin大小必须稍微大一些,因为最后一个值包含在最后一个bin中。因此,如果我们使用更直观的range(0, 50),则48和49将位于同一个bin中。结果是一个元组,其中包含一个长度为n的计数数组和一个长度为n + 1的bin边界数组:

>>> hist
(array([2, 2, 1, 2, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 5, 5, 5, 3, 3, 
        3, 3, 3, 2, 1, 0, 2, 3, 3, 1, 0, 2, 3, 2, 2, 2, 3, 2, 1, 1, 2, 2, 
        2, 0, 0, 0, 1, 0]), 
 array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
        17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
        34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50]))

现在我们可以按4.1的因子扩大计数,然后进行向量相加:
>>> s = numpy.arange(50, dtype='f')
>>> hist[0] * 4.1 + s
array([  8.2,   9.2,   6.1,  11.2,   8.1,   5. ,   6. ,   7. ,  12.1,
        13.1,  14.1,  15.1,  16.1,  13. ,  18.1,  19.1,  20.1,  37.5,
        38.5,  39.5,  32.3,  33.3,  34.3,  35.3,  36.3,  33.2,  30.1,
        27. ,  36.2,  41.3,  42.3,  35.1,  32. ,  41.2,  46.3,  43.2,
        44.2,  45.2,  50.3,  47.2,  44.1,  45.1,  50.2,  51.2,  52.2,
        45. ,  46. ,  47. ,  52.1,  49. ])

我不知道这是否符合您的需求,但这似乎是一个不错的方法,而且应该以接近 c 的速度运行,因为它只使用了 numpyitertools


这是numpy数组的正确答案。唯一需要注意的是将其扩展到增强赋值时要小心。当index_values中存在重复时,增强赋值不会像在完整的for循环中那样重复发生(原因比较复杂)。因此,您不能使用这种类型的索引来执行特定的直方图操作,尽管许多人尝试这样做。 - Robert Kern
index_values 是一个 Python 字典列表,例如 [[3, 6, 7], [5, 7, 11, 25, 99], [8, 45]]。 index_values 不能是 ndarray - 抱歉! - Henry Thornton
@dbv,我认为让我困惑的是“字典列表”。我不知道什么是“字典列表”。你是指一个简单的列表字典吗?如果是这样,为什么要使用整数来索引它呢?为什么不使用一个列表的列表呢?一个列表的列表可以传递给numpy.array,以生成相应形状的ndarray,如下所示:numpy.array([[1, 2, 3], [1, 2, 3]])。如果你必须使用一个字典,你至少可以通过以下方式获得一些速度:numpy.array([d[i] for i in range(2)]) - senderle
@dev 使用ndarray来索引s几乎肯定是正确的方法,让我们知道为什么您认为无法将index_values转换为ndarray,也许我们可以想出一个解决方案。此外,对于Robert Kern提出的问题,有一个相对简单的解决方法,如果在您的情况下相关,请告诉我,我会发布代码。 - Bi Rico
各位,我今天打算删除这个问题并重新制定一个新的问题。谢谢你们,希望在另一边见到你们。 - Henry Thornton
@dbv,这是一个合理的问题,没有人投票关闭它。我建议你不要删除它。如果你的实际问题非常不同,因此不会被视为重复,那么请创建一个新问题。如果不是很不同,请编辑您当前的问题。 - senderle

1

这个怎么样:

s[reduce(lambda x,y: x+y, [index_values[x] for x in range(33)], [])] = 4.1

请查看Tadeck的评论下面的翻译文本。谢谢! - Henry Thornton
在Tadeck下面?看不到任何Tadek :)(人们不断删除他们的答案,很难跟踪!) - Ricardo Cárdenes
是的,我刚看到@Tadeck的答案已经被删除了。我已经编辑了上面的原始问题,以显示Numpy向量s是就地更新的。 - Henry Thornton
当然。我对@Tadeck的回答发表评论是关于他对理解和过滤器的使用,而不是最终结果。当然,我明白你打算修改s的某些部分,而不是创建一个新数组 :) - Ricardo Cárdenes
reduce函数正在累加index_values[x]中的值。假设index_value[x] = [4, 7, 9, 11, 13],那么我们希望s[[4, 7, 9, 11, 13]] = s[[4, 7, 9, 11, 13]] + 4.1,即s[4] += 4.1,s[7] += 4.1等。对于x在range(33)范围内。 - Henry Thornton
啊,该死!好吧,一开始没有那个"+=",对吧?因为当然了,我写的代码不是你想要的效果! - Ricardo Cárdenes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接