首页 > 解决方案 > 带有元组列表的 Numba

问题描述

我有一个list of tuples来自数据库的查询。

tuple_data = [(1.1,1,"one"),(2.1,2,"two"),(3.1,3,"three")]

每个都tuple将包含不同的数据类型。

从这个列表中,我需要每个元组的第一个元素,所以我做了:

data = [result[0] for result in tuple_data]

现在我正在尝试使用numba module而不是list comprehension.

所以我尝试了以下方法:

@numba.njit(cache = True)
def loop_faster(results):
    res = []
    for result in results:
        res.append(result[0])

这会抛出NumbaPendingDeprecationWarning:,我无法在迭代中使用元组列表(根据 numba 文档)

所以我把它改成了numpy array从这里):

L_arr = np.array(tuple_data)

现在一切都很好。loop_faster方法有效。

问题是,我的原始数据是 (float, int, str) 而更改numpy array为预期的全部 (str,str,str)。

问题是我想要数据float本身。

所以我的代码如下:

import numba, logging
import numpy as np

numba_logger = logging.getLogger('numba')
numba_logger.setLevel(logging.WARNING)

@numba.njit(cache = True)
def loop_faster_1(results, n):
    res = []
    for result in results:
        res.append(result[0])
    print(res)

t1 = [(1.1,1,"one"),(2.1,2,"two"),(3.1,3,"three")]
L_arr = np.array(t1)
loop_faster_1(L_arr,0)

在实际情况下,我的元组列表很大,我将其转换为numpy arrayfornumba并且我需要 float 中的数据,因此我必须将所有 str 转换为 float。

基本上用numba,

  1. 元组列表
  2. 转换为 numpy 数组
  3. 调用 numba 方法
  4. 转换回浮动
  5. 用于进一步处理。

但通过列表理解,

  1. 元组列表
  2. 列表理解
  3. 用于进一步处理。

有没有更好的方法来做到这一点numba?或者我只是list comprehension在使用 numba 时删除这些步骤。因为有了这个,我觉得我实际上正在扼杀减少循环时间的目的。

标签: pythonlistnumpytuples

解决方案


您不需要numba快速获取 numpy 数组的第一列。这是一个基本的索引操作。

您的样品:

In [23]: tuple_data = [(1.1,1,"one"),(2.1,2,"two"),(3.1,3,"three")]

明显的列表理解:

In [24]: [x[0] for x in tuple_data]
Out[24]: [1.1, 2.1, 3.1]

数组方法:

In [25]: np.array(tuple_data)[:,0].astype(float)
Out[25]: array([1.1, 2.1, 3.1])

列表理解要快得多 - 对于这个小样本:

In [26]: timeit [x[0] for x in tuple_data]
349 ns ± 7.76 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [27]: timeit np.array(tuple_data)[:,0].astype(float)
15.4 µs ± 55.4 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

大时代的消费者是np.array(...)。索引速度很快。理解应该与列表大小成线性关系。但也是如此np.array,毕竟它是逐个元素地处理列表。它可能会以更快的速度结束,但过去的经验表明这是 1000 多个元组。

另一种方法是使用复合 dtype,制作结构化数组。现在浮点数不会转换为字符串。但这并没有改善时机。

In [28]: np.array(tuple_data, dtype='f,f,U10')
Out[28]: 
array([(1.1, 1., 'one'), (2.1, 2., 'two'), (3.1, 3., 'three')],
      dtype=[('f0', '<f4'), ('f1', '<f4'), ('f2', '<U10')])
In [29]: np.array(tuple_data, dtype='f,f,U10')['f0']
Out[29]: array([1.1, 2.1, 3.1], dtype=float32)
In [30]: timeit np.array(tuple_data, dtype='f,f,U10')['f0']
21.1 µs ± 1.08 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

这是另一种方法 - 创建一个对象 dtype 数组:

In [31]: np.array(tuple_data, dtype=object)[:,0]
Out[31]: array([1.1, 2.1, 3.1], dtype=object)
In [32]: timeit np.array(tuple_data, dtype=object)[:,0]
4.52 µs ± 9.41 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

这比其他数组更好,那些仍然落后于理解。它创建了一个 (3,3) 对象数组,与元组中的对象相同。它非常类似于列表,除了它执行多维索引。

完整的数组:

In [33]: np.array(tuple_data, dtype=object)
Out[33]: 
array([[1.1, 1, 'one'],
       [2.1, 2, 'two'],
       [3.1, 3, 'three']], dtype=object)
In [34]: np.array(tuple_data, dtype='f,f,U10')
Out[34]: 
array([(1.1, 1., 'one'), (2.1, 2., 'two'), (3.1, 3., 'three')],
      dtype=[('f0', '<f4'), ('f1', '<f4'), ('f2', '<U10')])

推荐阅读