我正在将几列字符串转换为我可以在LabeledPoint
中使用的数字要素。我正在考虑两种方法:
在第一种方法中,我们必须收集地图的唯一值。我不确定这需要多长时间(线性时间?)。然后我们遍历值列表并构建一个HashMap - 线性时间和内存。最后,我们迭代并查找每个值,N * eC(有效恒定时间)。
在第二种方法中,我们对(n log n time)进行排序,然后迭代并跟踪一个简单的计数器和一些变量。
建议采用什么方法?有记忆,性能和编码风格的考虑因素。第一种感觉就像2N + eC * N带有N *(String,Double)内存,可以用函数式编写。第二个是N log N + N,但感觉势在必行。 Spark需要传输静态地图吗?我可以看出这是一个交易破坏者。
答案 0 :(得分:1)
第二种方法很遗憾没有工作,原因是你无法阅读表单计数器,你只能增加它。什么是最糟糕的,你真的不知道什么时候价值变化,你没有状态记住以前的矢量。我想你可以使用mapPartition和total order partitioner之类的东西。您必须知道您的分区是按顺序处理的,并且在一个分区中不能使用相同的密钥,但这感觉真的很麻烦(我不知道它是否可行)。
我认为不可能一次性做到这一点。但你可以做到两个。在第一种方法中,您可以使用例如set accumulator将所有值放入其中,然后在驱动程序中对它们进行编号,并在第二遍中使用以替换它们。复杂度将是2N(假设值的数量<&lt;&lt; N)。
修改强>
implicit object SetAcc extends AccumulatorParam[Set[String]] {
def zero(s: Set[String]) = Set()
def addInPlace(s1: Set[String], s2: Set[String]) = s1 ++ s2
}
val rdd = sc.parallelize(
List((1, "a"), (2, "a"), (3, "b"), (4, "a"), (5, "c"), (6, "b"))
)
val acc: Accumulator[Set[String]] = sc.accumulator(Set())
rdd.foreach(p => acc += Set(p._2))
val encoding = acc.value.zipWithIndex.toMap
val result = rdd map {p => (p._1, encoding(p._2))}
如果你觉得这本词典太大了,你当然可以做它。如果你有许多功能和价值,你不想创建这么多大型累加器,那么你可以使用reduce函数一起处理它们并收集驱动程序。只是我的想法。我想你只需要尝试看看最适合你的用途。
修改强>
在mllib中有专门用于此目的的课程HashingTF
。它允许您一次性转换数据集。缺点是它使用散列模数指定参数将对象映射到双打。如果参数太小,这可能导致冲突。
val tf = new HashingTF(numFeatures = 10000)
val transformed = data.map(line => tf.transform(line.split("""\s+"""))
Ofc你可以在不使用HashingTF
类的情况下手工完成同样的事情。