在 pandas 中高效地创建稀疏数据透视表?
Efficiently create sparse pivot tables in pandas?
我正在将包含两列(A 和 B)的记录列表转换为矩阵表示。我一直在 pandas 中使用 pivot 函数,但结果最终变得相当大。 pandas 是否支持转换为稀疏格式?我知道我可以旋转它,然后将它变成某种稀疏表示,但没有我想要的那么优雅。我的最终目标是将其用作预测模型的输入。
或者,pandas 之外是否有某种稀疏枢轴功能?
编辑:这里是一个非稀疏主元的例子
import pandas as pd
frame=pd.DataFrame()
frame['person']=['me','you','him','you','him','me']
frame['thing']=['a','a','b','c','d','d']
frame['count']=[1,1,1,1,1,1]
frame
person thing count
0 me a 1
1 you a 1
2 him b 1
3 you c 1
4 him d 1
5 me d 1
frame.pivot('person','thing')
count
thing a b c d
person
him NaN 1 NaN 1
me 1 NaN NaN 1
you 1 NaN 1 NaN
这将创建一个可以包含所有可能的人和物组合的矩阵,但它并不稀疏。
http://docs.scipy.org/doc/scipy/reference/sparse.html
稀疏矩阵占用较少 space 因为它们可以表示 NaN 或 0 之类的东西。如果我有一个非常大的数据集,这个旋转函数可以生成一个矩阵,由于数量很大,所以应该是稀疏的NaN 或 0。我希望我可以通过立即生成稀疏的东西而不是创建密集矩阵然后将其转换为稀疏矩阵来节省很多 space/memory。
这是一种基于人和物的数据和索引创建稀疏 scipy 矩阵的方法。 person_u
和 thing_u
是代表您要创建的数据透视表的行和列的唯一条目的列表。注意:这假定您的计数列已经具有您想要的值。
from scipy.sparse import csr_matrix
person_u = list(sort(frame.person.unique()))
thing_u = list(sort(frame.thing.unique()))
data = frame['count'].tolist()
row = frame.person.astype('category', categories=person_u).cat.codes
col = frame.thing.astype('category', categories=thing_u).cat.codes
sparse_matrix = csr_matrix((data, (row, col)), shape=(len(person_u), len(thing_u)))
>>> sparse_matrix
<3x4 sparse matrix of type '<type 'numpy.int64'>'
with 6 stored elements in Compressed Sparse Row format>
>>> sparse_matrix.todense()
matrix([[0, 1, 0, 1],
[1, 0, 0, 1],
[1, 0, 1, 0]])
根据您最初的问题,scipy 稀疏矩阵应该足以满足您的需求,但如果您希望拥有稀疏数据框,您可以执行以下操作:
dfs=pd.SparseDataFrame([ pd.SparseSeries(sparse_matrix[i].toarray().ravel(), fill_value=0)
for i in np.arange(sparse_matrix.shape[0]) ], index=person_u, columns=thing_u, default_fill_value=0)
>>> dfs
a b c d
him 0 1 0 1
me 1 0 0 1
you 1 0 1 0
>>> type(dfs)
pandas.sparse.frame.SparseDataFrame
我遇到了类似的问题,我偶然发现了这个 post。唯一的区别是我在 DataFrame
中有两列定义了输出矩阵的 "row dimension" (i
)。我认为这可能是一个有趣的概括,我使用了 grouper
:
# function
import pandas as pd
from scipy.sparse import csr_matrix
def df_to_sm(data, vars_i, vars_j):
grpr_i = data.groupby(vars_i).grouper
idx_i = grpr_i.group_info[0]
grpr_j = data.groupby(vars_j).grouper
idx_j = grpr_j.group_info[0]
data_sm = csr_matrix((data['val'].values, (idx_i, idx_j)),
shape=(grpr_i.ngroups, grpr_j.ngroups))
return data_sm, grpr_i, grpr_j
# example
data = pd.DataFrame({'var_i_1' : ['a1', 'a1', 'a1', 'a2', 'a2', 'a3'],
'var_i_2' : ['b2', 'b1', 'b1', 'b1', 'b1', 'b4'],
'var_j_1' : ['c2', 'c3', 'c2', 'c1', 'c2', 'c3'],
'val' : [1, 2, 3, 4, 5, 6]})
data_sm, _, _ = df_to_sm(data, ['var_i_1', 'var_i_2'], ['var_j_1'])
data_sm.todense()
@khammel 之前发布的答案很有用,但不幸的是,由于 pandas 和 Python 的更改而不再有效。以下应产生相同的输出:
from scipy.sparse import csr_matrix
from pandas.api.types import CategoricalDtype
person_c = CategoricalDtype(sorted(frame.person.unique()), ordered=True)
thing_c = CategoricalDtype(sorted(frame.thing.unique()), ordered=True)
row = frame.person.astype(person_c).cat.codes
col = frame.thing.astype(thing_c).cat.codes
sparse_matrix = csr_matrix((frame["count"], (row, col)), \
shape=(person_c.categories.size, thing_c.categories.size))
>>> sparse_matrix
<3x4 sparse matrix of type '<class 'numpy.int64'>'
with 6 stored elements in Compressed Sparse Row format>
>>> sparse_matrix.todense()
matrix([[0, 1, 0, 1],
[1, 0, 0, 1],
[1, 0, 1, 0]], dtype=int64)
dfs = pd.SparseDataFrame(sparse_matrix, \
index=person_c.categories, \
columns=thing_c.categories, \
default_fill_value=0)
>>> dfs
a b c d
him 0 1 0 1
me 1 0 0 1
you 1 0 1 0
主要变化是:
.astype()
不再接受 "categorical"。您必须创建一个 CategoricalDtype 对象。
sort()
不再有效
其他变化更肤浅:
- 使用类别大小而不是唯一系列对象的长度,只是因为我不想不必要地制作另一个对象
csr_matrix
(frame["count"]
)的数据输入不需要是列表对象
- pandas
SparseDataFrame
现在直接接受一个 scipy.sparse 对象
我正在将包含两列(A 和 B)的记录列表转换为矩阵表示。我一直在 pandas 中使用 pivot 函数,但结果最终变得相当大。 pandas 是否支持转换为稀疏格式?我知道我可以旋转它,然后将它变成某种稀疏表示,但没有我想要的那么优雅。我的最终目标是将其用作预测模型的输入。
或者,pandas 之外是否有某种稀疏枢轴功能?
编辑:这里是一个非稀疏主元的例子
import pandas as pd
frame=pd.DataFrame()
frame['person']=['me','you','him','you','him','me']
frame['thing']=['a','a','b','c','d','d']
frame['count']=[1,1,1,1,1,1]
frame
person thing count
0 me a 1
1 you a 1
2 him b 1
3 you c 1
4 him d 1
5 me d 1
frame.pivot('person','thing')
count
thing a b c d
person
him NaN 1 NaN 1
me 1 NaN NaN 1
you 1 NaN 1 NaN
这将创建一个可以包含所有可能的人和物组合的矩阵,但它并不稀疏。
http://docs.scipy.org/doc/scipy/reference/sparse.html
稀疏矩阵占用较少 space 因为它们可以表示 NaN 或 0 之类的东西。如果我有一个非常大的数据集,这个旋转函数可以生成一个矩阵,由于数量很大,所以应该是稀疏的NaN 或 0。我希望我可以通过立即生成稀疏的东西而不是创建密集矩阵然后将其转换为稀疏矩阵来节省很多 space/memory。
这是一种基于人和物的数据和索引创建稀疏 scipy 矩阵的方法。 person_u
和 thing_u
是代表您要创建的数据透视表的行和列的唯一条目的列表。注意:这假定您的计数列已经具有您想要的值。
from scipy.sparse import csr_matrix
person_u = list(sort(frame.person.unique()))
thing_u = list(sort(frame.thing.unique()))
data = frame['count'].tolist()
row = frame.person.astype('category', categories=person_u).cat.codes
col = frame.thing.astype('category', categories=thing_u).cat.codes
sparse_matrix = csr_matrix((data, (row, col)), shape=(len(person_u), len(thing_u)))
>>> sparse_matrix
<3x4 sparse matrix of type '<type 'numpy.int64'>'
with 6 stored elements in Compressed Sparse Row format>
>>> sparse_matrix.todense()
matrix([[0, 1, 0, 1],
[1, 0, 0, 1],
[1, 0, 1, 0]])
根据您最初的问题,scipy 稀疏矩阵应该足以满足您的需求,但如果您希望拥有稀疏数据框,您可以执行以下操作:
dfs=pd.SparseDataFrame([ pd.SparseSeries(sparse_matrix[i].toarray().ravel(), fill_value=0)
for i in np.arange(sparse_matrix.shape[0]) ], index=person_u, columns=thing_u, default_fill_value=0)
>>> dfs
a b c d
him 0 1 0 1
me 1 0 0 1
you 1 0 1 0
>>> type(dfs)
pandas.sparse.frame.SparseDataFrame
我遇到了类似的问题,我偶然发现了这个 post。唯一的区别是我在 DataFrame
中有两列定义了输出矩阵的 "row dimension" (i
)。我认为这可能是一个有趣的概括,我使用了 grouper
:
# function
import pandas as pd
from scipy.sparse import csr_matrix
def df_to_sm(data, vars_i, vars_j):
grpr_i = data.groupby(vars_i).grouper
idx_i = grpr_i.group_info[0]
grpr_j = data.groupby(vars_j).grouper
idx_j = grpr_j.group_info[0]
data_sm = csr_matrix((data['val'].values, (idx_i, idx_j)),
shape=(grpr_i.ngroups, grpr_j.ngroups))
return data_sm, grpr_i, grpr_j
# example
data = pd.DataFrame({'var_i_1' : ['a1', 'a1', 'a1', 'a2', 'a2', 'a3'],
'var_i_2' : ['b2', 'b1', 'b1', 'b1', 'b1', 'b4'],
'var_j_1' : ['c2', 'c3', 'c2', 'c1', 'c2', 'c3'],
'val' : [1, 2, 3, 4, 5, 6]})
data_sm, _, _ = df_to_sm(data, ['var_i_1', 'var_i_2'], ['var_j_1'])
data_sm.todense()
@khammel 之前发布的答案很有用,但不幸的是,由于 pandas 和 Python 的更改而不再有效。以下应产生相同的输出:
from scipy.sparse import csr_matrix
from pandas.api.types import CategoricalDtype
person_c = CategoricalDtype(sorted(frame.person.unique()), ordered=True)
thing_c = CategoricalDtype(sorted(frame.thing.unique()), ordered=True)
row = frame.person.astype(person_c).cat.codes
col = frame.thing.astype(thing_c).cat.codes
sparse_matrix = csr_matrix((frame["count"], (row, col)), \
shape=(person_c.categories.size, thing_c.categories.size))
>>> sparse_matrix
<3x4 sparse matrix of type '<class 'numpy.int64'>'
with 6 stored elements in Compressed Sparse Row format>
>>> sparse_matrix.todense()
matrix([[0, 1, 0, 1],
[1, 0, 0, 1],
[1, 0, 1, 0]], dtype=int64)
dfs = pd.SparseDataFrame(sparse_matrix, \
index=person_c.categories, \
columns=thing_c.categories, \
default_fill_value=0)
>>> dfs
a b c d
him 0 1 0 1
me 1 0 0 1
you 1 0 1 0
主要变化是:
.astype()
不再接受 "categorical"。您必须创建一个 CategoricalDtype 对象。sort()
不再有效
其他变化更肤浅:
- 使用类别大小而不是唯一系列对象的长度,只是因为我不想不必要地制作另一个对象
csr_matrix
(frame["count"]
)的数据输入不需要是列表对象- pandas
SparseDataFrame
现在直接接受一个 scipy.sparse 对象