我正在努力实现Spark LDA模型(通过Scala API),并且无法对我的数据进行必要的格式化步骤。我的原始数据(存储在文本文件中)采用以下格式,基本上是令牌列表及其对应的文档。一个简化的例子:
doc XXXXX term XXXXX
1 x 'a' x
1 x 'a' x
1 x 'b' x
2 x 'b' x
2 x 'd' x
...
XXXXX列是垃圾数据我不关心。我意识到这是一种存储语料库数据的非典型方式,但这就是我所拥有的。我希望从示例中可以清楚地看出,原始数据中每个标记有一行(因此,如果给定术语在文档中出现5次,则对应于5行文本)。
无论如何,我需要将这些数据格式化为用于运行Spark LDA模型的稀疏术语 - 频率向量,但是我不熟悉Scala,因此遇到了一些麻烦。
我从:
开始import org.apache.spark.mllib.clustering.{LDA, DistributedLDAModel}
import org.apache.spark.mllib.linalg.{Vector, Vectors}
import org.apache.spark.rdd.RDD
val corpus:RDD[Array[String]] = sc.textFile("path/to/data")
.map(_.split('\t')).map(x => Array(x(0),x(2)))
然后我得到了生成稀疏向量所需的词汇数据:
val vocab: RDD[String] = corpus.map(_(1)).distinct()
val vocabMap: Map[String, Int] = vocab.collect().zipWithIndex.toMap
我不知道的是在这里使用的正确的映射函数,这样我最终得到每个文档的稀疏项频率向量,然后我可以将其输入到LDA模型中。我想我需要这些方面的东西......
val documents: RDD[(Long, Vector)] = corpus.groupBy(_(0)).zipWithIndex
.map(x =>(x._2,Vectors.sparse(vocabMap.size, ???)))
此时我可以运行实际的LDA:
val lda = new LDA().setK(n_topics)
val ldaModel = lda.run(documents)
基本上,我没有应用于每个组的功能,以便我可以将术语频率数据(可能是map
?)提供给稀疏矢量。换句话说,如何填写上面代码段中的???
以达到预期的效果?
答案 0 :(得分:3)
处理此问题的一种方法:
spark-csv
包可用将数据加载到DataFrame中并选择感兴趣的列
val df = sqlContext.read
.format("com.databricks.spark.csv")
.option("header", "true")
.option("inferSchema", "true") // Optional, providing schema is prefered
.option("delimiter", "\t")
.load("foo.csv")
.select($"doc".cast("long").alias("doc"), $"term")
索引term
列:
import org.apache.spark.ml.feature.StringIndexer
val indexer = new StringIndexer()
.setInputCol("term")
.setOutputCol("termIndexed")
val indexed = indexer.fit(df)
.transform(df)
.drop("term")
.withColumn("termIndexed", $"termIndexed".cast("integer"))
.groupBy($"doc", $"termIndexed")
.agg(count(lit(1)).alias("cnt").cast("double"))
转换为PairwiseRDD
import org.apache.spark.sql.Row
val pairs = indexed.map{case Row(doc: Long, term: Int, cnt: Double) =>
(doc, (term, cnt))}
按文档分组:
val docs = pairs.groupByKey
创建特征向量
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.sql.functions.max
val n = indexed.select(max($"termIndexed")).first.getInt(0) + 1
val docsWithFeatures = docs.mapValues(vs => Vectors.sparse(n, vs.toSeq))
现在您已经拥有创建LabeledPoints
或应用其他处理所需的一切