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