繁体   English   中英

将元组列表列表转换为数组时,如何阻止元组创建第3维?

[英]When turning a list of lists of tuples to an array, how can I stop tuples from creating a 3rd dimension?

我有一个元组的列表(每个相同长度的子列表)(每个相同长度的元组为2)。 每个子列表代表一个句子,而元组是该句子的双字母组。

当使用np.asarray将其转换为数组时,python似乎在解释元组,因为我要求创建第三维。

完整的工作代码在这里:

import numpy as np 
from nltk import bigrams  

arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

bi_grams = []
for sent in arr:
    bi_grams.append(list(bigrams(sent)))
bi_grams = np.asarray(bi_grams)
print(bi_grams)

因此,在将bi_grams转换为数组之前,它看起来像这样: [[(1, 2), (2, 3)], [(4, 5), (5, 6)], [(7, 8), (8, 9)]]

上面代码的输出:

array([[[1, 2],
        [2, 3]],

       [[4, 5],
        [5, 6]],

       [[7, 8],
        [8, 9]]])

以这种方式将列表列表转换为数组通常很好,并创建了一个2D数组,但似乎python将元组解释为添加的维,因此输出的形状为(3, 2, 2) ,当位于实际上,我想要并且一直期望(3, 2)的形状。

我想要的输出是:

array([[(1, 2), (2, 3)],
       [(4, 5), (5, 6)],
       [(7, 8), (8, 9)]])

形状为(3, 2)

为什么会这样? 如何获得所需形状/形状的数组?

对于np.array ,您的元组列表列表与列表列表没有任何不同。 从头到尾都是可迭代的。 np.array尝试创建尽可能高的维度数组。 在这种情况下是3d。

有一些方法可以避免这种情况,并制作一个包含对象的2d数组,其中这些对象是元组之类的东西。 但是,正如评论中指出的那样,您为什么要这么做?

在最近的SO 问题中 ,我想出了一种将nd数组转换为(nm)-d形状的对象数组的方法:

In [267]: res = np.empty((3,2),object)
In [268]: arr = np.array(alist)
In [269]: for ij in np.ndindex(res.shape):
     ...:     res[ij] = arr[ij]
     ...:     
In [270]: res
Out[270]: 
array([[array([1, 2]), array([2, 3])],
       [array([4, 5]), array([5, 6])],
       [array([7, 8]), array([8, 9])]], dtype=object)

但这是数组的二维数组,而不是元组。

In [271]: for ij in np.ndindex(res.shape):
     ...:     res[ij] = tuple(arr[ij].tolist())
     ...:     
     ...:     
In [272]: res
Out[272]: 
array([[(1, 2), (2, 3)],
       [(4, 5), (5, 6)],
       [(7, 8), (8, 9)]], dtype=object)

更好(或者是?)

或者我可以直接索引嵌套列表:

In [274]: for i,j in np.ndindex(res.shape):
     ...:     res[i,j] = alist[i][j]
     ...:     
In [275]: res
Out[275]: 
array([[(1, 2), (2, 3)],
       [(4, 5), (5, 6)],
       [(7, 8), (8, 9)]], dtype=object)

我正在使用ndindex生成(3,2)数组的所有索引。

注释中提到的结构化数组之所以起作用,是因为对于复合dtype,元组与列表不同。

In [277]: np.array(alist, 'i,i')
Out[277]: 
array([[(1, 2), (2, 3)],
       [(4, 5), (5, 6)],
       [(7, 8), (8, 9)]], dtype=[('f0', '<i4'), ('f1', '<i4')])

从技术上讲,虽然不是元组数组。 它只是将数组的元素(或记录)表示为元组。

在对象dtype数组中,数组的元素是指向列表中元组的指针(至少在Out[275]情况下)。 在结构化数组的情况下,数字以与3d数组相同的方式存储为数组数据缓冲区中的字节。

这是补充@hpaulj答案的另外两种方法。 其中之一, frompyfunc方法的扩展性似乎比其他方法好一些,尽管如果我们摆脱循环,hpaulj的预分配方法也不错。 请参阅以下时间:

import numpy as np
import itertools

bi_grams = [[(1, 2), (2, 3)], [(4, 5), (5, 6)], [(7, 8), (8, 9)]]

def f_pp_1(bi_grams):
    return np.frompyfunc(itertools.chain.from_iterable(bi_grams).__next__, 0, 1)(np.empty((len(bi_grams), len(bi_grams[0])), dtype=object))

def f_pp_2(bi_grams):
    res = np.empty((len(bi_grams), len(bi_grams[0])), dtype=object)
    res[...] = bi_grams
    return res

def f_hpaulj(bi_grams):
    res = np.empty((len(bi_grams), len(bi_grams[0])), dtype=object)
    for i, j in np.ndindex(res.shape):
        res[i, j] = bi_grams[i][j]
    return res

print(np.all(f_pp_1(bi_grams) == f_pp_2(bi_grams)))
print(np.all(f_pp_1(bi_grams) == f_hpaulj(bi_grams)))

from timeit import timeit
kwds = dict(globals=globals(), number=1000)

print(timeit('f_pp_1(bi_grams)', **kwds))
print(timeit('f_pp_2(bi_grams)', **kwds))
print(timeit('f_hpaulj(bi_grams)', **kwds))

big = 10000 * bi_grams

print(timeit('f_pp_1(big)', **kwds))
print(timeit('f_pp_2(big)', **kwds))
print(timeit('f_hpaulj(big)', **kwds))

样本输出:

True                      <- same result for
True                      <- different methods
0.004281356999854324      <- frompyfunc          small input
0.002839841999957571      <- prealloc ellipsis   small input
0.02361366100012674       <- prealloc loop       small input
2.153144505               <- frompyfunc          large input
5.152567720999741         <- prealloc ellipsis   large input
33.13142323599959         <- prealloc looop      large input

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM