Numpy: поиск минимальных и максимальных значений из ассоциаций через биннинг
необходимое условие
Это вопрос, полученный из этого поста. Таким образом, некоторые из введения проблемы будут похожи на этот пост.
проблема
Скажем result
это 2D массив и values
является одномерным массивом values
содержит некоторые значения, связанные с каждым элементом в result
, Отображение элемента в values
в result
хранится в x_mapping
а также y_mapping
, Положение в result
могут быть связаны с разными значениями. Теперь я должен найти минимум и максимум значений, сгруппированных по ассоциациям.
Пример для лучшего разъяснения.
min_result
массив:
[[0, 0],
[0, 0],
[0, 0],
[0, 0]]
max_result
массив:
[[0, 0],
[0, 0],
[0, 0],
[0, 0]]
values
массив:
[ 1., 2., 3., 4., 5., 6., 7., 8.]
Примечание: здесь result
массивы и values
имеют одинаковое количество элементов. Но это может быть не так. Между размерами нет никакой связи.
x_mapping
а также y_mapping
есть отображения из 1D values
в 2D result
(как мин, так и макс). Размеры x_mapping
, y_mapping
а также values
будет таким же.
x_mapping
- [0, 1, 0, 0, 0, 0, 0, 0]
y_mapping
- [0, 3, 2, 2, 0, 3, 2, 1]
Здесь 1-е значение (values[0]
) и 5-е значение (values[4]
) иметь х как 0 и у как 0(x_mapping[0]
а также y_mappping[0]
) и, следовательно, связано с result[0, 0]
, Если мы вычислим минимум и максимум из этой группы, мы получим 1 и 5 в качестве результатов соответственно. Так, min_result[0, 0]
будет 1 и max_result[0, 0]
будет 5.
Обратите внимание, что если нет никакой связи, то значение по умолчанию для result
будет ноль.
Текущее рабочее решение
x_mapping = np.array([0, 1, 0, 0, 0, 0, 0, 0])
y_mapping = np.array([0, 3, 2, 2, 0, 3, 2, 1])
values = np.array([ 1., 2., 3., 4., 5., 6., 7., 8.], dtype=np.float32)
max_result = np.zeros([4, 2], dtype=np.float32)
min_result = np.zeros([4, 2], dtype=np.float32)
min_result[-y_mapping, x_mapping] = values # randomly initialising from values
for i in range(values.size):
x = x_mapping[i]
y = y_mapping[i]
# maximum
if values[i] > max_result[-y, x]:
max_result[-y, x] = values[i]
# minimum
if values[i] < min_result[-y, x]:
min_result[-y, x] = values[i]
min_result
,
[[1., 0.],
[6., 2.],
[3., 0.],
[8., 0.]]
max_result
,
[[5., 0.],
[6., 2.],
[7., 0.],
[8., 0.]]
Неудачные решения
# 1
min_result = np.zeros([4, 2], dtype=np.float32)
np.minimum.reduceat(values, [-y_mapping, x_mapping], out=min_result)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-17-126de899a90e> in <module>()
1 min_result = np.zeros([4, 2], dtype=np.float32)
----> 2 np.minimum.reduceat(values, [-y_mapping, x_mapping], out=min_result)
ValueError: object too deep for desired array
# 2
min_result = np.zeros([4, 2], dtype=np.float32)
np.minimum.reduceat(values, lidx, out= min_result)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-24-07e8c75ccaa5> in <module>()
1 min_result = np.zeros([4, 2], dtype=np.float32)
----> 2 np.minimum.reduceat(values, lidx, out= min_result)
ValueError: operands could not be broadcast together with remapped shapes [original->remapped]: (4,2)->(4,) (8,)->() (8,)->(8,)
# 3
lidx = ((-y_mapping) % 4) * 2 + x_mapping #from mentioned post
min_result = np.zeros([8], dtype=np.float32)
np.minimum.reduceat(values, lidx, out= min_result).reshape(4,2)
[[1., 4.],
[5., 5.],
[1., 3.],
[5., 7.]]
Вопрос
Как пользоваться np.minimum.reduceat
а также np.maximum.reduceat
для решения этой проблемы? Я ищу решение, оптимизированное для времени выполнения.
Примечание
Я использую Numpy версии 1.14.3 с Python 3.5.2
1 ответ
Подход № 1
Опять же, самые интуитивные были бы с numpy.ufunc.at
, Теперь, поскольку эти сокращения будут выполняться по отношению к существующим значениям, нам нужно инициализировать вывод с максимальными значениями для минимальных сокращений и минимальными значениями для максимальных. Следовательно, реализация будет -
min_result[-y_mapping, x_mapping] = values.max()
max_result[-y_mapping, x_mapping] = values.min()
np.minimum.at(min_result, [-y_mapping, x_mapping], values)
np.maximum.at(max_result, [-y_mapping, x_mapping], values)
Подход № 2
Использовать np.ufunc.reduceat
нам нужно отсортировать данные -
m,n = max_result.shape
out_dtype = max_result.dtype
lidx = ((-y_mapping)%m)*n + x_mapping
sidx = lidx.argsort()
idx = lidx[sidx]
val = values[sidx]
m_idx = np.flatnonzero(np.r_[True,idx[:-1] != idx[1:]])
unq_ids = idx[m_idx]
max_result_out.flat[unq_ids] = np.maximum.reduceat(val, m_idx)
min_result_out.flat[unq_ids] = np.minimum.reduceat(val, m_idx)