Python - Сравните два массива NumPy - PullRequest
0 голосов
/ 12 октября 2018

У меня есть два числовых массива:

a = np.array([10,  3,  2,  1,  4,  5,  0,  7,  9,  8, 11,  6])  
b = np.array([ 2, 10,  1,  3,  4,  0, 11,  9,  7,  5,  8,  6])

Каждый числовой массив соответствует ранжированию месяцев года.Итак, для массива a месяц 10 (ноябрь) был лучшим.Второй лучший месяц соответствует месяцу 3 (апрель) и т. Д.

Моя цель - сравнить рейтинг b с рейтингом a с учетом позиции, которую каждый месяц занимает на каждом изрейтинги.Есть ли какая-либо метрика, которая может помочь мне в этом (если возможно, какая-то нормализованная оценка между 0 и 1, которая количественно определяет, насколько близки эти два рейтинга)?

Ответы [ 2 ]

0 голосов
/ 12 октября 2018

Вот метрика сходства, основанная на числе инверсии.Сначала приведем несколько примеров:

['Jan' 'Feb' 'Mar' 'Apr' 'May' 'Jun' 'Jul' 'Aug' 'Sep' 'Oct' 'Nov' 'Dec']
['Dec' 'Nov' 'Oct' 'Sep' 'Aug' 'Jul' 'Jun' 'May' 'Apr' 'Mar' 'Feb' 'Jan']
0.0

['Jan' 'Feb' 'Mar' 'Apr' 'May' 'Jun' 'Jul' 'Aug' 'Sep' 'Oct' 'Nov' 'Dec']
['Jan' 'Feb' 'Mar' 'Apr' 'May' 'Jun' 'Jul' 'Aug' 'Sep' 'Oct' 'Nov' 'Dec']
1.0

['May' 'Jun' 'Jul' 'Aug' 'Sep' 'Oct' 'Nov' 'Dec' 'Jan' 'Feb' 'Mar' 'Apr']
['Feb' 'Mar' 'Apr' 'May' 'Jun' 'Jul' 'Aug' 'Sep' 'Oct' 'Nov' 'Dec' 'Jan']
0.5909090909090908

['Jan' 'Feb' 'Mar' 'Apr' 'May' 'Jun' 'Jul' 'Aug' 'Sep' 'Oct' 'Nov' 'Dec']
['Jul' 'Aug' 'Sep' 'Oct' 'Nov' 'Dec' 'Jan' 'Feb' 'Mar' 'Apr' 'May' 'Jun']
0.4545454545454546

['Jan' 'Feb' 'Mar' 'Apr' 'May' 'Jun' 'Jul' 'Aug' 'Sep' 'Oct' 'Nov' 'Dec']
['Feb' 'Jan' 'Mar' 'Apr' 'May' 'Jun' 'Jul' 'Aug' 'Sep' 'Oct' 'Nov' 'Dec']
0.9848484848484849

[10  3  2  1  4  5  0  7  9  8 11  6]
[ 2 10  1  3  4  0 11  9  7  5  8  6]
0.8333333333333334

['Nov' 'Jun' 'Dec' 'Oct' 'Feb' 'Mar' 'Jan' 'Jul' 'Sep' 'Aug' 'May' 'Apr']
['Dec' 'Nov' 'Oct' 'May' 'Jun' 'Sep' 'Jan' 'Jul' 'Mar' 'Feb' 'Aug' 'Apr']
0.7121212121212122

['Jan' 'Aug' 'May' 'Feb' 'Dec' 'Apr' 'Sep' 'Mar' 'Nov' 'Jul' 'Oct' 'Jun']
['May' 'Jun' 'Dec' 'Oct' 'Jan' 'Aug' 'Nov' 'Jul' 'Sep' 'Feb' 'Mar' 'Apr']
0.48484848484848486

['Nov' 'Oct' 'Jul' 'Feb' 'Dec' 'Sep' 'Apr' 'May' 'Mar' 'Aug' 'Jan' 'Jun']
['Apr' 'Jul' 'Dec' 'Jan' 'Aug' 'Jun' 'Feb' 'Sep' 'Nov' 'May' 'Oct' 'Mar']
0.4696969696969697

['Dec' 'Jul' 'May' 'Mar' 'Feb' 'Oct' 'Aug' 'Jun' 'Apr' 'Sep' 'Nov' 'Jan']
['Sep' 'Jan' 'Jul' 'Apr' 'Jun' 'Oct' 'May' 'Mar' 'Dec' 'Nov' 'Feb' 'Aug']
0.3787878787878788

['2033-03' '2025-07' '2013-10' '2013-02' '2018-01' '2068-07' '2054-06'                                              
 '2002-05' '2055-04' '2030-05' '2034-09' '2040-09' '2024-03' '2022-11'                                              
 '2007-07' '2034-09' '2077-11' '2026-03' '2072-12' '2070-06' '2054-12'                                              
 '2067-11' '2003-01' '2011-09' '2051-10' '2058-01' '2081-05' '2058-12'                                              
 '2000-10' '2018-09' '2060-05' '2050-05' '2015-04' '2034-12' '2017-03'                                              
 '2043-05' '2001-10' '2047-06' '2050-06' '2034-10']                                                                 
