Python, Scipy: создание триплетов с использованием большой матрицы смежности
Я использую матрицу смежности для представления сети друзей, которую можно визуально интерпретировать как
Mary 0 1 1 1
Joe 1 0 1 1
Bob 1 1 0 1
Susan 1 1 1 0
Mary Joe Bob Susan
Используя эту матрицу, я хочу скомпилировать список всех возможных треугольников дружбы с условием, что пользователь 1 дружит с пользователем 2, а пользователь 2 дружит с пользователем 3. Для моего списка не требуется, чтобы пользователь 1 дружит с пользователем 3.
(joe, mary, bob)
(joe, mary, susan)
(bob, mary, susan)
(bob, joe, susan)
У меня есть немного кода, который хорошо работает с маленькими треугольниками, но мне нужно его масштабировать для очень больших разреженных матриц.
from numpy import *
from scipy import *
def buildTriangles(G):
# G is a sparse adjacency matrix
start = time.time()
ctr = 0
G = G + G.T # I do this to make sure it is symmetric
triples = []
for i in arange(G.shape[0] - 1): # for each row but the last one
J,J = G[i,:].nonzero() # J: primary friends of user i
# I do J,J because I do not care about the row values
J = J[ J < i ] # only computer the lower triangle to avoid repetition
for j in J:
K, buff = G[:,j].nonzero() # K: secondary friends of user i
K = K[ K > i ] # only compute below i to avoid repetition
for k in K:
ctr = ctr + 1
triples.append( (i,j,k) )
print("total number of triples: %d" % ctr)
print("run time is %.2f" % (time.time() - start())
return triples
Мне удалось запустить код на csr_matrix примерно через 21 минуту. Матрица составляла 1032570 x 1032570 и содержала 88910 сохраненных элементов. Всего было создано 2178893 триплетов.
Мне нужно сделать что-то подобное с разреженной матрицей 1968654 x 1968654 с хранимыми элементами 9428596.
Я очень новичок в python (чуть меньше месяца опыта), а не в линейной алгебре, поэтому мой код не использует операции над матрицами.
Может ли кто-нибудь сделать какие-либо предложения по улучшению или сообщить мне, если моя цель даже реалистична?
Ответы
Ответ 1
Я думаю, вы можете найти треугольники только в строках или столбцах. например:
Susan 1 1 1 0
Mary Joe Bob Susan
это означает, что Мэри, Джо, Боб все друзья Сьюзан, поэтому используйте комбинации, чтобы выбрать двух человек из [Мэри, Джо, Боб], и объединить его с Сьюзан получит один треугольник. itertools.combinations() сделать это быстро.
Вот код:
import itertools
import numpy as np
G = np.array( # clear half of the matrix first
[[0,0,0,0],
[1,0,0,0],
[1,1,0,0],
[1,1,1,0]])
triples = []
for i in xrange(G.shape[0]):
row = G[i,:]
J = np.nonzero(row)[0].tolist() # combinations() with list is faster than NumPy array.
for t1,t2 in itertools.combinations(J, 2):
triples.append((i,t1,t2))
print triples
Ответ 2
Вот несколько советов по оптимизации:
K = K[ K > i ] # only compute below i to avoid repetition
for k in K:
ctr = ctr + 1
triples.append( (i,j,k) )
Не увеличивайте цикл, это ужасно медленно. Просто будет ctr += K.shape[0]
. Затем полностью исключить наиболее глубоко вложенную петлю, заменив append
на
triples += ((i, j, k) for k in K[K > i])
Теперь, если вы хотите выполнить реальную производительность этой задачи, вам придется попасть в некоторую линейную алгебру. "Я хочу скомпилировать список всех возможных треугольников дружбы" означает, что вы хотите скомпоновать матрицу смежности, которую вы можете сделать с помощью простого **2
.
Тогда осознайте, что 1.968.654² означает очень большую матрицу, и хотя она очень скудная, ее квадрат будет намного меньше и займет много памяти. (Однажды я столкнулся с аналогичной проблемой, когда я рассматривал связи между статьями Википедии на расстоянии два, что потребовало 20 минут для решения на суперкомпьютерном кластере node, на С++. Это не тривиальная проблема. Матрица смежности Wikipedia была несколько на порядок более плотные.)