如何将键值对减少到键和值列表?

时间:2014-11-06 13:09:46

标签: python list scala bigdata apache-spark

让我们假设,我在Spark中有一个键值对,如下所示。

[ (Key1, Value1), (Key1, Value2), (Key1, Vaue3), (Key2, Value4), (Key2, Value5) ]

现在我想减少这个,就像这样。

[ (Key1, [Value1, Value2, Value3]), (Key2, [Value4, Value5]) ]

即,从Key-Value到Key-List of Values。

如何使用python或scala中的map和reduce函数来做到这一点?

6 个答案:

答案 0 :(得分:4)

collections.defaultdict可以是解决方案https://docs.python.org/2/library/collections.html#collections.defaultdict

>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> for key, value in [('Key1', 'Value1'), ('Key1', 'Value2'), ('Key1', 'Vaue3'), ('Key2', 'Value4'), ('Key2', 'Value5') ]:
...     d[key].append(value)

>>> print d.items()
[('Key2', ['Value4', 'Value5']), ('Key1', [ 'Value1','Value2', 'Vaue3'])]

答案 1 :(得分:2)

val data = Seq(("Key1", "Value1"), ("Key1", "Value2"), ("Key1", "Vaue3"), ("Key2", "Value4"), ("Key2", "Value5"))

data
  .groupBy(_._1)
  .mapValues(_.map(_._2))

res0: scala.collection.immutable.Map[String,Seq[String]] =
     Map(
        Key2 -> List(Value4, Value5), 
        Key1 -> List(Value1, Value2, Vaue3))

答案 2 :(得分:1)

我确信有一种更易读的方法,但首先想到的是使用itertools.groupby。按元组的第一个元素(键)对列表进行排序。然后使用列表推导来迭代组。

from itertools import groupby

l = [('key1', 1),('key1', 2),('key1', 3),('key2', 4),('key2', 5)]
l.sort(key = lambda i : i[0])

[(key, [i[1] for i in values]) for key, values in groupby(l, lambda i: i[0])]

输出

[('key1', [1, 2, 3]), ('key2', [4, 5])]

答案 3 :(得分:0)

像这样的东西

newlist = dict()
for x in l: 
    if x[0] not in newlist: 
        dict[x[0]] = list()
    dict[x[0]].append(x[1])

答案 4 :(得分:0)

使用defaultdict的最短时间如下:没有要求排序。

>>> from collections import defaultdict                                                                                       
>>> collect = lambda tuplist: reduce(lambda acc, (k,v): acc[k].append(v) or acc,\
                                     tuplist, defaultdict(list))
>>> collect( [(1,0), (2,0), (1,2), (2,3)])
defaultdict(<type 'list'>, {1: [0, 2], 2: [0, 3]})

答案 5 :(得分:0)

另一个scala,避免使用groupBy / mapValues(虽然这是明显的Scala解决方案,但这个解决方案遵循Vishni给出的python,因为@MetallicPriest评论说“更容易”)

val data = Seq(("Key1", "Value1"), ("Key1", "Value2"), ("Key1", "Vaue3"),
               ("Key2", "Value4"), ("Key2", "Value5"))

val dict = Map[String, Seq[String]]() withDefaultValue(Nil)

data.foldLeft(dict){ case (d, (k,v)) => d updated (k, d(k) :+ v) }

// Map(Key1 -> List(Value1, Value2, Vaue3), Key2 -> List(Value4, Value5))

(是否附加了密钥以提供问题的确切结果。但是,Prepend会更有效率)

可变版本,更接近Python版本:

import scala.collection.mutable.{Map, Seq}
val dict = Map[String, Seq[String]]() withDefaultValue(Seq())

for ((k,v) <- data) dict(k) :+= v
dict
// Map(Key2 -> ArrayBuffer(Value4, Value5),
//     Key1 -> ArrayBuffer(Value1, Value2, Vaue3))