Scala:列表元素的groupBy(标识)

时间:2010-11-21 11:50:04

标签: scala

我开发了一个应用程序,它在(标记化的)文本中构建单词对,并产生每对发生的次数(即使同一对词出现多次,也可以,因为它会在算法后期得到平衡)。

当我使用

elements groupBy()

我想按元素的内容进行分组,所以我写了以下内容:

def self(x: (String, String)) = x

/**
 * Maps a collection of words to a map where key is a pair of words and the 
 *  value is number of
 * times this pair
 * occurs in the passed array
 */
def producePairs(words: Array[String]): Map[(String,String), Double] = {
  var table = List[(String, String)]()
  words.foreach(w1 =>
    words.foreach(w2 =>
      table = table ::: List((w1, w2))))


  val grouppedPairs = table.groupBy(self)
  val size = int2double(grouppedPairs.size)
  return grouppedPairs.mapValues(_.length / size)
}

现在,我完全意识到这个自我()技巧是一个肮脏的黑客。所以我想了一下:

grouppedPairs = table groupBy (x => x)

这样就产生了我想要的东西。但是,我仍然觉得我明显错过了一些东西,应该有更简单的方法。有什么想法,亲爱的?

另外,如果你帮助我改进对提取部分,它也会有很多帮助 - 它看起来非常迫切,C ++ - 现在就是这样。非常感谢提前!

3 个答案:

答案 0 :(得分:13)

我建议:

def producePairs(words: Array[String]): Map[(String,String), Double] = {
    val table = for(w1 <- words; w2 <- words) yield (w1,w2)
    val grouppedPairs = table.groupBy(identity)
    val size = grouppedPairs.size.toDouble
    grouppedPairs.mapValues(_.length / size)
}

for comprehension更容易阅读,并且已经有一个预先设定的函数identity,它是self的通用版本。

答案 1 :(得分:2)

你正在通过迭代两次单词来创建一个对所有单词的所有单词对的列表,我猜你只想要相邻的对。最简单的方法是使用滑动视图。

def producePairs(words: Array[String]): Map[(String, String), Int] = {
  val pairs   = words.sliding(2, 1).map(arr => arr(0) -> arr(1)).toList
  val grouped = pairs.groupBy(t => t)
  grouped.mapValues(_.size)
}

另一种方法是通过对它们求和来折叠对列表。不确定这是否更有效:

def producePairs(words: Array[String]): Map[(String, String), Int] = {
  val pairs = words.sliding(2, 1).map(arr => arr(0) -> arr(1))
  pairs.foldLeft(Map.empty[(String, String), Int]) { (m, p) =>
     m + (p -> (m.getOrElse(p, 0) + 1))
  }
}

我看到你返回一个相对数字(Double)。为简单起见,我只计算了出现次数,所以你需要进行最后的划分。我想你想要除以总对的数量(words.size - 1)而不是除以唯一对的数量(grouped.size)...,所以相对频率总和达到1.0

答案 2 :(得分:1)

替代方法不是订单O(num_words * num_words)而是订单O(num_unique_words * num_unique_words)(或类似的东西):

def producePairs[T <% Traversable[String]](words: T): Map[(String,String), Double] = {
  val counts = words.groupBy(identity).map{case (w, ws) => (w -> ws.size)}
  val size = (counts.size * counts.size).toDouble
  for(w1 <- counts; w2 <- counts) yield {
      ((w1._1, w2._1) -> ((w1._2 * w2._2) / size))
  }
}