#numpy #vectorization
Вопрос:
Я реализовал функцию numpy, которая:
- принимает в качестве входных данных:
- массив поплавков n (строк) x m (столбцов).
- a
threshold
(плавающий)
- для каждой строки:
- если максимальное значение строки больше или равно
threshold
, - если этому максимальному значению не предшествует в той же строке минимальное значение, меньшее или равное
-threshold
, - затем эта строка помечается
True
(больше, чем), - в противном случае эта строка помечена
False
(не больше)
- если максимальное значение строки больше или равно
- возвращает затем этот массив n (строк) x 1 (столбцов) логических значений
То, что я реализовал, работает (по крайней мере, на приведенном примере), но я далек от того, чтобы быть экспертом в numpy, и мне интересно, нет ли более эффективного способа справиться с этим (возможно, избежать разных transpose
amp; tile
, например?) Я с радостью приму любые советы о том, как сделать эту функцию более эффективной и/или читаемой.
import numpy as np
import pandas as pd
# Test data
threshold=0.02 #2%
df = pd.DataFrame({'variation_1': [0.01, 0.02, 0.005, -0.02, -0.01, -0.01],
'variation_2': [-0.01, 0.08, 0.08, 0.01, -0.02, 0.01],
'variation_3': [0.005, -0.03, -0.03, 0.002, 0.025, -0.03],
})
data = df.values
Проверка ожидаемых результатов:
In [75]: df
Out[75]:
variation_1 variation_2 variation_3 # Expecting
0 0.010 -0.01 0.005 # False (no value larger than threshold)
1 0.020 0.08 -0.030 # True (1st value equal to threshold)
2 0.005 0.08 -0.030 # True (2nd value larger than threshold)
3 -0.020 0.01 0.002 # False (no value larger than threshold)
4 -0.010 -0.02 0.025 # False (2nd value lower than -threshold)
5 -0.010 0.01 -0.030 # False (no value larger than threshold)
Текущая функция.
def greater_than(data: np.ndarray, threshold: float) -> np.ndarray:
# Step 1.
# Filtering out from 'low_max' mask the rows which 'max' is not greater than or equal
# to 'threshold'. 'low_max' is reshaped like input array for use in next step.
data_max = np.amax(data, axis=1)
low_max = np.transpose([data_max >= threshold] * data.shape[1])
# Step 2.
# Filtering values preceding max of each row
max_idx = np.argmax(data, axis=1) # Get idx of max.
max_idx = np.transpose([max_idx] * data.shape[1]) # Reshape like input array.
# Create an array of index.
idx_array = np.tile(np.arange(data.shape[1]), (data.shape[0],1))
# Keep indices lower than index of max for each row, and filter out rows with
# a max too low vs 'threshold' (from step 1).
mask_max = (idx_array <= max_idx) amp; (low_max)
# Step 3.
# On a masked array re-using mask from step 2 to filter out unqualifying values,
# filter out rows with a 'min' preceding the 'max' and that are lower than or
# equal to '-threshold'.
data = np.ma.array(data, mask=~mask_max)
data_min = np.amin(data, axis=1)
mask_min = data_min > -threshold
# Return 'mask_min', filling masked values with 'False'.
return np.ma.filled(mask_min, False)
Результаты.
res = greater_than(data, threshold)
In [78]:res
Out[78]: array([False, True, True, False, False, False])
Заранее спасибо за любые советы!
Ответ №1:
lesser = data <= -threshold
greater = data >= threshold
idx_lesser = np.argmax(lesser, axis=1)
idx_greater = np.argmax(greater, axis=1)
has_lesser = np.any(lesser, axis=1)
has_greater = np.any(greater, axis=1)
outptut = has_greater * (has_lesser * (idx_lesser > idx_greater) np.logical_not(has_lesser))
выдает ожидаемый результат для ваших данных и должен быть довольно быстрым. Кроме того, я не совсем уверен, что понимаю ваше объяснение, поэтому, если это не сработает с вашими фактическими данными, дайте мне знать.
Комментарии:
1. Большое спасибо @Dominik. ваше предложение очень аккуратное / читабельное, намного лучше, чем мое. Это почему-то расстраивает: я очень хорошо умею делать вещи более сложными, чем они есть на самом деле… Ах, ах, еще раз спасибо!