Найти абсолютный минимум между двумя массивами, но сохранить знак - PullRequest
3 голосов
/ 17 июня 2019

Рассмотрим два массива разной длины:

A = np.array([58, 22, 86, 37, 64])

B = np.array([105, 212,   5, 311, 253, 419, 123, 461, 256, 464])

Для каждого значения в A я хочу найти наименьшую абсолютную разницу между значениями в A и B.Я использую Pandas, потому что мои фактические массивы - это подмножества фреймов данных Pandas, а также потому, что метод apply является удобным (хотя и медленным) подходом для определения разницы между двумя массивами разных размеров:

In [22]: pd.Series(A).apply(lambda x: np.min(np.abs(x-B)))
Out[22]:
0    47
1    17
2    19
3    32
4    41
dtype: int64

НО Я также хочу сохранить знак, поэтому желаемый вывод:

0    -47
1     17
2    -19
3     32
4    -41
dtype: int64

[обновление] мои фактические массивы A и B являютсяприблизительно 5e4 и 1e6 в длину, так что решение с низким объемом памяти было бы идеальным.Кроме того, я хочу избегать использования Pandas, потому что это очень медленно на реальных массивах.

Ответы [ 4 ]

5 голосов
/ 17 июня 2019

Давайте используем транслируемое вычитание здесь. Затем мы используем argmin, чтобы найти абсолютный минимум, а затем извлекаем значения на следующем шаге.

u = A[:,None] - B
idx = np.abs(u).argmin(axis=1)

u[np.arange(len(u)), idx]
# array([-47,  17, -19,  32, -41])

При этом используется чистое вещание NumPy, поэтому оно должно быть достаточно быстрым.

3 голосов
/ 18 июня 2019

Постижение

Я не мог с собой поделать. Это не то, что вы должны делать! Но это мило.

[min(x - B, key=abs) for x in A]

[-47, 17, -19, 32, -41]

Уменьшенный раствор Big-O Numpy

Если N = len(A) и M = len(B), тогда это решение должно быть O(N + M log(M)) Если B уже отсортирован, то этап сортировки не требуется. и это становится O(N + M)

C          = np.sort(B)
a          = C.searchsorted(A)

# It is possible that `i` has a value equal to the length of `C`
# in which case the searched value exceeds all those found in `C`.
# In that case, we want to clip the index value to the right most index
# which is `len(C) - 1`
right      = np.minimum(a, len(C) - 1)

# For those searched values that are less than the first value in `C`
# the index value will be `0`.  When I subtract `1`, we'll end up with
# `-1` which makes no sense.  So we clip it to `0`.
left       = np.maximum(a - 1, 0)

Для отсеченных значений мы в итоге будем сравнивать значение с самим собой, и поэтому оно безопасно.

right_diff = A - C[right]
left_diff  = A - C[left ]

np.where(np.abs(right_diff) <= left_diff, right_diff, left_diff)

array([-47,  17, -19,  32, -41])
2 голосов
/ 17 июня 2019

Поскольку вы пометили pandas:

# compute the diff by broadcasting
diff = pd.DataFrame(A[None,:] - B[:,None])
# mininum value
min_val = diff.abs().min()

# mask with where and stack to drop na
diff.where(diff.abs().eq(min_val)).stack()

Вывод:

0  0   -47.0
   2   -19.0
   4   -41.0
2  1    17.0
   3    32.0
dtype: float64
1 голос
/ 17 июня 2019

np.argmin можно найти положение минимального значения. Поэтому вы можете просто сделать это:

pd.Series(A).apply(lambda x: x-B[np.argmin(np.abs(x-B))])
...