2014-11-06 6 views
2

Предположим, что у меня есть пара значений ключей в 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?

+1

Итак, вы хотите решение Spark в Scala или Python или в простой Scala | Решение Python? другими словами, ваша стартовая коллекция RDD? – maasg

+1

Возможная дубликация [Уменьшить пару «ключ-значение» в парном списке с Apache Spark] (http://stackoverflow.com/questions/27002161/reduce-a-key-value-pair-into-a-key- list-pair-with-apache-spark) –

ответ

0

Что-то вроде этого

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

Вы можете использовать 'defaultdict (list)' для упрощения –

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])] 
+2

Это зависит от сортируемых ключей –

+0

Хороший улов, отредактированный для решения этой проблемы. – CoryKramer

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)) 
+0

Прохладный! Однако решение в python выглядит намного проще. – MetallicPriest

+1

@MetallicPriest Этот вопрос помечен как «apache-spark», что делает этот ответ единственным правильным для запуска Spark. Вы работаете на Spark? – maasg

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'])] 
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]}) 
+0

Хотя я могу оценить функциональный стиль, используя его просто для вызова функции с побочными эффектами, выглядит немного нечетно. – goncalopp

+0

Основная причина, по которой она написана, заключается в том, что код не полагается на модификацию переменной во внешней области. Я не уверен, какой побочный эффект вы имеете в виду? – haavee

+0

Я имею в виду 'append' – goncalopp

0

Другой Scala один, избегая GroupBy/mapValues ​​(хотя это очевидно Scala решение это один следует питона один дается Vishni так @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)) 

(делает Append ключа, чтобы дать точные результаты вопроса Prepend будет более эффективным, хотя)

Mutable версия, даже ближе к 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)) 
Смежные вопросы