Получить норму пустых разреженных строк матрицы

У меня есть разреженная матрица, которую я получил с помощью объекта Sklearn's TfidfVectorizer:

vect = TfidfVectorizer(sublinear_tf=True, max_df=0.5, analyzer='word', vocabulary=my_vocab, stop_words='english')
tfidf = vect.fit_transform([my_docs])

Разреженная матрица (вычтя числа для общности):

<sparse matrix of type '<type 'numpy.float64'>'
with stored elements in Compressed Sparse Row format>]

Я пытаюсь получить числовое значение для каждой строки, чтобы сказать мне, насколько высоко в документе были условия, которые я ищу. Меня не волнует, в каких словах оно содержится, я просто хочу знать, сколько в нем слов. Поэтому я хочу получить норму для каждой строки или строки. Тем не менее, мне очень трудно работать с NumPy, чтобы получить это.

Мой первый подход был просто сделать:

tfidf[i] * numpy.transpose(tfidf[i])

Тем не менее, NumPy, по-видимому, не будет транспонировать массив с менее чем одним измерением, так что он будет просто квадрат вектора Итак, я попытался сделать:

tfidf[i] * numpy.transpose(numpy.atleast_2d(tfidf[0]))

Но numpy.transpose(numpy.atleast_2d(tfidf[0])) по-прежнему не будет транспонировать строку.

Я перешел к попытке получить норму ряда (этот подход, вероятно, лучше в любом случае). Мой первоначальный подход был с использованием numpy.linalg.

numpy.linalg.norm(tfidf[0])

Но это дало мне ошибку "несоответствие размеров". Поэтому я попытался вычислить норму вручную. Я начал с того, что просто установил переменную, равную версии массива с разреженной матрицей, и распечатал len первой строки:

my_array = numpy.array(tfidf)
print my_array
print len(my_array[0])

Он правильно печатает my_array, но когда я пытаюсь получить доступ к len, он говорит мне:

IndexError: 0-d arrays can't be indexed

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

2 ответа

Решение

Некоторые простые фальшивые данные:

a = np.arange(9.).reshape(3,3)
s = sparse.csr_matrix(a)

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

np.sqrt(s.multiply(s).sum(1))

И перенормированный s было бы

s.multiply(1/np.sqrt(s.multiply(s).sum(1)))

или сохранить его редким перед перенормировкой:

s.multiply(sparse.csr_matrix(1/np.sqrt(s.multiply(s).sum(1))))

Чтобы получить из нее обычную матрицу или массив, используйте:

m = s.todense()
a = s.toarray()

Если у вас достаточно памяти для плотной версии, вы можете получить норму каждой строки с помощью:

n = np.sqrt(np.einsum('ij,ij->i',a,a))

или же

n = np.apply_along_axis(np.linalg.norm, 1, a)

Для нормализации можно сделать

an = a / n[:, None]

или, чтобы нормализовать исходный массив на месте:

a /= n[:, None]

[:, None] вещь в основном переносит n быть вертикальным массивом.

scipy.sparse Это отличный пакет, и он продолжает улучшаться с каждым выпуском, но многие вещи еще только наполовину приготовлены, и вы можете добиться значительных улучшений производительности, если сами реализуете некоторые алгоритмы. Например, 7-кратное улучшение по сравнению с реализацией @askewchan с использованием функций scipy:

In [18]: a = sps.rand(1000, 1000, format='csr')

In [19]: a
Out[19]: 
<1000x1000 sparse matrix of type '<type 'numpy.float64'>'
    with 10000 stored elements in Compressed Sparse Row format>

In [20]: %timeit a.multiply(a).sum(1)
1000 loops, best of 3: 288 us per loop

In [21]: %timeit np.add.reduceat(a.data * a.data, a.indptr[:-1])
10000 loops, best of 3: 36.8 us per loop

In [24]: np.allclose(a.multiply(a).sum(1).ravel(),
    ...:             np.add.reduceat(a.data * a.data, a.indptr[:-1]))
Out[24]: True

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

norm_rows = np.sqrt(np.add.reduceat(a.data * a.data, a.indptr[:-1]))
nnz_per_row = np.diff(a.indptr)
a.data /= np.repeat(norm_rows, nnz_per_row)

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

Как насчет использования собственного scipy API scipy.sparse.linalg.norm?

Подробнее читайте на https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.linalg.norm.html.

Другие вопросы по тегам