簡體   English   中英

基於另一個陣列中的ID值有效地處理一個陣列中的值

[英]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在meanstd計算中都很有用。

現在,對於平均計算,這些數字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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM