没有循环或列表推导的元组列表的列表列表

时间:2018-04-15 05:46:11

标签: python numpy

我有一个列表列表,比如[[1,2],[2,3],[1,3]]到元组列表[(1,2),(2,3),(1, 3)]。这可以通过列表推导轻松完成

[tuple(l) for l in list]

然而,这对于大型列表来说会很慢。所以我想使用纯粹的numpy操作来执行相同的操作。

编辑1 我会尽量让它更清晰。

我有一个函数说foo(),它将返回一个列表的python列表

def foo(*args):
   # Do something
   return arr

arr将有一个列表结构arr = [[a,b], [c,d],...]。 每个内部列表(例如[a, b])将是2个元素长,并且arr将包含大量此类列表(通常大于90,000)。

但是,我要求每个内部列表都是一个元组,用于不变性,比如

arr = [(a,b), (c, d),...]

这可以使用列表推导来执行

def result(arr):
    return [tuple(l) for l in arr]

但是,考虑到列表很大,我会避免这种情况,并使用纯numpy函数来完成此任务。 (正如@hpaulj建议使用arr.view(),在下面的答案中使用dict()和zip()查看他的另一种方法。

我想知道这是否可行。如果可行,请告诉我如何。

1 个答案:

答案 0 :(得分:2)

您的样本列表以及由其制作的数组:

In [26]: alist = [[1,2], [2,3], [1,3]]
In [27]: arr = np.array(alist)
In [28]: arr
Out[28]: 
array([[1, 2],
       [2, 3],
       [1, 3]])

tolist是一种相对快速的“解包”数组的方法,但它会生成一个列表列表 - 就像我们开始时一样:

In [29]: arr.tolist()
Out[29]: [[1, 2], [2, 3], [1, 3]]

因此将其转换为元组列表需要相同的列表理解:

In [30]: [tuple(x) for x in arr.tolist()]
Out[30]: [(1, 2), (2, 3), (1, 3)]
In [31]: [tuple(x) for x in alist]
Out[31]: [(1, 2), (2, 3), (1, 3)]

现在,如果数组具有复合dtype,则tolist会生成元组列表。相反,要从列表创建结构化数组,我们需要一个元组列表:

In [33]: arr1 = np.array([tuple(x) for x in alist], dtype='i,i')
In [34]: arr1
Out[34]: array([(1, 2), (2, 3), (1, 3)], dtype=[('f0', '<i4'), ('f1', '<i4')])
In [35]: arr1.tolist()
Out[35]: [(1, 2), (2, 3), (1, 3)]

从二维数组构造一个结构化数组,有点棘手:

In [37]: arr.view('i,i')
Out[37]: 
array([[(1, 0), (2, 0)],
       [(2, 0), (3, 0)],
       [(1, 0), (3, 0)]], dtype=[('f0', '<i4'), ('f1', '<i4')])

astype并没有好多少。事实上,我不止一次推荐使用tolist路线:

np.array([tuple(x) for x in arr.tolist()],'i,i')

In[33]是一个案例列表,其中元组列表很重要。那是因为numpy开发人员选择将元组解释为结构数组'标记'。

我想不到常规的Python案例是必需的元组列表,列表列表也不行。通常元组和列表之间的显着差异是元组是不可变的。好的,在构造字典键(或设置元素)时,不变性很重要。

In [42]: dict(zip(alist,['a','b','c']))
....
TypeError: unhashable type: 'list'
In [43]: dict(zip([tuple(x) for x in alist],['a','b','c']))
Out[43]: {(1, 2): 'a', (1, 3): 'c', (2, 3): 'b'}

更正view转换为结构化数组

我之前使用view的尝试是错误的,因为我使用了错误的dtype

In [45]: arr.dtype
Out[45]: dtype('int64')
In [46]: arr.view('i8,i8')
Out[46]: 
array([[(1, 2)],
       [(2, 3)],
       [(1, 3)]], dtype=[('f0', '<i8'), ('f1', '<i8')])
In [47]: arr.view('i8,i8').tolist()
Out[47]: [[(1, 2)], [(2, 3)], [(1, 3)]]

更好 - 尽管现在我在列表中有元组。

In [48]: arr.view('i8,i8').reshape(3).tolist()
Out[48]: [(1, 2), (2, 3), (1, 3)]

这可以避免列表理解,但速度并不快:

In [49]: timeit arr.view('i8,i8').reshape(3).tolist()
21.4 µs ± 51.1 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [50]: timeit [tuple(x) for x in arr]
6.26 µs ± 5.51 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

从列表列表与元组列表创建字典的时间测试:

In [51]: timeit dict(zip([tuple(x) for x in alist],['a','b','c']))
2.67 µs ± 21.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [52]: timeit dict(zip(Out[48],['a','b','c']))
1.31 µs ± 5.96 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

显然你需要对现实问题进行时间测试,但是这个小例子表明了这些问题的实现方式。尽管所有关于numpy操作的讨论都很快(呃),但列表推导并不是那么糟糕,特别是如果结果将成为Python对象列表的话。