如何在cython(或numba)中迭代列表?

2024-05-26 16:29:10 发布

您现在位置:Python中文网/ 问答频道 /正文

我想要一个函数,它接收一个列表列表作为参数,每个子列表具有不同的大小,并且可以迭代每个子列表(包含整数),将它们作为广播传递给numpy数组并执行不同的操作(如平均值)

让我举一个不使用cython的预期行为的简单示例:

import numpy as np

mask = [[0, 1, 2, 4, 6, 7, 8, 9],
        [0, 1, 2, 4, 6, 7, 8, 9],
        [0, 1, 2, 4, 6, 9],
        [3, 5, 8],
        [0, 1, 2, 4, 6, 7, 8, 9],
        [3, 5, 7],
        [0, 1, 2, 4, 6, 9],
        [0, 1, 4, 5, 7, 8, 9],
        [0, 1, 3, 4, 7, 8, 9],
        [0, 1, 2, 4, 6, 7, 8, 9]] # This is the list of lists

x = np.array([2.0660689 , 2.08599832, 0.45032649, 1.05435649, 2.06010132,
              1.07633407, 0.43014785, 1.54286467, 1.644388  , 2.15417444])

def nocython(mask, x):
    out = np.empty(len(x), dtype=np.float64)
    for i, v in enumerate(mask):
        out[i] = x[v].mean()
    return out

>>> nocython(mask, x)
array([1.55425875, 1.55425875, 1.54113622, 1.25835952, 1.55425875,
       1.22451841, 1.54113622, 1.80427567, 1.80113602, 1.55425875])

主要的问题是,我必须处理更大的numpy数组和掩码列表,而在Python中,循环变得非常低效。所以我想知道如何将这个函数进行cythonize(或numbaize)。大概是这样的:

%%cython

import numpy as np
cimport numpy as np

cdef np.ndarray[np.float64_t] cythonloop(int[:,:] mask, np.ndarray[np.float64_t] x):
    cdef Py_ssize_t i
    cdef Py_ssize_t N = len(x)
    cdef np.ndarray[np.float64_t] out = np.empty(N, dtype=np.float64)
    for i in range(N):
        out[i] = x[mask[i]]

cythonloop(mask, x)

但这不起作用(无法强制列表键入“int[:,:]”)

如果我在numba试一下也不行

import numba as nb

@nb.njit
def nocython(mask, x):
    out = np.empty(len(x), dtype=np.float64)
    for i, v in enumerate(mask):
        out[i] = x[v].mean()
    return out

这会产生以下错误:

TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Invalid use of Function(<built-in function getitem>) with argument(s) of type(s): (array(float64, 1d, A), reflected list(int64))
 * parameterized

Tags: ofinimportnumpy列表lenasnp
1条回答
网友
1楼 · 发布于 2024-05-26 16:29:10

在Numba中,可以使用Typed List对列表列表进行迭代。Numba不支持使用列表索引NumPy数组,因此该函数还需要一些修改,以通过迭代内部列表的元素并索引到x来实现平均值

在调用jitted函数之前,还需要将列表列表转换为类型化列表

把这些放在一起(除了你问题中的代码):

from numba import njit
from numba.typed import List

@njit
def jitted(mask, x): 
    out = np.empty(len(x), dtype=np.float64)
    for i in range(len(mask)):
        m_i = mask[i]
        s = 0 
        for j in range(len(m_i)):
            s += x[m_i[j]]
        out[i] = s / len(m_i)
    return out 

typed_mask = List()
for m in mask:
    typed_mask.append(List(m))

# Sanity check - Numba and nocython implementations produce the same result
np.testing.assert_allclose(nocython(mask, x),  jitted(typed_mask, x))

请注意,也可以避免将列表设置为类型化列表,因为传递内置列表类型时,Numba将使用Reflected List,但是此功能已被弃用,并将从未来版本的Numba中删除,因此建议改用类型化列表

相关问题 更多 >

    热门问题