[英]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.