将字符串要素转换为数字要素:算法效率

时间:2015-05-31 13:53:24

标签: scala apache-spark apache-spark-mllib

我正在将几列字符串转换为我可以在LabeledPoint中使用的数字要素。我正在考虑两种方法:

  1. 创建字符串到双精度的映射,遍历RDD并查找每个字符串并分配适当的值。
  2. 按列对RDD进行排序,使用计数器迭代RDD,将每个字符串分配给当前计数器值,直到字符串更改为止,此时计数器值会递增并分配。由于我们从未看过两次字符串(由于排序),因此这将有效地为每个字符串分配一个唯一值。
  3. 在第一种方法中,我们必须收集地图的唯一值。我不确定这需要多长时间(线性时间?)。然后我们遍历值列表并构建一个HashMap - 线性时间和内存。最后,我们迭代并查找每个值,N * eC(有效恒定时间)。

    在第二种方法中,我们对(n log n time)进行排序,然后迭代并跟踪一个简单的计数器和一些变量。

    建议采用什么方法?有记忆,性能和编码风格的考虑因素。第一种感觉就像2N + eC * N带有N *(String,Double)内存,可以用函数式编写。第二个是N log N + N,但感觉势在必行。 Spark需要传输静态地图吗?我可以看出这是一个交易破坏者。

1 个答案:

答案 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类的情况下手工完成同样的事情。