当将一系列列表的元组列表转换为数组时,如何阻止元组创建第三个维度?

2024-04-19 08:01:13 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个元组列表(每个长度相同的子列表)(每个长度相同的元组,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)。你知道吗

为什么会这样?如何实现所需形式/形状的数组?你知道吗


Tags: 代码import列表np数组array句子sent
2条回答

这里还有两种方法可以补充@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

对于np.array,元组列表与列表列表没有任何区别。一路下来都很难。np.array尝试创建尽可能高的维度数组。在这种情况下,这是三维的

有一些方法可以跳过这些步骤,并生成一个包含对象的二维数组,其中这些对象是元组之类的东西。但正如评论中提到的,你为什么要这样做?你知道吗

在最近的一个SOquestion中,我提出了一种将n-d数组转换为(n-m)-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)数组的所有索引。你知道吗

注释中提到的结构化数组之所以有效,是因为对于复合数据类型,元组不同于列表。你知道吗

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')])

但从技术上讲,这并不是一个元组数组。它只是将数组的元素(或记录)表示为元组。你知道吗

在object dtype数组中,数组的元素是指向列表中元组的指针(至少在Out[275]情况下是这样)。在结构化数组的情况下,数字存储在与三维数组相同的位置,作为数组数据缓冲区中的字节。你知道吗

相关问题 更多 >