![](/img/trans.png)
[英]Numpy: given a vector of 0s and 1s, how to efficiently use Numpy functions manipulate values in another array, based on 0/1 value of first array?
[英]Efficiently processing values in one array based on a ID value in another
我有一個包含一系列值的2-d numpy數組,以及一個用於標識該單元所屬組的匹配數組。 我希望在每個組的第一個數組中運行一些簡單的操作。 為了說明,我可能有一個看起來像這樣的數據和ID數組:
data = np.arange(9).reshape(3,3)
IDs = np.array([
["A", "A", "B"],
["A", "B", "C"],
["B", "C", "C"]
])
我可能想要對所有A,B和C值采用均值和標准差,輸出看起來像這樣:
[['A', 1.333333333333333, 1.247219128924647],
['B', 4.0, 1.6329931618554521],
['C', 6.666666666666667, 1.247219128924647]]
現在,我用以下代碼解決了這個問題:
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])
我認為這很簡單易懂,但速度不是很快。 使用我的真實數據時,陣列通常約為200 mb(5,000 * 10,000而不是3 * 3),並且有數百個唯一ID。 每次迭代需要幾秒鍾,整個操作可能需要半個多小時。
我的“直覺”是,如果我不必迭代每個唯一的ID值並重復查找和操作,這可以更快地完成,但我不知道這是否真的如此或我將如何去做。
你可以使用np.unique
得到數字IDs
對應的字符串IDs
。 這些數字ID在mean
和std
計算中都很有用。
現在,對於平均計算,這些數字ID可以用作與np.bincount
進行分箱的"weights"
,為我們提供與每個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,則這可能是一種需要內存的方法。 該實現將重新使用idx
並從前面的代碼counts
,看起來像這樣 -
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. ])
如果您要進行大量這類分組操作,我強烈建議您查看一下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
有關更多示例,請參見此處 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.