Python — Как ускорить косинусное подобие при подсчете массивов

#python #arrays #list #scipy

#python #массивы #Список #scipy

Вопрос:

Мне нужно вычислить функцию косинусного подобия для очень большого набора. Этот набор представляет пользователей и каждого пользователя в виде массива идентификаторов объектов. Пример ниже:

 user_1 = [1,4,6,100,3,1]
user_2 = [4,7,8,3,3,2,200,9,100]
  

Если я правильно понимаю, для вычисления подобия косинуса мне нужно сначала создать подсчитывающие массивы, чтобы иметь общее представление для каждого из них. Затем мне нужно вычислить функцию подобия косинуса. Для подсчета массивов я имею в виду следующее:

 #user_1 array
#                        1,2,3,4,5,6,[7-99],100,[101-200]
user_1_counting_array = [2,0,1,1,0,1,.......,1,.........]
user_2_counting_array = [0,1,2,1,0,0,1,1,1,.,1,.......,1]
  

(В данном случае точки представляют нули)

после того, как я получу это общее представление, я использую функцию косинусного подобия из sklearn.

 from scipy import spatial
s = 1 - spatial.distance.cosine(user_1_counting_array, user_2_counting_array)
  

Проблема в том, что когда я на самом деле запускаю код, все происходит чрезвычайно медленно, и у меня более 1 МЛН пользователей. Я понимаю, что комбинации будет много, но я думаю, что то, как я создаю общее представление, создает очень большое узкое место.

Для полноты картины ниже представлена моя реализация:

 from collections import Counter
from scipy import spatial

def fill_array(array, counter):
    for c in counter:
        array[c] = counter[c]
    return array

user_1 = [1,4,6,100,3,1]
user_2 = [4,7,8,3,3,2,200,9,100]

user_1_c = Counter(user_1)
user_2_c = Counter(user_2)

if max(user_1_c) > max(user_2_c):
    max_a = max(user_1_c) 1
else:
    max_a = max(user_2_c) 1

user_1_c_array = [0]*max_a
user_2_c_array = [0]*max_a

fill_array(user_1_c_array, user_1_c)
fill_array(user_2_c_array, user_2_c)

result = 1 - spatial.distance.cosine(user_1_c_array, user_2_c_array)
  

Комментарии:

1. Я предполагаю, что вы делаете это для графиков (например, друзей / соединений). В этом случае вам не нужно использовать косинусное подобие для всех перед max_index . Вам нужно использовать только вектор, который содержит все элементы, которые существуют хотя бы в одном из двух списков .

2. Вы правы, но с точки зрения вычислений у меня такое чувство, что для создания этих векторов требуется гораздо больше вычислений. Вы так не думаете? @RockyLi

3. На самом деле это намного меньше, поскольку вы перебираете огромное количество пустых данных. Это также быстрее, когда вы используете хэш (set, dictionary).

Ответ №1:

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

 user_1 = [1,4,6,100,3,1]
user_2 = [4,7,8,3,3,2,200,9,100]

# Create a list of unique elements
uniq = list(set(user_1   user_2))

# Map all unique entrees in user_1 and user_2
duniq = {k:0 for k in uniq}

def create_vector(duniq, l):
    dx = duniq.copy()
    dx.update(Counter(l)) # Count the values
    return list(dx.values()) # Return a list

u1 = create_vector(duniq, user_1)
u2 = create_vector(duniq, user_2)

# u1, u2:

u1 = [2, 0, 1, 1, 1, 0, 0, 0, 0, 1]
u2 = [0, 1, 2, 1, 0, 1, 1, 1, 1, 1]
  

Затем вы можете передать эти 2 вектора в spatial.distance.cosine