2016-11-13 13 views
8

考慮名單l有效地轉換列表的不均勻名單與南填補

l = [[1, 2, 3], [1, 2]] 

列表最小含數組,如果我將它轉換爲一個np.array我將在第一個拿到一個維對象數組與[1, 2, 3]位置和[1, 2]在第二位置。

print(np.array(l)) 

[[1, 2, 3] [1, 2]] 

我想這不是

print(np.array([[1, 2, 3], [1, 2, np.nan]])) 

[[ 1. 2. 3.] 
[ 1. 2. nan]] 

我可以用一個循環做到這一點,但我們都知道循環多麼不受歡迎是

def box_pir(l): 
    lengths = [i for i in map(len, l)] 
    shape = (len(l), max(lengths)) 
    a = np.full(shape, np.nan) 
    for i, r in enumerate(l): 
     a[i, :lengths[i]] = r 
    return a 

print(box_pir(l)) 

[[ 1. 2. 3.] 
[ 1. 2. nan]] 

如何我要這樣做嗎?以一種快速,矢量化的方式?


定時

enter image description here

enter image description here

設置功能

%%cython 
import numpy as np 

def box_pir_cython(l): 
    lengths = [len(item) for item in l] 
    shape = (len(l), max(lengths)) 
    a = np.full(shape, np.nan) 
    for i, r in enumerate(l): 
     a[i, :lengths[i]] = r 
    return a 

def box_divikar(v): 
    lens = np.array([len(item) for item in v]) 
    mask = lens[:,None] > np.arange(lens.max()) 
    out = np.full(mask.shape, np.nan) 
    out[mask] = np.concatenate(v) 
    return out 

def box_hpaulj(LoL): 
    return np.array(list(zip_longest(*LoL, fillvalue=np.nan))).T 

def box_simon(LoL): 
    max_len = len(max(LoL, key=len)) 
    return np.array([x + [np.nan]*(max_len-len(x)) for x in LoL]) 

def box_dawg(LoL): 
    cols=len(max(LoL, key=len)) 
    rows=len(LoL) 
    AoA=np.empty((rows,cols,)) 
    AoA.fill(np.nan) 
    for idx in range(rows): 
     AoA[idx,0:len(LoL[idx])]=LoL[idx] 
    return AoA 

def box_pir(l): 
    lengths = [len(item) for item in l] 
    shape = (len(l), max(lengths)) 
    a = np.full(shape, np.nan) 
    for i, r in enumerate(l): 
     a[i, :lengths[i]] = r 
    return a 

def box_pandas(l): 
    return pd.DataFrame(l).values 

回答

6

這似乎是密切的this question之一,填充是zeros而不是NaNs。在那裏張貼有趣的方法,以及基於broadcastingboolean-indexingmine。所以,我只需要修改一行從我的文章有沒有辦法解決這種情況下,像這樣 -

def boolean_indexing(v, fillval=np.nan): 
    lens = np.array([len(item) for item in v]) 
    mask = lens[:,None] > np.arange(lens.max()) 
    out = np.full(mask.shape,fillval) 
    out[mask] = np.concatenate(v) 
    return out 

採樣運行 -

In [32]: l 
Out[32]: [[1, 2, 3], [1, 2], [3, 8, 9, 7, 3]] 

In [33]: boolean_indexing(l) 
Out[33]: 
array([[ 1., 2., 3., nan, nan], 
     [ 1., 2., nan, nan, nan], 
     [ 3., 8., 9., 7., 3.]]) 

In [34]: boolean_indexing(l,-1) 
Out[34]: 
array([[ 1, 2, 3, -1, -1], 
     [ 1, 2, -1, -1, -1], 
     [ 3, 8, 9, 7, 3]]) 

我已經發布了幾個運行結果有沒有對所有發佈的方法Q & A,這可能很有用。

+0

@piRSquared列表理解在性能上會比'map'更好嗎? – Divakar

+0

@piRSquared啊不,沒關係。另外,我不知道那個'map'可能存在兼容性問題。所以,我認爲這是一個公平的編輯。看到一些運行時測試肯定會很有趣! – Divakar

+0

@piRSquared可愛,真正全面的基準測試!正如我在所有數據庫中看不到明確的贏家。儘管如此,比賽仍然很好 – Divakar

2

