2016-02-04 39 views
2

我有一個看起來像下面這樣NumPy的使用fromiter創建兩個數組同時

it = ((x, x**2) for x in range(20)) 

和我要的是兩個數組的迭代器。其中一個x s和另一個x**2 s,但我實際上並不知道元素的數量,而且我無法從一個條目轉換到另一個條目,所以我無法構建第一個,然後構建從第一個第二。

如果我只有一個未知大小的結果,我可以使用np.fromiter使它有效地動態分配,例如,

y = np.fromiter((x[0] for x in it), float) 

有兩個我希望我可以做類似

ita, itb = itertools.tee(it) 
y = np.fromiter((x[0] for x in ita), float) 
y2 = np.fromiter((x[1] for x in itb), float) 

但因爲第一次調用耗盡了迭代器,我會更好做

lst = list(it) 
y = np.fromiter((x[0] for x in lst), float, len(lst)) 
y2 = np.fromiter((x[1] for x in lst), float, len(lst)) 

因爲開球會無論如何,要填寫整個列表的大小。我希望避免在將迭代器複製到數組中之前將迭代器複製到列表中,但我想不出如何在不完全手動執行的情況下增量構建數組。另外,fromiter似乎是用c編寫的,所以用python編寫它可能最終會在首先創建列表時產生不可忽略的差異。

回答

2

你可以使用np.fromiter建立一個陣列與所有的值,然後切片陣列:

In [103]: it = ((x, x**2) for x in range(20)) 

In [104]: import itertools 

In [105]: y = np.fromiter(itertools.chain.from_iterable(it), dtype=float) 

In [106]: y 
Out[106]: 
array([ 0., 0., 1., 1., 2., 4., 3., 9., 4., 
     16., 5., 25., 6., 36., 7., 49., 8., 64., 
      9., 81., 10., 100., 11., 121., 12., 144., 13., 
     169., 14., 196., 15., 225., 16., 256., 17., 289., 
     18., 324., 19., 361.]) 

In [107]: y, y2 = y[::2], y[1::2] 

In [108]: y 
Out[108]: 
array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 
     11., 12., 13., 14., 15., 16., 17., 18., 19.]) 

In [109]: y2 
Out[109]: 
array([ 0., 1., 4., 9., 16., 25., 36., 49., 64., 
     81., 100., 121., 144., 169., 196., 225., 256., 289., 
     324., 361.]) 

上述設法從迭代器中的數據加載到陣列,而不使用中間的Python列表。然而,數組中的底層數據並不是連續的。許多操作上的連續陣列更快:

In [19]: a = np.arange(10**6) 

In [20]: y1 = a[::2] 

In [21]: z1 = np.ascontiguousarray(y1) 

In [24]: %timeit y1.sum() 
1000 loops, best of 3: 975 µs per loop 

In [25]: %timeit z1.sum() 
1000 loops, best of 3: 464 µs per loop 

所以你不妨讓yy2連續:

y = np.ascontiguousarray(y) 
y2 = np.ascontiguousarray(y2) 

調用np.ascontiguousarray需要yy2到新陣列複製非連續數據。不幸的是,我沒有看到創建yy2作爲連續數組而不復制的方法。


這裏是使用中間Python列表的比較來NumPy的切片(具有和不具有ascontiguousarray)的基準:

import numpy as np 
import itertools as IT 

def using_intermediate_list(g): 
    lst = list(g) 
    y = np.fromiter((x[0] for x in lst), float, len(lst)) 
    y2 = np.fromiter((x[1] for x in lst), float, len(lst)) 
    return y, y2 

def using_slices(g): 
    y = np.fromiter(IT.chain.from_iterable(g), dtype=float) 
    y, y2 = y[::2], y[1::2] 
    return y, y2 

def using_slices_contiguous(g): 
    y = np.fromiter(IT.chain.from_iterable(g), dtype=float) 
    y, y2 = y[::2], y[1::2] 
    y = np.ascontiguousarray(y) 
    y2 = np.ascontiguousarray(y2) 
    return y, y2 

def using_array(g): 
    y = np.array(list(g)) 
    y, y2 = y[:, 0], y[:, 1] 
    return y, y2 

In [27]: %timeit using_intermediate_list(((x, x**2) for x in range(10**6))) 
1 loops, best of 3: 376 ms per loop 

In [28]: %timeit using_slices(((x, x**2) for x in range(10**6))) 
1 loops, best of 3: 220 ms per loop 

In [29]: %timeit using_slices_contiguous(((x, x**2) for x in range(10**6))) 
1 loops, best of 3: 237 ms per loop 

In [34]: %timeit using_array(((x, x**2) for x in range(10**6))) 
1 loops, best of 3: 707 ms per loop 
+0

相比'np.array([( x,x ** 2)for x in range(20)])',這個'chain'方法快1.2倍(對於更大的範圍是1.5倍)。 – hpaulj

+0

是的,我考慮過這個。這可能不是太糟糕,因爲你將視圖返回到較大的數組中。然而,我擔心因爲參賽作品將會更加分散,因此我在閱讀數據後要做的矩陣運算效率會降低。這是我應該測試的東西。 – Erik

+0

是的,矩陣操作在非連續陣列上會比較慢。不幸的是,我沒有看到沒有複製的情況下創建連續數組的方式。但是,上面的內容避免使用中間Python列表。 – unutbu

相關問題