如何从长表中有效地创建SparseDataFrame?

时间:2016-02-23 15:45:53

标签: python pandas sparse-matrix

我有一个SQL表,我可以将其作为Pandas数据框读入,具有以下结构:

user_id    value
1          100
1          200
2          100
4          200

它是矩阵的表示,所有值都是1或0.此矩阵的密集表示如下所示:

    100  200
1   1    1
2   1    0
4   0    1

通常,要进行此转换,您可以使用pivot,但在我的情况下,在第一个表中有数十或数亿行,会得到一个充满零的大密集矩阵,拖动成本很高。你可以将它转换为稀疏,但是到目前为止需要大量的资源。

现在我正在研究一种解决方案,为每个user_id分配行号,排序,然后在重新组合成SparseDataFrame之前将'value'列拆分为SparseSeries。还有更好的方法吗?

1 个答案:

答案 0 :(得分:2)

我找到了一个解决方案,尽管有点不完美。

可以做的是从列中手动创建一些Pandas SparseSeries,将它们组合成一个dict,然后将该dict转换为DataFrame(不是SparseDataFrame)。作为SparseDataFrame进行的转换当前命中一个不成熟的构造函数,它将整个对象解构为密集,然后无论输入如何都返回稀疏形式。但是,将SparseSeries构建到传统的DataFrame中可以保持稀疏性,但会创建一个可行且完整的DataFrame对象。

这里有一个如何做的演示,为了清晰而不是为了表现而写的更多。与我自己实现的一个区别是我创建了稀疏向量的字典作为字典理解而不是循环。

import pandas
import numpy

df = pandas.DataFrame({'user_id':[1,2,1,4],'value':[100,100,200,200]})

# Get unique users and unique features
num_rows = len(df['user_id'].unique())
num_features = len(df['value'].unique())
unique_users = df['user_id'].unique().copy()
unique_features = df['value'].unique().copy()
unique_users.sort()
unique_features.sort()


# assign each user_id to a row_number
user_lookup = pandas.DataFrame({'uid':range(num_rows), 'user_id':unique_users})


vec_dict = {}

# Create a sparse vector for each feature
for i in range(num_features):
    users_with_feature = df[df['value']==unique_features[i]]['user_id']

    uid_rows = user_lookup[user_lookup['user_id'].isin(users_with_feature)]['uid']

    vec = numpy.zeros(num_rows)
    vec[uid_rows] = 1

    sparse_vec = pandas.Series(vec).to_sparse(fill_value=0)

    vec_dict[unique_features[i]] = sparse_vec


my_pandas_frame = pandas.DataFrame(vec_dict)    
my_pandas_frame = my_pandas_frame.set_index(user_lookup['user_id']) 

结果:

>>> my_pandas_frame
         100  200
user_id          
1          1    1
2          1    0
4          0    1

>>> type(my_pandas_frame)
<class 'pandas.core.frame.DataFrame'>

>>> type(my_pandas_frame[100])
<class 'pandas.sparse.series.SparseSeries'>

完整,但仍然稀疏。有一些警告,如果你做一个简单的副本或子集非就地,那么它将忘记自己并尝试重铸到密集,但为了我的目的,我很高兴它。