也許這樣的事情?不知道你的硬件,但在16ms的100循環對於l =指[清單(範圍(20)),表(範圍(30))* 10000

from numpy import nan 


def box(l): 
    max_lenght = len(max(l, key=len)) 
    return [x + [nan]*(max_lenght-len(x)) for x in l] 
+0

我真的很喜歡'max(l,key = len)' – piRSquared

+0

我真的很喜歡地圖(len,l)^^。 – Simon

+0

問題是'numpy'不能擴展生成器,所以它不是一個真正的比較。 – dawg

1

我可能會這樣寫的切片分配的每個已填充有默認的子陣列的形式:

def to_numpy(LoL, default=np.nan): 
    cols=len(max(LoL, key=len)) 
    rows=len(LoL) 
    AoA=np.empty((rows,cols,)) 
    AoA.fill(default) 
    for idx in range(rows): 
     AoA[idx,0:len(LoL[idx])]=LoL[idx] 
    return AoA 

我在加入Divakar的Boolean Indexingf4,並加入到定時檢測。至少在我的測試中,(Python 2.7和Python 3.5; Numpy 1.11)並不是最快的。

計時顯示,izip_longestf2略有大多數列表,但片分配(這是f1)快是對大型列表更快:

from __future__ import print_function 
import numpy as np 
try: 
    from itertools import izip_longest as zip_longest 
except ImportError: 
    from itertools import zip_longest 

def f1(LoL): 
    cols=len(max(LoL, key=len)) 
    rows=len(LoL) 
    AoA=np.empty((rows,cols,)) 
    AoA.fill(np.nan) 
    for idx in range(rows): 
     AoA[idx,0:len(LoL[idx])]=LoL[idx] 
    return AoA 

def f2(LoL): 
    return np.array(list(zip_longest(*LoL,fillvalue=np.nan))).T 

def f3(LoL): 
    max_len = len(max(LoL, key=len)) 
    return np.array([x + [np.nan]*(max_len-len(x)) for x in LoL]) 

def f4(LoL): 
    lens = np.array([len(item) for item in LoL]) 
    mask = lens[:,None] > np.arange(lens.max()) 
    out = np.full(mask.shape,np.nan) 
    out[mask] = np.concatenate(LoL) 
    return out 

if __name__=='__main__': 
    import timeit 
    for case, LoL in (('small', [list(range(20)), list(range(30))] * 1000), 
         ('medium', [list(range(20)), list(range(30))] * 10000), 
         ('big', [list(range(20)), list(range(30))] * 100000), 
         ('huge', [list(range(20)), list(range(30))] * 1000000)): 
     print(case) 
     for f in (f1, f2, f3, f4): 
      print(" ",f.__name__, timeit.timeit("f(LoL)", setup="from __main__ import f, LoL", number=100))  

打印:

small 
    f1 0.245459079742 
    f2 0.209980010986 
    f3 0.350691080093 
    f4 0.332141160965 
medium 
    f1 2.45869493484 
    f2 2.32307982445 
    f3 3.65722203255 
    f4 3.55545687675 
big 
    f1 25.8796288967 
    f2 26.6177148819 
    f3 41.6916451454 
    f4 41.3140149117 
huge 
    f1 262.429639101 
    f2 295.129109859 
    f3 427.606887817 
    f4 441.810388088 
3

可能是最快的列表版本使用itertools.zip_longest(可能在Py2中爲izip_longest):

In [747]: np.array(list(itertools.zip_longest(*ll,fillvalue=np.nan))).T 
Out[747]: 
array([[ 1., 2., 3.], 
     [ 1., 2., nan]]) 

的滑動zip生產:

In [748]: list(itertools.zip_longest(*ll)) 
Out[748]: [(1, 1), (2, 2), (3, None)] 

另一個拉鍊「調換」:

In [751]: list(zip(*itertools.zip_longest(*ll))) 
Out[751]: [(1, 2, 3), (1, 2, None)] 

常與列表(或甚至列表的目的數組)啓動時,它比較快堅持使用列表方法。創建一個數組或數據框會有相當大的開銷。

這不是第一次問這個問題。

How can I pad and/or truncate a vector to a specified length using numpy?

我的回答也包括這zip_longest和你box_pir

我覺得也有使用扁平陣列快速numpy的版本,但我不記得細節。它可能是由沃倫或迪瓦卡爾提供的。

我認爲「扁平化」版本的作品的東西沿着這條線:

In [809]: ll 
Out[809]: [[1, 2, 3], [1, 2]] 
In [810]: sll=np.hstack(ll)  # all values in a 1d array 
In [816]: res=np.empty((2,3)); res.fill(np.nan) # empty target 

得到扁平指標值的地方去。這是至關重要的一步。這裏使用r_是迭代的;快速版本可能使用cumsum

In [817]: idx=np.r_[0:3, 3:3+2] 
In [818]: idx 
Out[818]: array([0, 1, 2, 3, 4]) 
In [819]: res.flat[idx]=sll 
In [820]: res 
Out[820]: 
array([[ 1., 2., 3.], 
     [ 1., 2., nan]]) 

================

因此丟失的鏈接>np.arange()廣播

In [897]: lens=np.array([len(i) for i in ll]) 
In [898]: mask=lens[:,None]>np.arange(lens.max()) 
In [899]: mask 
Out[899]: 
array([[ True, True, True], 
     [ True, True, False]], dtype=bool) 
In [900]: idx=np.where(mask.ravel()) 
In [901]: idx 
Out[901]: (array([0, 1, 2, 3, 4], dtype=int32),)