如何将数据框转换为具有混合列类型的稀疏矩阵?
Posted
技术标签:
【中文标题】如何将数据框转换为具有混合列类型的稀疏矩阵?【英文标题】:How to convert a dataframe to sparse matrix with mixed column types? 【发布时间】:2017-05-23 16:45:48 【问题描述】:我有以下格式的数据框:
df:
key f1 f2
k1 10 a, b, c
k2 20 b, d
k3 15 NaN
列 f2 有一个词袋作为值。我想将此数据帧转换为稀疏矩阵,因为 f2 中的不同单词有数千个。我期待的最终结果是以下格式:
key f1 f2.a f2.b f2.c f2.d
k1 10 1 1 1 0
k2 20 0 1 0 1
k3 15 0 0 0 0
我可以弄清楚如何根据 key 和 f2 字段独立创建稀疏矩阵。我首先熔化列 f2,所以我得到以下数据框:
df1:
key f2
k1 a
k1 b
k1 c
k2 b
k2 d
然后我对 f2 进行编码,并使用 sklearn.preprocessing 包中的 LabelEncoder 对 f2 进行编码。然后我创建一个稀疏矩阵如下:
df1['trainrow'] = np.arrange(df1.shape[0])
sparse.csr_matrix((np.ones(df1.shape[0], (df1.trainrow, df1.f2_encoded)))
这通过对字段 f2 进行一次热编码来创建一个稀疏矩阵。但我不确定如何将它与数字字段 f1 连接起来。
【问题讨论】:
我会使用来自sklearn.feature_extraction.text
的CountVectorizer
来完成这项任务——它应该更快
@MaxU 我正面临 CountVectorizer 的一个问题。我的词袋用逗号隔开,每个词都可能有空格。我不希望矢量化器将它们视为不同的特征。我们能以某种方式控制它吗?提前致谢。
【参考方案1】:
您可以将concat
与str.get_dummies
和add_prefix
一起使用:
df = pd.concat([df[['key','f1']], df.f2.str.get_dummies(sep=', ').add_prefix('f2.')], axis=1)
print (df)
key f1 f2.a f2.b f2.c f2.d
0 k1 10 1 1 1 0
1 k2 20 0 1 0 1
2 k3 15 0 0 0 0
在非常大的不同值get_dummies
非常慢,您可以使用自定义函数f
:
def f(category_list):
n_categories = len(category_list)
return pd.Series(dict(zip(category_list, [1]*n_categories)))
#remove NaN rows and create list of values by split
df1 = df.f2.dropna().str.split(', ').apply(f).add_prefix('f2.')
df2 = pd.concat([df[['key','f1']], df1], axis=1)
#replace NaN to 0 by position from 3.column to end of df
df2.iloc[:, 2: ] = df2.iloc[:, 2: ].fillna(0).astype(int)
print (df2)
key f1 f2.a f2.b f2.c f2.d
0 k1 10 1 1 1 0
1 k2 20 0 1 0 1
2 k3 15 0 0 0 0
时间安排:
In [256]: %timeit s.str.get_dummies(sep=', ')
1 loop, best of 3: 1min 16s per loop
In [257]: %timeit (s.dropna().str.split(', ').apply(f).fillna(0).astype(int))
1 loop, best of 3: 2.95 s per loop
计时码:
np.random.seed(100)
s = pd.DataFrame(np.random.randint(10000, size=(1000,1000))).astype(str).apply(', '.join, axis=1)
print (s)
df2 = s.str.get_dummies(sep=', ')
print (df2)
def f(category_list):
n_categories = len(category_list)
return pd.Series(dict(zip(category_list, [1]*n_categories)))
print (s.dropna().str.split(', ').apply(f).fillna(0).astype(int))
【讨论】:
此解决方案适用于字段 f2 中有限数量的不同值。但是我在该领域有大约 20k 不同的值,因此它需要永远。有什么建议我可以处理这么大的数据集吗? (出于同样的原因,我试图使用稀疏矩阵)【参考方案2】:我已经找到了解决此问题的最佳方法,因此将其发布为我未来参考和他人利益的答案:
由于数据量巨大,我只能使用稀疏矩阵。
第一步是将词袋转换为矢量化格式。我使用了 CountVectorizer(感谢@MaxU),如下所示:
from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer()
df2 = vectorizer.fit_transform(df['f2'].str.replace(' ',''))
我想忽略空格并使用逗号作为强制分隔符。我不知道该怎么做,所以我替换了空格,否则矢量化器会在空格处分割单词。
这已将 df1 创建为稀疏矩阵。
然后将另一个字段f1转换为不同的稀疏矩阵:
df1 = csr_matrix(df[['f1']].fillna(0))
然后使用 hstack 将这两者结合起来: sparseDF = hstack((df1,df2),format='csr')
【讨论】:
我用您的示例数据尝试您的答案,但它对我不起作用。有什么问题?它返回ValueError: empty vocabulary; perhaps the documents only contain stop words
抱歉回复晚了。我尝试了该方法,似乎它不适用于单个字母。您可以尝试再添加一个字母(aa、bb、cc 而不是 a、b、c)吗?我的实际用例有文字,所以我没有意识到它不适用于我提供的示例。
是的,这是一个问题。但现在我在sparseDF = hstack((df1,df2),format='csr')
中收到错误ValueError: blocks[0,:] has incompatible row dimensions
。输出是带有0
和1
的矩阵?
@jezrael 我已经修改了 df1 创建步骤。我们应该使用 df[['f1']] 而不是调用 df['f1'],以便它返回数据帧而不仅仅是列。请让我知道它是否仍然引发任何错误。感谢您的承受。我刚刚开始使用 Python 进行数据分析。以上是关于如何将数据框转换为具有混合列类型的稀疏矩阵?的主要内容,如果未能解决你的问题,请参考以下文章