假设你有
val docs = List(List("one", "two"), List("two", "three"))
例如列表(“一个”,“两个”)表示包含术语“一”和“两个”的文档,并且您希望为每个术语构建一个包含文档频率的地图,即在这种情况下
Map("one" -> 1, "two" -> 2, "three" -> 1)
你会如何在Scala中做到这一点? (并且以一种有效的方式,假设一个更大的数据集。)
我的第一个类似Java的想法是使用可变映射:
val freqs = mutable.Map.empty[String,Int]
for (doc <- docs)
for (term <- doc)
freqs(term) = freqs.getOrElse(term, 0) + 1
效果不错,但我想知道如何以更“实用”的方式做到这一点,而不是诉诸可变地图?
答案 0 :(得分:20)
试试这个:
scala> docs.flatten.groupBy(identity).mapValues(_.size)
res0: Map[String,Int] = Map(one -> 1, two -> 2, three -> 1)
如果您要多次访问计数,那么您应该避免使用mapValues
,因为它是“懒惰的”,因此会在每次访问时重新计算大小。此版本为您提供相同的结果,但不需要重新计算:
docs.flatten.groupBy(identity).map(x => (x._1, x._2.size))
identity
功能仅表示x => x
。
答案 1 :(得分:13)
docs.flatten.foldLeft(new Map.WithDefault(Map[String,Int](),Function.const(0))){
(m,x) => m + (x -> (1 + m(x)))}
火车撞坏了什么!
[编辑]
啊,那更好!
docs.flatten.foldLeft(Map[String,Int]() withDefaultValue 0){
(m,x) => m + (x -> (1 + m(x)))}
答案 2 :(得分:0)
从Scala 2.13
开始,在flatten
列出列表之后,我们可以使用groupMapReduce
,它是groupBy
/ mapValues
的单次通过选择:
// val docs = List(List("one", "two"), List("two", "three"))
docs.flatten.groupMapReduce(identity)(_ => 1)(_ + _)
// Map[String,Int] = Map("one" -> 1, "three" -> 1, "two" -> 2)
此:
flatten
s List
s中的List
作为List
group
的列表元素(identity
)(组 MapReduce的组部分)
map
将每个分组值出现的次数设为1(_ => 1
)(映射组 Map Reduce)
reduce
在一组值(_ + _
)中的值相加(减少groupMap Reduce 的一部分)。