Эффективно рассчитать и сохранить матрицу подобия
Для проекта системы рекомендаций в классе я в настоящее время пытаюсь построить и сохранить матрицу подобия на основе элементов для набора данных с примерно 7000 пользователей (строки) и 4000 фильмов (столбцы). Так что у меня есть сводная таблица с идентификаторами пользователей в качестве индекса, идентификаторами MovieID в качестве столбцов и рейтингами в качестве значений. Как вы можете себе представить, существует множество 0-рейтингов.
В настоящее время я использую функцию Pearsonr из пакета scipy. Я решил, что для хранения всех расстояний мне нужно вычислить коэффициент Пирсона между всеми столбцами и сохранить их в симметричной матрице кинофильмов. Мой код до сих пор (как вы можете видеть, я новичок в Python/ кодирования):
import pandas as pd
import numpy as np
from scipy.stats import pearsonr
pd.read_csv('data.csv')
data = data.pivot(index = 'UserID', columns = 'MovieID', values = "Rating")
similarity_data = pd.DataFrame(index=data.columns, columns=data.columns)
for i in range(0,len(data.columns)):
for j in range(0,len(data.columns)):
similarity_data.iloc[i,j] = pearsonr(data.iloc[:,i],data.iloc[:,j])[0]
Ну, как вы можете себе представить, это занимает вечность, и я очень хочу выяснить, как сделать это более эффективно. Моей первой идеей было воспользоваться симметричностью матрицы. Но я не мог понять, как.
Моя идея была что-то вроде:
for i in range(0,len(data.columns)):
for j in range(0,len(data.columns)):
similarity_data.iloc[i,j] = pearsonr(data.iloc[:,i],data.iloc[:,j+i])[0]
similarity_data[j,i] = similarity_data.iloc[i,j]
Однако, даже если бы я заставил это работать, я боюсь, что проблема здесь в двух циклах. Я пытался каким-то образом использовать карту или лямбда-подход, но ничего не получилось.
Любая идея, как улучшить это (вероятно, их много)?
2 ответа
Вы обязательно захотите использовать np.corrcoef
, который будет примерно в 1000 раз быстрее, чем наивный цикл scipy.stats.pearsonr
, Например:
from scipy.stats import pearsonr
import numpy as np
import pandas as pd
# make some small data
df = pd.DataFrame(np.random.rand(100, 40))
C1 = np.array([[pearsonr(df[i], df[j])[0] for i in df] for j in df])
C2 = np.corrcoef(df.values.T)
np.allclose(C1, C2)
# True
Вот времена:
%timeit np.array([[pearsonr(df[i], df[j])[0] for i in df] for j in df])
10 loops, best of 3: 154 ms per loop
%timeit np.corrcoef(df.values.T)
10000 loops, best of 3: 116 µs per loop
Тем не менее, ваш результат будет плотной матрицей с примерно 16 миллионами записей, так что это не будет быстрое вычисление. Вы можете подумать о том, действительно ли вам нужно хранить все эти значения или вы можете использовать алгоритм, который (например) просто вычисляет корреляции ближайших соседей.
Не было бы np.corrcoef(data)
дать вам ту же матрицу корреляции?
Если нет, вы сможете примерно удвоить производительность, рассчитав только половину симметричной матрицы результатов и не вызывая pearsonr()
вообще когда i
равно j
,