['2002-05' '2051-10' '2007-07' '2018-01' '2043-05' '2050-06' '2034-12'                                              
 '2015-04' '2022-11' '2040-09' '2054-06' '2070-06' '2058-12' '2067-11'                                              
 '2077-11' '2017-03' '2050-05' '2011-09' '2072-12' '2025-07' '2013-02'                                              
 '2018-09' '2001-10' '2000-10' '2081-05' '2033-03' '2030-05' '2060-05'                                              
 '2013-10' '2026-03' '2034-09' '2034-10' '2054-12' '2003-01' '2024-03'                                              
 '2068-07' '2034-09' '2055-04' '2047-06' '2058-01']                                                                 
0.4717948717948718                                                                                                  

Количество инверсий - это минимальное количество соседних свопов, необходимое для переупорядочения одного ордера в другой.Это может быть что угодно от 0 до N (N-1) /2.

Код:

import numpy as np

def inversion_count_similarity(data1, data2):
    N = len(data1)
    o1 = np.argsort(data1, kind='mergesort')
    o2 = np.argsort(data2, kind='mergesort')
    o1inv = np.empty_like(o1)
    o1inv[o1] = np.arange(N)
    # pad to power of two
    order = np.arange(1<<N.bit_length())
    order[:N] = o2[o1inv]

    sum_ = 0
    for i in range(1, N.bit_length()+1):
        order = np.reshape(order, (-1, 1<<i))
        oo = np.argsort(order, axis = -1, kind='mergesort')
        ioo = np.empty_like(oo)
        ioo[np.arange(order.shape[0])[:, None], oo] = np.arange(1<<i)
        order[...] = order[np.arange(order.shape[0])[:, None], oo]
        hw = 1<<(i-1)
        sum_ += ioo[:, :hw].sum() - order.shape[0] * (hw-1)*hw // 2
    return 1 - (2*sum_)/((N-1)*N)

months = "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split()
months = np.array(months)
upsidedown = months[::-1]
cycle = months[np.r_[1:12, 0]]
cycle4 = months[np.r_[4:12, :4]]
cycle6 = months[np.r_[6:12, :6]]
singleflip = months[np.r_[1, 0, 2:12]]

a = np.array([10,  3,  2,  1,  4,  5,  0,  7,  9,  8, 11,  6])  
b = np.array([ 2, 10,  1,  3,  4,  0, 11,  9,  7,  5,  8,  6])

random = [[months[np.random.permutation(12)] for i in 'xx'] for j in 'xxxx']

a40 = np.random.randint(0, 1000, (40,)).view('m8[M]') + np.datetime64('2000-10')          
b40 = a40[np.random.permutation(40)]                          

for m1, m2 in [(months, upsidedown), (months, months), (cycle4, cycle),
               (months, cycle6), (months, singleflip), (a, b)] + random + [(a40, b40)]:
    print(m1)
    print(m2)
    print(inversion_count_similarity(m1, m2))
    print()

Попытка объяснения.

Сначала мы вычисляем относительный порядокпутем аргументирования обоих, а затем составления одной из результирующих перестановок с инверсией другой.Это дополняется степенью двойки, чтобы упростить реализацию следующего биссектрисового алгоритма.

Альтернативное эквивалентное определение числа инверсии приведенному выше является суммой по всем элементам числа меньших элементов, которыепомещенный над ним.

Используя это определение, мы можем рассмотреть специальный случай последовательности, которая разбивается на две отсортированные половины.Мы также предполагаем, что элементы - это просто индексы, то есть первые n чисел (вот почему нам нужно ioo в коде; ioo - обратная перестановка порядка сортировки oo, то есть расположение первых n чисел, которые необходимо переставитьпо oo сортироваться).Если бы он был полностью упорядочен, то элементы в левой половине были бы просто 0, 1, ... и суммировались бы в (n / 2) (n / 2-1) / 2.Нетрудно убедиться, что если вместо элемента i в позиции i у нас i + d в позиции i, то над ним должно быть d меньших элементов (потому что точно i + d меньших чисел и i из них слева, потому чтомы предположили, что половина была отсортирована).Таким образом, мы можем взять сумму по элементам в левой половине и вычесть (n / 2) (n / 2-1) / 2, чтобы получить число инверсии в этом особом случае.

Также легко проверитьчто, начиная с общего случая несортированных половин, числа инверсии каждой половины плюс номер инверсии всей последовательности после сортировки каждой половины суммы по полному числу инверсии.(Это снова легко увидеть, используя альтернативное определение числа инверсии.)

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

Обратите внимание, что сортировка двух отсортированных половинок фактически является O (n).Мы могли бы использовать heapq.merge для реализации O (n).На практике, однако, argsort почти наверняка будет быстрее, даже если это O (n log n).

0 голосов
/ 12 октября 2018

Что ж, одним из способов сравнения двух наборов данных является KL Divergence.Это дает оценку, которая говорит нам, как одно распределение вероятностей отличается от второго, эталонного распределения вероятностей.Однако, поскольку он сравнивает два распределения вероятностей, вам нужно убедиться, что массивы суммируются с одним.

divergence = np.sum(np.where(p != 0, p * np.log(p / q), 0))

Sklearn предоставляет метрику взаимной информации, которая тесно связана (не совсем) с расхождением KL.

sklearn.metrics.mutual_info_score(a,b)
...