[英]Element wise operation on nested numpy array
背景
我有一個嵌套的 numpy 數組,我想:
...
[[1, 2, 3], [4, 5], [6, 7, 8]]
#(add random value for each scaler element)
[5.5, 6.7, 8.2], [4.1, -3.0], [**16**, -2, 7]]
# (remove elements larger than 10)
[5.5, 6.7, 8.2], [4.1, -3.0], [-2, 7]]
代碼:
original_nested_array = np.array([np.array([1,2,3]),np.array([1,2]),np.array([3,2,1])], dtype = object)
# add a random value on each minimum element of original_nested_array
...
# Delete elements larger than fixed value, e.g. 10
...
關鍵是我的嵌套數組具有不同長度的元素。
在上面的示例中,第一個元素的長度 == 3,第二個元素的長度 == 2,第三個元素的長度 == 3。因此, original_nested_array.shape
等於(3,)
而不是(3,3)
,這更難用於元素或廣播操作。
您可以計算每個部分的長度,計算每個部分的偏移量(在展平表示中當前項目之前的項目數),將部分與np.concatenate
合並,使用np.random.randn
的簡單和添加隨機數,使用np.argmax
找到最大值的位置,刪除扁平數組的元素並在使用np.split
拆分數組之前更新部分偏移量:
len_of_parts = np.fromiter(map(len, original_nested_array), dtype=int)
part_sections = len_of_parts.cumsum()
all_values = np.concatenate(original_nested_array).astype(np.float64)
all_values += np.random.randn(all_values.size)
max_index = all_values.argmax()
all_values = np.delete(all_values, max_index)
part_sections[np.searchsorted(part_sections, max_index, 'right'):] -= 1
output = np.split(all_values, part_sections[:-1])
但是,請不要使用鋸齒狀數組。 他們顯然沒有效率。 事實上,Numpy 的設計不是為了有效地操作它們,也不是很容易地操作它們。 Numpy function 的開銷大部分乘以鋸齒狀數組中的項目數,因此。 實際上,包含 1000 個項目的鋸齒狀數組包含平均大小為 10 的子項,其計算速度可能比一個大的扁平數組慢 1000 倍(在這種情況下,它在我的機器上大約慢 200 倍)。 在這種情況下,使用 Python 列表可能要快得多(但與大數組相比仍然效率低下)。
有效的解決方案是展平鋸齒狀數組並保留定義子數組的起始部分數組。 如果您使用 Cython 或 Numba 來計算 Numpy 幾乎無法完成的操作,這尤其快得多。
另請注意,刪除操作很慢,因為需要創建 now 數組(並且幾乎要復制)。 只要不是在循環中完成(至少不是關鍵循環),就可以使用np.delete
。 否則,這個調用的復雜性會變得更糟。
from functools import partial
import numpy as np
import pandas as pd
from typing import Union
class NPTools:
def __init__(self, nparray:Union[np.array, np.ndarray, list,tuple]):
if isinstance(nparray, (list,tuple)):
nparray = np.array(nparray)
self.nparray = nparray
def bb_get_size_of_biggest_element_in_list(self, lst: iter):
return len(max(lst, key=len))
def aa_adjust_2d_numpy_array(self, fillvalue=np.nan, dtype=np.object_):
maxlen = self.bb_get_size_of_biggest_element_in_list(self.nparray)
ajustedlists = np.array(
[
*(
np.fromiter(x + (maxlen - len(x)) * [fillvalue], dtype=dtype)
for x in self.nparray
)
]
)
self.nparray = ajustedlists
return self
def aa_sort_2d_numpy_array(self):
self.nparray = np.array([np.sort(lst) for lst in self.nparray])
return self
def bb_create_random_value_array_of_same_shape_with_int(
self, startvalue: int, stopvalue: int
) -> np.array:
return np.random.randint(startvalue, stopvalue, self.nparray.shape)
def bb_create_zero_array_of_same_shape(self) -> np.array:
return np.zeros(self.nparray.shape)
def bb_slice_vertical(
self, start_index: int = 0, stop_index: Union[int, None] = None
) ->np.array:
"""[[2 2 3]
[4 5 nan]
[6 7 8]] -> [2 4 6]
bb_slice_vertical(1,2)
second_part = sorted_array[0:,1:2]
rest : bb_slice_vertical = bb_slice_vertical[0:,1:]
"""
if stop_index is None:
return self.nparray[0:, start_index:]
return self.nparray[0:, start_index:stop_index]
def bb_delete_all_na_in_2d_array(self):
return np.fromiter(
map(
lambda arraya: np.array([x for x in arraya if pd.notna(x)]),
self.nparray,
),
dtype=self.nparray.dtype,
)
def aa_apply_vetor_function(self, function, arguments=None):
if arguments is not None:
applyfunction = partial(function, *arguments)
else:
applyfunction = partial(function)
oct_array = np.frompyfunc(applyfunction, 1, 1)
self.nparray = oct_array(self.nparray)
return self
def aa_sort_array(self):
self.nparray = np.sort(self.nparray)
return self
@staticmethod
def cc_delete_elements_if_smaller(comparevalue:Union[int,float], value:Union[int,float]) ->Union[int,float]:
if pd.isna(value):
return np.nan
if value < comparevalue:
return np.nan
return value
@staticmethod
def cc_delete_elements_if_bigger(comparevalue:Union[int,float], value:Union[int,float])->Union[int,float]:
if pd.isna(value):
return np.nan
if value > comparevalue:
return np.nan
return value
randomlist = [
[3, 2, 2], [4, 5], [6, 8, 7] ]
nptools_ = NPTools(np.array(randomlist))
vertical_slice = (nptools_.aa_adjust_2d_numpy_array(fillvalue=np.nan, dtype=np.object_) #ajust_array, so that all lists have the same length
.aa_sort_2d_numpy_array() #sort_all_arrays, so that the smallest element's index is 0 in all lists/arrays
.bb_slice_vertical(start_index= 0, stop_index=1)) # get_all_minimum values
print(f'{nptools_.nparray=}\n')
print(f'{vertical_slice=}\n')
vertical_slice += np.random.randint(0,10,vertical_slice.shape) # add some random numbers to the smallest number in each list
print(f'{nptools_.nparray=}\n')
nptools_.aa_apply_vetor_function(function=NPTools.cc_delete_elements_if_bigger, arguments=[10]) #return np.nan if the number is bigger than 10
print(f'{nptools_.nparray=}\n')
finalarray = nptools_.bb_delete_all_na_in_2d_array() #get rid of all np.nans, so that only the values bigger than 10 are left over
print(f'{finalarray=}\n')
output:
nptools_.nparray=array([[2, 2, 3],
[4, 5, nan],
[6, 7, 8]], dtype=object)
vertical_slice=array([[2],
[4],
[6]], dtype=object)
nptools_.nparray=array([[10, 2, 3],
[11, 5, nan],
[13, 7, 8]], dtype=object)
nptools_.nparray=array([[10, 2, 3],
[nan, 5, nan],
[nan, 7, 8]], dtype=object)
finalarray=array([array([10, 2, 3]), array([5]), array([7, 8])], dtype=object)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.