2017-07-25 16 views
3

我想执行操作如何向量化/与不规则排列numpy的tensorize操作形状

form1

如果form2有一个规则的形状,然后我可以使用np.einsum,我相信语法将是

np.einsum('ijp,ipk->ijk',X, alpha) 

不幸的是,我的数据X有一个非规则的结构(如果我们是零指数)的轴。

为了提供更多的背景知识,form3指的是第i组第j个成员的第p个特征。由于组的大小不同,实际上,它是不同长度的列表的列表,具有相同长度的列表。

form4有一个规则的结构,因此可以保存为一个标准的numpy数组(它进入1维然后我使用alpha.reshape(a,b,c)其中a,b,c是问题特定的整数)

我想避免存储X作为列表的列表清单或不同尺寸的np.arrays的列表,并写像

A = [] 
for i in range(num_groups): 
    temp = np.empty(group_sizes[i], dtype=float) 
    for j in range(group_sizes[i]): 
     temp[i] = np.einsum('p,pk->k',X[i][j], alpha[i,:,:]) 
    A.append(temp) 

这是一些不错的功能numpy的/数据结构这样做还是我将不得不妥协与一些仅部分矢量化的实施?

+1

你可以添加一个案例吗? – Divakar

+0

借调;特别是id有兴趣了解涉及不同轴的典型尺寸;以及group_sizes中的整数是如何分布的。偶然,group_sizes是否具有相对较少的独特元素? –

+0

group_sizes在几十到几千之间变化。有82个组。有人建议我可以通过置零来使结构保持正常。这在数学上可以算出来,但是由于最大的团队比最小的团队大得多(〜2个数量级),所以它将包括比必要更多的操作。 – gazza89

回答

1

我知道这听起来很明显,但是,如果你能负担得起内存,我只需简单地通过填充数据来检查性能即可获得统一的大小,即只需添加零并执行操作。有时候更简单的解决方案比更理想的更具有Python/C往返速度的解决方案更快。

如果这不起作用,那么你最好的选择,就像Tom Wyllie建议的那样,可能是一个分水岭战略。假设X是你的名单列表和列表alpha是一个数组,你可以通过收集第二个索引的大小开始(也许你已经有这个):

X_sizes = np.array([len(x_i) for x_i in X]) 

而且对它们进行排序:

idx_sort = np.argsort(X_sizes) 
X_sizes_sorted = X_sizes[idx_sort] 

然后你选择一些桶,这是你工作的分区数。假设你选择BUCKETS = 4。你只需要让更多或更少的每件是相同的大小来划分数据:

sizes_cumsum = np.cumsum(X_sizes_sorted) 
total = sizes_cumsum[-1] 
bucket_idx = [] 
for i in range(BUCKETS): 
    low = np.round(i * total/float(BUCKETS)) 
    high = np.round((i + 1) * total/float(BUCKETS)) 
    m = sizes_cumsum >= low & sizes_cumsum < high 
    idx = np.where(m), 
    # Make relative to X, not idx_sort 
    idx = idx_sort[idx] 
    bucket_idx.append(idx) 

然后你为每个桶计算:灌装阵列X_bucket可能会

bucket_results = [] 
for idx in bucket_idx: 
    # The last index in the bucket will be the biggest 
    bucket_size = X_sizes[idx[-1]] 
    # Fill bucket array 
    X_bucket = np.zeros((len(X), bucket_size, len(X[0][0])), dtype=X.dtype) 
    for i, X_i in enumerate(idx): 
     X_bucket[i, :X_sizes[X_i]] = X[X_i] 
    # Compute 
    res = np.einsum('ijp,ipk->ijk',X, alpha[:, :bucket_size, :]) 
    bucket_results.append(res) 

这部分慢。同样,如果你能负担得起内存,那么将X放在单个填充阵列中,然后就是X[idx, :bucket_size, :]即可。

最后,你可以把你背到结果列表:

result = [None] * len(X) 
for res, idx in zip(bucket_results, bucket_idx): 
    for r, X_i in zip(res, idx): 
     result[X_i] = res[:X_sizes[X_i]] 

,对不起,我不给一个适当的功能,但我不知道是你的输入或输出的预期究竟如何,所以我只是把它们放在一起,你可以根据你的需要使用它们。