【问题标题】:How can a reduce a key value pair to key and list of values?如何将键值对减少为键和值列表?
【发布时间】:2015-01-03 00:16:23
【问题描述】:

假设,我在 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 函数?

【问题讨论】:

标签: python list scala bigdata apache-spark


【解决方案1】:

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'])]

【讨论】:

    【解决方案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))
    

    【讨论】:

    • 酷!但是,python 中的解决方案看起来要容易得多。
    • @MetallicPriest 这个问题被标记为“apache-spark”,这使得这个答案成为在 Spark 上运行的唯一正确答案。你是在 Spark 上运行它吗?
    【解决方案3】:

    我确信有一种更易读的方法来做到这一点,但首先想到的是使用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])]
    

    【讨论】:

    • 这依赖于被排序的键
    【解决方案4】:

    类似的东西

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

    【讨论】:

    • 你可以使用defaultdict(list)来简化
    【解决方案5】:

    最短的,使用默认字典,如下;对排序没有要求。

    >>> 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]})
    

    【讨论】:

    • 虽然我可以欣赏函数式风格,但仅使用它来调用具有副作用的函数看起来有点奇怪
    • 这样写的主要原因是代码不依赖于外部作用域中变量的修改。我不确定你指的是哪种副作用?
    【解决方案6】:

    另一个 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))
    

    (是否附加密钥以给出问题的确切结果。不过,前置会更有效)

    可变版本,更接近 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))
    

    【讨论】:

      猜你喜欢
      • 2016-11-24
      • 2015-01-16
      • 1970-01-01
      • 2022-10-15
      • 2020-01-31
      • 2012-10-05
      • 2019-10-01
      • 2013-02-24
      • 1970-01-01
      相关资源
      最近更新 更多