Pandas разреженные данные. Рамка к разреженной матрице без генерации плотной матрицы в памяти.
Есть ли способ конвертировать из pandas.SparseDataFrame
в scipy.sparse.csr_matrix
, не создавая в памяти плотную матрицу?
scipy.sparse.csr_matrix(df.values)
не работает, поскольку он генерирует плотную матрицу, которая передается в csr_matrix
.
Спасибо заранее!
Ответы
Ответ 1
Pandas docs рассказывает об экспериментальном преобразовании в scipy sparse, SparseSeries.to_coo:
http://pandas-docs.github.io/pandas-docs-travis/sparse.html#interaction-with-scipy-sparse
================
edit - это специальная функция из мультииндекса, а не для фрейма данных. См. Другие ответы для этого. Обратите внимание на разницу в датах.
============
Начиная с 0.20.0, существует sdf.to_coo()
и multiindex ss.to_coo()
. Так как разреженная матрица является по существу 2d, имеет смысл требовать мультииндекс для (эффективно) 1d-данных. Хотя dataframe может представлять таблицу или 2d-массив.
Когда я впервые ответил на этот вопрос, эта редкая функция DataFrame/series была экспериментальной (июнь 2015 г.).
Ответ 2
Pandas 0,20.0 +:
Начиная с версии pandas версии 0.20.0, выпущенной 5 мая 2017 года, для этого есть однострочный:
from scipy import sparse
def sparse_df_to_csr(df):
return sparse.csr_matrix(df.to_coo())
Это использует новый метод to_coo()
.
Предыдущие версии:
Основываясь на Victor May, ответьте здесь немного быстрее, но он работает только в том случае, если весь SparseDataFrame
разрежен со всеми BlockIndex
(обратите внимание: если он был создан с помощью get_dummies
, это будет так).
Изменить. Я изменил это, чтобы он работал с ненулевым значением заполнения. CSR не имеет собственного значения ненулевого заполнения, поэтому вам придется записывать его извне.
import numpy as np
import pandas as pd
from scipy import sparse
def sparse_BlockIndex_df_to_csr(df):
columns=df.columns
zipped_data = zip(*[(df[col].sp_values - df[col].fill_value,
df[col].sp_index.to_int_index().indices)
for col in columns])
data, rows=map(list, zipped_data)
cols=[np.ones_like(a)*i for (i,a) in enumerate(data)]
data_f = np.concatenate(data)
rows_f = np.concatenate(rows)
cols_f = np.concatenate(cols)
arr = sparse.coo_matrix((data_f, (rows_f, cols_f)),
df.shape, dtype=np.float64)
return arr.tocsr()
Ответ 3
Ответ @Marigold делает трюк, но он медленный из-за доступа ко всем элементам в каждом столбце, включая нули. Основываясь на этом, я написал следующий быстрый "грязный" код, который работает примерно на 50 раз быстрее на матрице 1000x1000 с плотностью около 1%. Мой код также обрабатывает плотные столбцы соответствующим образом.
def sparse_df_to_array(df):
num_rows = df.shape[0]
data = []
row = []
col = []
for i, col_name in enumerate(df.columns):
if isinstance(df[col_name], pd.SparseSeries):
column_index = df[col_name].sp_index
if isinstance(column_index, BlockIndex):
column_index = column_index.to_int_index()
ix = column_index.indices
data.append(df[col_name].sp_values)
row.append(ix)
col.append(len(df[col_name].sp_values) * [i])
else:
data.append(df[col_name].values)
row.append(np.array(range(0, num_rows)))
col.append(np.array(num_rows * [i]))
data_f = np.concatenate(data)
row_f = np.concatenate(row)
col_f = np.concatenate(col)
arr = coo_matrix((data_f, (row_f, col_f)), df.shape, dtype=np.float64)
return arr.tocsr()
Ответ 4
Здесь решение, которое заполняет разреженный столбец матрицы столбцом (предполагается, что вы можете поместить хотя бы один столбец в память).
import pandas as pd
import numpy as np
from scipy.sparse import lil_matrix
def sparse_df_to_array(df):
""" Convert sparse dataframe to sparse array csr_matrix used by
scikit learn. """
arr = lil_matrix(df.shape, dtype=np.float32)
for i, col in enumerate(df.columns):
ix = df[col] != 0
arr[np.where(ix), i] = df.ix[ix, col]
return arr.tocsr()
Ответ 5
EDIT. Этот метод на самом деле имеет плотное представление на каком-то этапе, поэтому он не решает вопрос.
Вы должны использовать экспериментальный метод .to_coo()
в pandas [1] следующим образом:
df, idx_rows, idx_cols = df.stack().to_sparse().to_coo()
df = df.tocsr()
Этот метод вместо принятия DataFrame
(rows/columns) занимает Series
со строками и столбцами в MultiIndex
(вот почему вам нужен метод .stack()
). Этот Series
с MultiIndex
должен быть SparseSeries
, и даже если ваш ввод SparseDataFrame
, .stack()
возвращает регулярный Series
. Итак, перед вызовом .to_coo()
вам нужно использовать метод .to_sparse()
.
Series
, возвращаемый .stack()
, даже если он не SparseSeries
содержит только элементы, которые не являются нулевыми, поэтому он не должен занимать больше памяти, чем разреженная версия (по крайней мере, с np.nan
, когда тип np.float
).