我有一個非常大的輸入 numpy 陣列和一個字典。字典規定了 numpy 陣列中的值應該更新為什么。我可以使用 for 回圈來做到這一點,但它非常耗時,我可以使用 numpy 矢量化來解決這個問題嗎?
輸入:
arr_to_check = numpy.array([['A', 20],['B', 100],['C', 80],['D', 90], ['E', 100]]) # actual length is ~10^8
max_possible = {'A': 25, 'B': 40, 'C': 90, 'D': 50, 'F': 100, 'G': 90} # actual length is ~10^3
預期結果:
[['A', '20'], # do not change, because 20 < 25 --- max possible for 'A' is 25.
['B', '0'], # change to 0, because 100 > 50 --- max possible for 'B' is 40.
['C', '80'], # do not change, because 80 < 90
['D', '0'], # change to 0, because 90 > 50 --- max possible for 'D' is 50.
['E', '100' ]]
這是回圈解決方案:
for i in range(arr_to_check.shape[0]):
row = arr_to_check[i]
if row[0] in max_possible and int(row[1]) > max_possible[row[0]]:
row[1] = 0
uj5u.com熱心網友回復:
這是一種執行您所要求的方法(已更新以簡化代碼)。
先說幾點:
- numpy 陣列必須是同質型別,因此您在問題中顯示的數字將由 numpy 轉換為字串以匹配標簽的資料型別(如果熊貓是一個選項,它可能允許您讓數字列共存具有不同的字串列)。
- 盡管我已經將結果一路匹配以匹配原始同類資料型別(字串),但您可以提前停止并使用中間 1D 數值結果,如果這就是您所需要的。
- 我已經用作數字型別,如果需要
int
,您可以將其更改為。float
import numpy
arr_to_check = numpy.array([['A', 20],['B', 100],['C', 80],['D', 90], ['E', 100]])
max_possible = {'A': 25, 'B': 40, 'C': 90, 'D': 50, 'F': 100, 'G': 90}
print('arr_to_check:'); print(arr_to_check)
aT = arr_to_check.T
labels = aT[0,:]
values = aT[1,:].astype(int)
print('labels:'); print(labels)
print('values:'); print(values)
for label, value in max_possible.items():
curMask = (labels == label)
values[curMask] *= (values[curMask] <= value)
print('values:'); print(values)
aT[1,:] = values
arr_to_check = aT.T
print('arr_to_check:'); print(arr_to_check)
輸入:
arr_to_check:
[['A' '20']
['B' '100']
['C' '80']
['D' '90']
['E' '100']]
輸出:
labels:
['A' 'B' 'C' 'D' 'E']
values:
[ 20 100 80 90 100]
values:
[ 20 0 80 0 100]
arr_to_check:
[['A' '20']
['B' '0']
['C' '80']
['D' '0']
['E' '100']]
解釋:
- 轉置輸入,以便我們可以直接對數字向量 (
values
) 使用向量化操作。 - 如果標簽 (in ) 與 in 中的鍵匹配的行的值 in已被破壞,則遍歷 in 中的每個鍵/值對
max_possible
并使用向量化公式乘以0 。values
max_possible
labels
max_possible
- 使用 . 更新原始 numpy 陣列
values
。
uj5u.com熱心網友回復:
正如其他人指出的那樣,numpy 陣列是同質的,您的輸出元素都將具有 str。如果沒問題,您可以使用apply_along_axis
:
t = lambda x: [x[0],0] if x[0] in max_possible and int(x[1]) > max_possible[x[0]] else x
numpy.apply_along_axis(t, 1, arr_to_check)
uj5u.com熱心網友回復:
正如其他人所說,你應該只在你的 numpy 陣列中使用數字。所以你可以有這樣的資料:
arr_to_check = np.array([[0, 20],[1, 100],[2, 80],[3, 90], [4, 100]])
max_possible = np.array([25, 40, 90, 50, np.inf, 100, 90])
這里我假設 'A': 0, 'B': 1, ... 請注意,這樣,不僅字串已被數字替換,而且 dict 也已被 max_possible[i] 為 max 的 Numpy 陣列替換對于第i個字串,方便后續操作。
現在,您可以通過以下方式獲得所需的內容:
m = max_possible.take(arr_to_check.T[0])
m1 = np.array([arr_to_check.T[0], np.minimum(arr_to_check.T[1], m)])
m1.T
第一行放入 m 每個鍵的最大值。
第二行將 m1 您的鍵作為第一行,您的值的最小值和每個鍵的最大值
第三行轉置為您的結果:
陣列([[0., 20.], [1., 40.], [2., 80.], [3., 50.], [4., 100.]])
uj5u.com熱心網友回復:
運行您的代碼:
In [362]: %%timeit arr = arr_to_check.copy()
...: for i in range(arr.shape[0]):
...: row = arr[i]
...: if row[0] in max_possible and int(row[1]) > max_possible[row[0]]:
...: row[1] = 0
...:
14.1 μs ± 203 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
像這樣在陣列上迭代比使用串列要慢,所以讓我們嘗試一個純串列解決方案:
In [372]: alist_to_check = [['A', 20],['B', 100],['C', 80],['D', 90], ['E', 100]]
...: max_possible = {'A': 25, 'B': 40, 'C': 90, 'D': 50, 'F': 100, 'G': 90}
使用帶有 if/else 運算式的串列推導:
In [373]: [[k,0] if k in max_possible and v>max_possible[k] else [k,v] for k,v in alist_to_check]
Out[373]: [['A', 20], ['B', 0], ['C', 80], ['D', 0], ['E', 100]]
In [374]: timeit [[k,0] if k in max_possible and v>max_possible[k] else [k,v] for k,v in alist_to_check]
1.45 μs ± 3.2 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
其中一個答案建議apply_along_axis
- 將鍵重新定義為整數。我的時機來了
In [366]: timeit np.apply_along_axis(t, 1, arr_to_check)
108 μs ± 2 μs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
舉一個小例子,純串列方法是最快的。對于非常大的情況,我們可能會將其視為一個可擴展的 numpy 問題,但我沒有看過這些選項。
結構化陣列
我們可以將串列轉換為結構化陣列。這保留了字串和 int dtypes:
In [398]: arr = np.array([tuple(kv) for kv in alist_to_check],'U10,int')
In [399]: arr
Out[399]:
array([('A', 20), ('B', 100), ('C', 80), ('D', 90), ('E', 100)],
dtype=[('f0', '<U10'), ('f1', '<i4')])
In [400]: arr['f0']
Out[400]: array(['A', 'B', 'C', 'D', 'E'], dtype='<U10')
In [401]: arr['f1']
Out[401]: array([ 20, 100, 80, 90, 100])
如果max_possible
相對于串列較小,則迭代其專案并設定結構化陣列的相應元素可能是最有效的。例如:
def foo(alist):
arr = np.array([tuple(kv) for kv in alist],'U10,int')
for k,v in max_possible.items():
idx = np.nonzero((arr['f0']==k) & (arr['f1']>v))[0]
arr['f1'][idx] = 0
return arr
In [395]: foo(alist_to_check)
Out[395]:
array([('A', 20), ('B', 0), ('C', 80), ('D', 0), ('E', 100)],
dtype=[('f0', '<U10'), ('f1', '<i4')])
對于這個示例,時間不是那么好:
In [397]: timeit foo(alist_to_check)
102 μs ± 360 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
對于一個大串列:
In [403]: biglist = alist_to_check*10000
In [409]: timeit foo(biglist)
44.1 ms ± 209 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [410]: timeit [[k,0] if k in max_possible and v>max_possible[k] else [k,v] for k,v in biglist]
14.8 ms ± 682 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
時間還沒有那么好。然而,其中很大一部分是創建結構化陣列:
In [411]: timeit arr = np.array([tuple(kv) for kv in biglist],'U10,int')
38.4 ms ± 49.5 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)
如果我們已經有了結構化陣列,我希望時代會好很多。
奇怪的是,從中創建一個純字串 dtype 陣列biglist
需要更長的時間:
In [412]: timeit np.array(biglist)
74.2 ms ± 1.17 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
盡管如此,這確實清楚地表明,作業dict
和字串匹配,串列仍然與解決方案競爭numpy
。 numpy
最適合純數字作業。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/481085.html
上一篇:內外印有什么區別