基于另一个阵列中的ID值有效地处理一个阵列中的值

时间:2015-10-28 17:17:58

标签: python arrays numpy

我有一个包含一系列值的2-d numpy数组,以及一个用于标识该单元所属组的匹配数组。我希望在每个组的第一个数组中运行一些简单的操作。为了说明,我可能有一个看起来像这样的数据和ID数组:

[['A', 1.333333333333333, 1.247219128924647],
['B', 4.0, 1.6329931618554521],
['C', 6.666666666666667, 1.247219128924647]]

我可能想要对所有A,B和C值采用均值和标准差,输出看起来像这样:

unique_IDs = np.unique(IDs)
results = []
for ID in unique_IDs:
    group_data = data[IDs == ID]
    mean = np.mean(group_data)
    stdev = np.std(group_data)
    results.append([ID, mean, stdev])

现在,我使用以下代码解决了这个问题:

if (localStorage.getItem("resizeColumnWidth")) {
  /* set the column width based on the value from local storage */
}

else {
    localStorage.setItem("resizeColumnWidth", resizeColumnWidth);
}

我认为这很简单易懂,但速度不是很快。使用我的真实数据时,阵列通常约为200 mb(5,000 * 10,000而不是3 * 3),并且有数百个唯一ID。每次迭代都需要几秒钟,整个操作可能需要半个多小时。

我的直觉本能"如果我不必迭代每个唯一的ID值并重复查找和操作,这可以更快地完成,但我不知道这是否真的如此或我将如何进行。

2 个答案:

答案 0 :(得分:2)

您可以使用np.unique获取与字符串IDs对应的数字IDs。这些数字ID在meanstd计算中都很有用。

现在,对于平均值计算,这些数字ID可以用作"weights"np.bincount进行分箱,为我们提供与每个ID对应的数据元素的总和。接下来。使用每个ID的元素计数,然后可以得到平均值。这是实施 -

_,idx,counts = np.unique(IDs,return_inverse=True,return_counts=True)  
mean_vals = np.bincount(idx,data.ravel())/counts

对于std计算,一种方法是对ID进行排序,以便连续放置相同的ID。然后,重新排列数据以形成另一个2D阵列,使得对应于相同ID的所有数据元素在相同的行中,并且未填充的位置被设置为NaNs。这里的想法是沿着列以矢量化方式执行np.std。要填补的地方需要掩蔽。请注意,如果存在具有相对较高计数的ID,则这可能是一种需要内存的方法。该实现将重复使用前面代码中的idxcounts,看起来像这样 -

data_RO = np.empty((counts.size,counts.max()))
data_RO[:] = np.nan
data_RO[np.arange(counts.max()) < counts[:,None]] = data.ravel()[idx.argsort()]
std_vals = np.nanstd(data_RO,axis=1)

示例运行并验证输出 -

1)输入:

In [51]: data
Out[51]: 
array([[0, 1, 6],
       [2, 5, 0],
       [6, 3, 6]])

In [52]: IDs
Out[52]: 
array([['A', 'A', 'B'],
       ['A', 'B', 'C'],
       ['B', 'A', 'C']], 
      dtype='|S1')

2)问题中列出的代码输出:

In [53]: unique_IDs = np.unique(IDs)

In [54]: results = []

In [55]: for ID in unique_IDs:
   ....:         group_data = data[IDs == ID]
   ....:         mean = np.mean(group_data)
   ....:         stdev = np.std(group_data)
   ....:         results.append([ID, mean, stdev])
   ....:     

In [56]: results
Out[56]: 
[['A', 1.5, 1.1180339887498949],
 ['B', 5.666666666666667, 0.47140452079103168],
 ['C', 3.0, 3.0]]

3)建议解决方案的输出:

In [57]: _,idx,counts = np.unique(IDs,return_inverse=True,return_counts=True) 

In [58]: np.bincount(idx,data.ravel())/counts
Out[58]: array([ 1.5       ,  5.66666667,  3.        ])

In [59]: data_RO = np.empty((counts.size,counts.max()))

In [60]: data_RO[:] = np.nan

In [61]: mask = np.arange(counts.max()) < counts[:,None]

In [62]: data_RO[mask] = data.ravel()[idx.argsort()]

In [63]: np.nanstd(data_RO,axis=1)
Out[63]: array([ 1.11803399,  0.47140452,  3.        ])

答案 1 :(得分:1)

如果您要进行大量这类分组操作,我强烈建议您查看pandas

import numpy as np
import pandas as pd

data = np.arange(9).reshape(3,3)
IDs = np.array([["A", "A", "B"],
                ["A", "B", "C"],
                ["B", "C", "C"]])
df = pd.DataFrame({'ID':IDs.ravel(), 'data':data.ravel()})
print(df)
#   ID  data
# 0  A     0
# 1  A     1
# 2  B     2
# 3  A     3
# 4  B     4
# 5  C     5
# 6  B     6
# 7  C     7
# 8  C     8

print(df.groupby('ID').agg([np.mean, np.std]))
#         data          
#         mean       std
# ID                    
# A   1.333333  1.527525
# B   4.000000  2.000000
# C   6.666667  1.527525

有关更多示例,请参阅here