Python:在使用Numba时,切片2d NumPy数组以生成C顺序数组

Python:在使用Numba时,切片2d NumPy数组以生成C顺序数组,python,numpy,numba,Python,Numpy,Numba,我已经开始使用Numba,现在我正在尝试使用Numba加速一个算法。但是,我在numpy.dot操作中遇到了问题。问题是,当我逐列切片字符串的2d数组时,它会生成一个数组类型([unichr x 100],1d,a)。我需要将此类型设置为array([unichr x 100],1d,C),以便numpy.where生成类型为array(float64,1d,C)的数组。然后在numpy.dot操作中使用该数组,并使用另一个相同类型的数组。Numba告诉我,我不喜欢数组有不同的阶数,A和C。没有

我已经开始使用Numba,现在我正在尝试使用Numba加速一个算法。但是,我在numpy.dot操作中遇到了问题。问题是,当我逐列切片字符串的2d数组时,它会生成一个数组类型([unichr x 100],1d,a)。我需要将此类型设置为array([unichr x 100],1d,C),以便numpy.where生成类型为array(float64,1d,C)的数组。然后在numpy.dot操作中使用该数组,并使用另一个相同类型的数组。Numba告诉我,我不喜欢数组有不同的阶数,A和C。没有Numba,算法运行良好

这里有一个简短的例子来说明这个问题

data_X = [['a1','b2','c1'],
          ['a1','b2','c2'],
          ['a2','b1','c3'],
          ['a1','b2','c1'],
          ['a2','b1','c3']]
data_Y = [1.0, 2.0, 3.0, 4.0, 5.0]
X = np.array(data_X, dtype='<U100')
Y = np.array(data_Y, dtype=np.float64)

@nb.jit(
    nopython=True,
    locals={
        'X': nb.types.Array(nb.types.UnicodeCharSeq(100), 2, 'C'),
        'Y': nb.types.Array(nb.float64, 1, 'C'),
    }
)
def func(X, Y):
    results = []
    for i in range(X.shape[1]):
        uniqs = np.unique(X[:,i])
        for u in uniqs:
            X_vars = np.where(X[:,i] == np.full_like(X[:,i], u), 1.0, 0.0)
            results.append(np.dot(X_vars, Y))
    return results

func(X, Y)
data_X=[[a1','b2','c1'],
['a1'、'b2'、'c2'],
['a2'、'b1'、'c3'],
['a1'、'b2'、'c1'],
['a2'、'b1'、'c3']
数据_Y=[1.0,2.0,3.0,4.0,5.0]

X=np.array(data_X,dtype='首先,让我们找出numba到底在哪里遇到麻烦。如果我们将操作简化为:

def func(X, Y):
    #results = []
    for i in range(X.shape[1]):
        uniqs = np.unique(X[:,i])
        for u in uniqs:
            X_vars = (X[:,i] == np.full_like(X[:,i], u))
            #X_vars = np.where(X[:,i] == np.full_like(X[:,i], u), 1.0, 0.0)
            #results.append(np.dot(X_vars, Y))
    #return results
然后numba仍然继续抛出相同的错误:
NotImplementedError:不知道如何分配布局为“A”的数组。
。这就是问题所在。事实上,您可以通过更简单的操作重现相同的错误:

def func(X, Y):
    for i in range(X.shape[1]):
        X[:,i] == X[:,i]
堆栈跟踪提供了一个提示:问题在于连续性。这里,
X[:,i]
是一个视图,因此没有指定'C'/'F'连续性,这使numba变得疯狂。因此,一个简单的解决方案是添加一行额外的代码,并在视图上应用
np.ascontiguousarray
。深度副本也同样有效

@nb.jit(
    nopython=True,
    locals={
        'X': nb.types.Array(nb.types.UnicodeCharSeq(100), 2, 'C'),
        'Y': nb.types.Array(nb.float64, 1, 'C'),
    }
)
def func(X, Y):
    results = []
    for i in range(X.shape[1]):
        X_i = np.ascontiguousarray(X[:,i])
        uniqs = np.unique(X_i)
        for u in uniqs:
            X_vars = np.where( X_i == np.full_like(X_i, u), 1.0, 0.0)
            results.append(np.dot(X_vars, Y))
    return results

numba
在这类事情上不如
numpy
灵活。这是提高速度的代价。但是我怀疑你可以在更基本的代码中完成这项任务,而不需要
其中的
,或者
Y
是1d,因此点是一个简单的1d迭代。感谢你的解决方案,它确实让我的代码变得更加有用N可选。不幸的是,它似乎比没有Numba decorator的相同Python代码慢。Timeit结果:-没有Numba:82.1µs±650 ns/循环,而Numba:600µs±17.8µs/循环。当我放大问题时,Numba变得更慢。似乎我必须找到一种不同的方法,但您的答案仍然受到赞赏。Setting cache=True不改变运行时似乎不改变代码的运行时。
@nb.jit(
    nopython=True,
    locals={
        'X': nb.types.Array(nb.types.UnicodeCharSeq(100), 2, 'C'),
        'Y': nb.types.Array(nb.float64, 1, 'C'),
    }
)
def func(X, Y):
    results = []
    for i in range(X.shape[1]):
        X_i = np.ascontiguousarray(X[:,i])
        uniqs = np.unique(X_i)
        for u in uniqs:
            X_vars = np.where( X_i == np.full_like(X_i, u), 1.0, 0.0)
            results.append(np.dot(X_vars, Y))
    return results