Вычислить расстояние между массивами, содержащими NaN - PullRequest
5 голосов
/ 08 мая 2020

рассмотрите array1 и array2, с:

array1 = [a1 a2 NaN ... an]
array2 = [[NaN b2 b3 ... bn],
          [b21 NaN b23 ... b2n],
          ...]

Оба массива являются numpy -массивами. Существует простой способ вычислить евклидово расстояние между array1 и каждой строкой array2:

EuclideanDistance = np.sqrt(((array1 - array2)**2).sum(axis=1))

Что мешает этому вычислению, так это значения NaN. Конечно, я мог легко заменить NaN каким-нибудь числом. Но вместо этого я хочу сделать следующее:

Когда я сравниваю array1 с row_x из array2, я считаю столбцы, в которых один из массивов имеет NaN, а другой - нет. Предположим, что count равно 3. Затем я удалю эти столбцы из обоих массивов и вычислю евклидово расстояние между ними. В конце я добавляю minus_value * count к рассчитанному расстоянию.

Сейчас я не могу придумать, как это сделать быстро и эффективно. Кто-нибудь может мне помочь?

Вот несколько моих идей:

minus = 1000
dist = np.zeros(shape=(array1.shape[0])) # this array will store the distance of array1 to each row of array2
array1 = np.repeat(array1, array2.shape[0], axis=0) # now array1 has the same dimensions as array2
for i in range(0, array1.shape[0]):
    boolarray = np.logical_or(np.isnan(array1[i]), np.isnan(array2[i]))
    count = boolarray.sum()
    deleteIdxs = boolarray.nonzero() # this should give the indices where boolarray is True
    dist[i] = np.sqrt(((np.delete(array1[i], deleteIdxs, axis=0) - np.delete(array2[i], deleteIdxs, axis=0))**2).sum(axis=0))
    dist[i] = dist[i] + count*minus

Однако эти строки кажутся мне более чем некрасивыми. Кроме того, я продолжаю получать ошибку индекса: очевидно, что deleteIdxs содержит индекс, выходящий за пределы диапазона для array1. Не знаю, как такое может быть.

Ответы [ 3 ]

2 голосов
/ 08 мая 2020

Вы можете найти все индексы, в которых значение равно nan, используя:

indices_1 = np.isnan(array1)
indices_2 = np.isnan(array2)

Которые вы можете объединить с:

indices_total = indices_1 + indices_2

И вы можете сохранить все значения not nan используя:

array_1_not_nan = array1[~indices_total]
array_2_not_nan = array2[~indices_total]
1 голос
/ 08 мая 2020

Вы можете отфильтровать столбцы, содержащие nan, с помощью:

mask1 = np.isnan(arr1)
mask2 = np.isnan(arr2).any(0)

mask = ~(mask1 | mask2)

# the two filtered arrays
arr1[mask], arr2[mask]
1 голос
/ 08 мая 2020

Я бы написал функцию для вычисления расстояния. Я уверен, что есть более быстрый и эффективный способ написать это (понимание списков, агрегирование и т. Д. c.), Но удобочитаемость имеет значение, верно? :)

import numpy as np
def calculate_distance(fixed_arr, var_arr, penalty):
    s_sum = 0.0
    counter = 0
    for num_1, num_2 in zip(fixed_arr, var_arr):
        if np.isnan(num_1) or np.isnan(num_2):
            counter += 1
        else:
            s_sum += (num_1 - num_2) ** 2
    return np.sqrt(s_sum) + penalty * counter, counter


array1 = np.array([1, 2, 3, np.NaN, 5, 6])
array2 = np.array(
    [
        [3, 4, 9, 3, 4, 8],
        [3, 4, np.NaN, 3, 4, 8],
        [np.NaN, 9, np.NaN, 3, 4, 8],
        [np.NaN, np.NaN, np.NaN, np.NaN, np.NaN, np.NaN],
    ]
)
dist = np.zeros(len(array2))


minus = 10
for index, arr in enumerate(array2):
    dist[index], _ = calculate_distance(array1, arr, minus)

print(dist)

Вы должны очень внимательно подумать о значении переменной минус . Действительно ли полезно добавлять случайное значение?

Как предположил @Nathan, можно легко реализовать более эффективное использование ресурсов.

fixed_arr = array1
penalty = minus
dist = [
    (
        lambda indices=(np.isnan(fixed_arr) + np.isnan(var_arr)): np.linalg.norm(
            fixed_arr[~indices] - var_arr[~indices]
        )
        + (indices == True).sum() * penalty
    )()
    for var_arr in array2
]
print(dist)

Однако я бы попытался реализовать что-то подобное, только если бы мне было необходимо (если это узкое место). Во всех остальных случаях я был бы счастлив пожертвовать некоторыми ресурсами, чтобы получить некоторую читаемость и расширяемость.

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