【问题标题】:How to efficiently create a SparseDataFrame from a long table?如何从长表有效地创建 SparseDataFrame?
【发布时间】:2016-05-17 06:04:56
【问题描述】:

我有一个可以作为 Pandas 数据框读取的 SQL 表,它具有以下结构:

user_id    value
1          100
1          200
2          100
4          200

这是一个矩阵的表示,它的所有值都是 1 或 0。这个矩阵的密集表示如下所示:

    100  200
1   1    1
2   1    0
4   0    1

通常,要进行此转换,您可以使用数据透视表,但在我的情况下,第一个表中有数千万或数亿行,一个大而密集的矩阵充满了零,拖拽起来很昂贵。您可以将其转换为稀疏的,但要做到这一点需要大量资源。

现在我正在研究一种解决方案,将行号分配给每个 user_id,排序,然后将“值”列拆分为 SparseSeries,然后重新组合为 SparseDataFrame。有没有更好的办法?

【问题讨论】:

  • 我在 SO 上没有看到太多关于 SparseSeries 的讨论。我已经回答了几个关于在它和scipysparse 矩阵之间来回传输的问题。我的印象是 Pandas sparse 结构仍在开发中。
  • 我找到了一些,就像你在这里的答案stackoverflow.com/questions/34181494/… 问题是它似乎没有扩展。现在我正在尝试转换一个 40,000 x 15,000 的 csc 矩阵,它已经运行了 30 多分钟。

标签: python pandas sparse-matrix


【解决方案1】:

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

可以做的是从列中手动创建多个 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'>

完整,但仍然稀疏。有一些注意事项,如果你做一个简单的副本或子集不在原地,那么它会忘记自己并尝试重铸为密集,但就我而言,我对此非常满意。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-06
    • 2022-01-14
    • 1970-01-01
    • 1970-01-01
    • 2014-09-08
    • 2012-07-01
    • 2019-09-22
    相关资源
    最近更新 更多