Можно ли сделать цикл быстрее?

Я хочу рассчитать попарные расстояния между точками набора. pdist мне не подходит, т.к. на некоторые расстояния, не удовлетворяющие условию, я хочу наложить "штраф" в виде множителя. В среднем для набора цикл длится минуты 3-4, наборов около 900 - время получается неприемлемым. Может быть можно этот цикл организовать по-другому?

distances = np.zeros((lat_long.values.shape[0], lat_long.values.shape[0]))
for i in range(lat_long.values.shape[0]):
    for j in range(lat_long.values.shape[0]):
        if i < j:
            if user_data.loc[i,'day_night_label'] == user_data.loc[j,'day_night_label']:
                    distance = cdist(lat_long.values[i:i+1], lat_long.values[j:j+1], metric=dist)
            else:
                    distance = 15*cdist(lat_long.values[i:i+1], lat_long.values[j:j+1], metric=dist)
            distances[i,j] = distance
        else:
            distances[i,j] = 0
distances += distances.T

Ответы (1 шт):

Автор решения: Stanislav Volodarskiy

Если изменить порядок операций то можно добиться ускорения примерно в 500-1000 раз.

Комбинация squareform(pdist(...)) вычисляет попарные расстояния в виде квадратной матрицы.

factor - матрица коэффициентов (штрафов). Первоначально она вся заполнена штрафами. Затем все ячейки с одинаковыми значениями столбца day_night_label заполняются единицами.

В конце штрафы умножаются на расстояния:

distances = squareform(pdist(lat_long.values, metric=dist))
factor = np.full((lat_long.values.shape[0], lat_long.values.shape[0]), 15)
for v in user_data['day_night_label'].unique():
    index = (user_data['day_night_label'] == v).values
    factor[np.ix_(index, index)] = 1
distances *= factor

Предыдущий способ можно улучшить ещё примерно в полтора раза. Тут factor вычисляется через выражение pdist(ud, metric='hamming') которое вычисляет единицу если пара значений в user_data['day_night_label'] различны, ноль иначе. Матрица штрафов (она пока не матрица) тоже вычисляется через pdist!

distances = pdist(lat_long.values, metric=dist)
ud = user_data['day_night_label'].values.reshape(
    (user_data['day_night_label'].shape[0], 1)
)
factor = 1 + 14 * pdist(ud, metric='hamming')
distances = squareform(distances * factor)
→ Ссылка