语料库中的 Pyspark CountVectorizer 和词频
Pyspark CountVectorizer and Word Frequency in a corpus
我目前正在研究文本语料库。
假设我逐字逐句地进行了清理,并且我有以下 pyspark DataFrame:
df = spark.createDataFrame([(0, ["a", "b", "c"]),
(1, ["a", "b", "b", "c", "a"])],
["label", "raw"])
df.show()
+-----+---------------+
|label| raw|
+-----+---------------+
| 0| [a, b, c]|
| 1|[a, b, b, c, a]|
+-----+---------------+
我现在想实现一个 CountVectorizer。所以,我使用 pyspark.ml.feature.CountVectorizer
如下:
cv = CountVectorizer(inputCol="raw", outputCol="vectors")
model = cv.fit(df)
model.transform(df).show(truncate=False)
+-----+---------------+-------------------------+
|label|raw |vectors |
+-----+---------------+-------------------------+
|0 |[a, b, c] |(3,[0,1,2],[1.0,1.0,1.0])|
|1 |[a, b, b, c, a]|(3,[0,1,2],[2.0,2.0,1.0])|
+-----+---------------+-------------------------+
现在,我还想获取CountVectorizer选择的词汇,以及语料库中对应的词频。
使用 cvmodel.vocabulary
仅提供词汇表:
voc = cvmodel.vocabulary
voc
[u'b', u'a', u'c']
我想要这样的东西:
voc = {u'a':3,u'b':3,u'c':2}
你有没有想过做这样的事情?
编辑:
我正在使用 Spark 2.1
调用 cv.fit()
returns a CountVectorizerModel
,它(AFAIK)存储词汇表但不存储计数。词汇表是模型的 属性(它需要知道要计算哪些词),但计数是 DataFrame(不是模型)的 属性。您可以应用拟合模型的转换函数来获取任何 DataFrame 的计数。
话虽这么说,这里有两种方法可以获得您想要的输出。
1.使用现有的计数向量器模型
您可以使用 pyspark.sql.functions.explode()
and pyspark.sql.functions.collect_list()
将整个语料库收集到一行中。出于说明目的,让我们考虑一个新的 DataFrame df2
,其中包含一些被拟合 CountVectorizer
:
看不到的词
import pyspark.sql.functions as f
df2 = sqlCtx.createDataFrame([(0, ["a", "b", "c", "x", "y"]),
(1, ["a", "b", "b", "c", "a"])],
["label", "raw"])
combined_df = (
df2.select(f.explode('raw').alias('col'))
.select(f.collect_list('col').alias('raw'))
)
combined_df.show(truncate=False)
#+------------------------------+
#|raw |
#+------------------------------+
#|[a, b, c, x, y, a, b, b, c, a]|
#+------------------------------+
然后使用拟合模型将其转化为计数并收集结果:
counts = model.transform(combined_df).select('vectors').collect()
print(counts)
#[Row(vectors=SparseVector(3, {0: 3.0, 1: 3.0, 2: 2.0}))]
接下来 zip
计数和词汇一起使用 dict
构造函数以获得所需的输出:
print(dict(zip(model.vocabulary, counts[0]['vectors'].values)))
#{u'a': 3.0, u'b': 3.0, u'c': 2.0}
正如您正确指出的那样 ,这只会考虑属于 CountVectorizerModel
词汇表的单词。任何其他词都将被忽略。因此我们没有看到 "x"
或 "y"
.
的任何条目
2。使用 DataFrame 聚合函数
或者您可以跳过 CountVectorizer
并使用 groupBy()
获取输出。这是一个更通用的解决方案,因为它将给出 DataFrame 中 all 个单词的计数,而不仅仅是词汇表中的单词:
counts = df2.select(f.explode('raw').alias('col')).groupBy('col').count().collect()
print(counts)
#[Row(col=u'x', count=1), Row(col=u'y', count=1), Row(col=u'c', count=2),
# Row(col=u'b', count=3), Row(col=u'a', count=3)]
现在简单地使用一个dict
理解:
print({row['col']: row['count'] for row in counts})
#{u'a': 3, u'b': 3, u'c': 2, u'x': 1, u'y': 1}
这里我们还有 "x"
和 "y"
的计数。
另一种使用RDD的方式:
total_counts=df_trans[['vectors']].rdd
.map(lambda row: row['vectors'].toArray())
.reduce(lambda x,y: [x[i]+y[i] for i in range(len(y))])
dict(zip(model.vocabulary, total_counts))
我目前正在研究文本语料库。
假设我逐字逐句地进行了清理,并且我有以下 pyspark DataFrame:
df = spark.createDataFrame([(0, ["a", "b", "c"]),
(1, ["a", "b", "b", "c", "a"])],
["label", "raw"])
df.show()
+-----+---------------+
|label| raw|
+-----+---------------+
| 0| [a, b, c]|
| 1|[a, b, b, c, a]|
+-----+---------------+
我现在想实现一个 CountVectorizer。所以,我使用 pyspark.ml.feature.CountVectorizer
如下:
cv = CountVectorizer(inputCol="raw", outputCol="vectors")
model = cv.fit(df)
model.transform(df).show(truncate=False)
+-----+---------------+-------------------------+
|label|raw |vectors |
+-----+---------------+-------------------------+
|0 |[a, b, c] |(3,[0,1,2],[1.0,1.0,1.0])|
|1 |[a, b, b, c, a]|(3,[0,1,2],[2.0,2.0,1.0])|
+-----+---------------+-------------------------+
现在,我还想获取CountVectorizer选择的词汇,以及语料库中对应的词频。
使用 cvmodel.vocabulary
仅提供词汇表:
voc = cvmodel.vocabulary
voc
[u'b', u'a', u'c']
我想要这样的东西:
voc = {u'a':3,u'b':3,u'c':2}
你有没有想过做这样的事情?
编辑:
我正在使用 Spark 2.1
调用 cv.fit()
returns a CountVectorizerModel
,它(AFAIK)存储词汇表但不存储计数。词汇表是模型的 属性(它需要知道要计算哪些词),但计数是 DataFrame(不是模型)的 属性。您可以应用拟合模型的转换函数来获取任何 DataFrame 的计数。
话虽这么说,这里有两种方法可以获得您想要的输出。
1.使用现有的计数向量器模型
您可以使用 pyspark.sql.functions.explode()
and pyspark.sql.functions.collect_list()
将整个语料库收集到一行中。出于说明目的,让我们考虑一个新的 DataFrame df2
,其中包含一些被拟合 CountVectorizer
:
import pyspark.sql.functions as f
df2 = sqlCtx.createDataFrame([(0, ["a", "b", "c", "x", "y"]),
(1, ["a", "b", "b", "c", "a"])],
["label", "raw"])
combined_df = (
df2.select(f.explode('raw').alias('col'))
.select(f.collect_list('col').alias('raw'))
)
combined_df.show(truncate=False)
#+------------------------------+
#|raw |
#+------------------------------+
#|[a, b, c, x, y, a, b, b, c, a]|
#+------------------------------+
然后使用拟合模型将其转化为计数并收集结果:
counts = model.transform(combined_df).select('vectors').collect()
print(counts)
#[Row(vectors=SparseVector(3, {0: 3.0, 1: 3.0, 2: 2.0}))]
接下来 zip
计数和词汇一起使用 dict
构造函数以获得所需的输出:
print(dict(zip(model.vocabulary, counts[0]['vectors'].values)))
#{u'a': 3.0, u'b': 3.0, u'c': 2.0}
正如您正确指出的那样 CountVectorizerModel
词汇表的单词。任何其他词都将被忽略。因此我们没有看到 "x"
或 "y"
.
2。使用 DataFrame 聚合函数
或者您可以跳过 CountVectorizer
并使用 groupBy()
获取输出。这是一个更通用的解决方案,因为它将给出 DataFrame 中 all 个单词的计数,而不仅仅是词汇表中的单词:
counts = df2.select(f.explode('raw').alias('col')).groupBy('col').count().collect()
print(counts)
#[Row(col=u'x', count=1), Row(col=u'y', count=1), Row(col=u'c', count=2),
# Row(col=u'b', count=3), Row(col=u'a', count=3)]
现在简单地使用一个dict
理解:
print({row['col']: row['count'] for row in counts})
#{u'a': 3, u'b': 3, u'c': 2, u'x': 1, u'y': 1}
这里我们还有 "x"
和 "y"
的计数。
另一种使用RDD的方式:
total_counts=df_trans[['vectors']].rdd
.map(lambda row: row['vectors'].toArray())
.reduce(lambda x,y: [x[i]+y[i] for i in range(len(y))])
dict(zip(model.vocabulary, total_counts))