Pandas 稀疏数据帧到稀疏矩阵,不在内存中生成密集矩阵
Pandas sparse dataFrame to sparse matrix, without generating a dense matrix in memory
有没有办法在不在内存中生成密集矩阵的情况下将 pandas.SparseDataFrame
转换为 scipy.sparse.csr_matrix
?
scipy.sparse.csr_matrix(df.values)
不起作用,因为它会生成一个密集矩阵,该矩阵被投射到 csr_matrix
。
提前致谢!
Pandas 文档讨论了到 scipy 稀疏的实验性转换,SparseSeries.to_coo:
http://pandas-docs.github.io/pandas-docs-travis/sparse.html#interaction-with-scipy-sparse
================
edit - 这是来自多索引的特殊函数,而不是数据框。请参阅其他答案。请注意日期差异。
============
从 0.20.0 开始,有一个 sdf.to_coo()
和一个多索引 ss.to_coo()
。由于稀疏矩阵本质上是二维的,因此(有效地)一维数据系列需要多索引是有意义的。虽然数据框可以表示 table 或二维数组。
当我第一次回答这个问题时,这个稀疏 dataframe/series 功能是实验性的(2015 年 6 月)。
这是一个逐列填充稀疏矩阵的解决方案(假设您可以将至少一列放入内存)。
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()
@Marigold 的回答可以解决问题,但由于访问每列中的所有元素(包括零),所以速度很慢。在此基础上,我编写了以下快速 n' 脏代码,它在密度约为 1% 的 1000x1000 矩阵上运行速度提高了约 50 倍。我的代码还可以适当地处理密集列。
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()
Pandas 0.20.0+:
从 2017 年 5 月 5 日发布的 pandas 版本 0.20.0 开始,有一条直线:
from scipy import sparse
def sparse_df_to_csr(df):
return sparse.csr_matrix(df.to_coo())
这使用了新的 to_coo()
method。
早期版本:
基于 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()
编辑:这个方法实际上在某个阶段有一个密集的表示,所以它没有解决问题。
您应该可以通过以下方式使用 pandas [1] 中的实验性 .to_coo()
方法:
df, idx_rows, idx_cols = df.stack().to_sparse().to_coo()
df = df.tocsr()
此方法不是采用 DataFrame
(行/列),而是采用 Series
,行和列位于 MultiIndex
(这就是为什么需要 .stack()
方法)。这个 Series
和 MultiIndex
需要是一个 SparseSeries
,即使你的输入是一个 SparseDataFrame
,.stack()
returns 一个普通的 Series
。所以,你需要在调用.to_coo()
.
之前使用.to_sparse()
方法
.stack()
返回的Series
,即使不是SparseSeries
也只包含不为空的元素,所以它不应该比稀疏版本占用更多内存(至少在 np.nan
类型为 np.float
时)。
从 Pandas 版本 0.25 开始,SparseSeries
和 SparseDataFrame
已弃用。 DataFrames 现在支持 Sparse Dtypes 用于具有稀疏数据的列。稀疏方法可通过 sparse
访问器使用,因此转换一行现在看起来像这样:
sparse_matrix = scipy.sparse.csr_matrix(df.sparse.to_coo())
有没有办法在不在内存中生成密集矩阵的情况下将 pandas.SparseDataFrame
转换为 scipy.sparse.csr_matrix
?
scipy.sparse.csr_matrix(df.values)
不起作用,因为它会生成一个密集矩阵,该矩阵被投射到 csr_matrix
。
提前致谢!
Pandas 文档讨论了到 scipy 稀疏的实验性转换,SparseSeries.to_coo:
http://pandas-docs.github.io/pandas-docs-travis/sparse.html#interaction-with-scipy-sparse
================
edit - 这是来自多索引的特殊函数,而不是数据框。请参阅其他答案。请注意日期差异。
============
从 0.20.0 开始,有一个 sdf.to_coo()
和一个多索引 ss.to_coo()
。由于稀疏矩阵本质上是二维的,因此(有效地)一维数据系列需要多索引是有意义的。虽然数据框可以表示 table 或二维数组。
当我第一次回答这个问题时,这个稀疏 dataframe/series 功能是实验性的(2015 年 6 月)。
这是一个逐列填充稀疏矩阵的解决方案(假设您可以将至少一列放入内存)。
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()
@Marigold 的回答可以解决问题,但由于访问每列中的所有元素(包括零),所以速度很慢。在此基础上,我编写了以下快速 n' 脏代码,它在密度约为 1% 的 1000x1000 矩阵上运行速度提高了约 50 倍。我的代码还可以适当地处理密集列。
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()
Pandas 0.20.0+:
从 2017 年 5 月 5 日发布的 pandas 版本 0.20.0 开始,有一条直线:
from scipy import sparse
def sparse_df_to_csr(df):
return sparse.csr_matrix(df.to_coo())
这使用了新的 to_coo()
method。
早期版本:
基于 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()
编辑:这个方法实际上在某个阶段有一个密集的表示,所以它没有解决问题。
您应该可以通过以下方式使用 pandas [1] 中的实验性 .to_coo()
方法:
df, idx_rows, idx_cols = df.stack().to_sparse().to_coo()
df = df.tocsr()
此方法不是采用 DataFrame
(行/列),而是采用 Series
,行和列位于 MultiIndex
(这就是为什么需要 .stack()
方法)。这个 Series
和 MultiIndex
需要是一个 SparseSeries
,即使你的输入是一个 SparseDataFrame
,.stack()
returns 一个普通的 Series
。所以,你需要在调用.to_coo()
.
.to_sparse()
方法
.stack()
返回的Series
,即使不是SparseSeries
也只包含不为空的元素,所以它不应该比稀疏版本占用更多内存(至少在 np.nan
类型为 np.float
时)。
从 Pandas 版本 0.25 开始,SparseSeries
和 SparseDataFrame
已弃用。 DataFrames 现在支持 Sparse Dtypes 用于具有稀疏数据的列。稀疏方法可通过 sparse
访问器使用,因此转换一行现在看起来像这样:
sparse_matrix = scipy.sparse.csr_matrix(df.sparse.to_coo())