如何从长 table 高效地创建 SparseDataFrame?
How to efficiently create a SparseDataFrame from a long table?
我有一个 SQL table,我可以将其作为 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,但在我的情况下,第一个 table 中有数千万或数亿行,会得到一个充满零的大密集矩阵,拖动起来很昂贵大约。您可以将其转换为稀疏,但要做到这一点需要大量资源。
现在我正在研究一个解决方案,为每个 user_id 分配行号,排序,然后在重新组合成 SparseDataFrame 之前将 'value' 列拆分为 SparseSeries。有没有更好的方法?
我找到了一个解决方案,虽然有点不完美。
可以做的是从列中手动创建一些 Pandas SparseSeries,将它们组合成一个字典,然后将该字典转换为 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'>
完整,但仍然稀疏。有一些注意事项,如果您不在适当的位置进行简单的复制或子集,那么它会忘记自己并尝试重铸为密集,但出于我的目的,我对此非常满意。
我有一个 SQL table,我可以将其作为 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,但在我的情况下,第一个 table 中有数千万或数亿行,会得到一个充满零的大密集矩阵,拖动起来很昂贵大约。您可以将其转换为稀疏,但要做到这一点需要大量资源。
现在我正在研究一个解决方案,为每个 user_id 分配行号,排序,然后在重新组合成 SparseDataFrame 之前将 'value' 列拆分为 SparseSeries。有没有更好的方法?
我找到了一个解决方案,虽然有点不完美。
可以做的是从列中手动创建一些 Pandas SparseSeries,将它们组合成一个字典,然后将该字典转换为 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'>
完整,但仍然稀疏。有一些注意事项,如果您不在适当的位置进行简单的复制或子集,那么它会忘记自己并尝试重铸为密集,但出于我的目的,我对此非常满意。