Как получить эквивалентный номер строки SQL для Spark RDD?
мне нужно создать полный список row_numbers для таблицы данных с большим количеством столбцов.
в SQL, это будет выглядеть так:
select
key_value,
col1,
col2,
col3,
row_number() over (partition by key_value order by col1, col2 desc, col3)
from
temp
;
теперь, скажем, в Spark у меня есть RDD формы (K, V), где V=(col1, col2, col3), поэтому мои записи похожи на
(key1, (1,2,3))
(key1, (1,4,7))
(key1, (2,2,3))
(key2, (5,5,5))
(key2, (5,5,9))
(key2, (7,5,5))
etc.
Я хочу заказать их, используя такие команды, как sortBy (), sortWith (), sortByKey (), zipWithIndex и т. д. и иметь новый RDD с правильным row_number
(key1, (1,2,3), 2)
(key1, (1,4,7), 1)
(key1, (2,2,3), 3)
(key2, (5,5,5), 1)
(key2, (5,5,9), 2)
(key2, (7,5,5), 3)
etc.
(мне все равно в скобках, так что форма тоже может быть (к (столбца col1,столбец col2,col3,для параметр rownum)) вместо)
как мне это сделать?
вот моя первая попытка:
val sample_data = Seq(((3,4),5,5,5),((3,4),5,5,9),((3,4),7,5,5),((1,2),1,2,3),((1,2),1,4,7),((1,2),2,2,3))
val temp1 = sc.parallelize(sample_data)
temp1.collect().foreach(println)
// ((3,4),5,5,5)
// ((3,4),5,5,9)
// ((3,4),7,5,5)
// ((1,2),1,2,3)
// ((1,2),1,4,7)
// ((1,2),2,2,3)
temp1.map(x => (x, 1)).sortByKey().zipWithIndex.collect().foreach(println)
// ((((1,2),1,2,3),1),0)
// ((((1,2),1,4,7),1),1)
// ((((1,2),2,2,3),1),2)
// ((((3,4),5,5,5),1),3)
// ((((3,4),5,5,9),1),4)
// ((((3,4),7,5,5),1),5)
// note that this isn't ordering with a partition on key value K!
val temp2 = temp1.???
также обратите внимание, что функция sortBy не может быть применена непосредственно к RDD, но сначала нужно запустить collect (), а затем вывод тоже не RDD, а array
temp1.collect().sortBy(a => a._2 -> -a._3 -> a._4).foreach(println)
// ((1,2),1,4,7)
// ((1,2),1,2,3)
// ((1,2),2,2,3)
// ((3,4),5,5,5)
// ((3,4),5,5,9)
// ((3,4),7,5,5)
вот немного больше прогресса, но все еще не разделено:
val temp2 = sc.parallelize(temp1.map(a => (a._1,(a._2, a._3, a._4))).collect().sortBy(a => a._2._1 -> -a._2._2 -> a._2._3)).zipWithIndex.map(a => (a._1._1, a._1._2._1, a._1._2._2, a._1._2._3, a._2 + 1))
temp2.collect().foreach(println)
// ((1,2),1,4,7,1)
// ((1,2),1,2,3,2)
// ((1,2),2,2,3,3)
// ((3,4),5,5,5,4)
// ((3,4),5,5,9,5)
// ((3,4),7,5,5,6)
3 ответов
на row_number() over (partition by ... order by ...)
функциональность была добавлена в Spark 1.4. Этот ответ использует PySpark / DataFrames.
создайте тестовый фрейм данных:
from pyspark.sql import Row, functions as F
testDF = sc.parallelize(
(Row(k="key1", v=(1,2,3)),
Row(k="key1", v=(1,4,7)),
Row(k="key1", v=(2,2,3)),
Row(k="key2", v=(5,5,5)),
Row(k="key2", v=(5,5,9)),
Row(k="key2", v=(7,5,5))
)
).toDF()
добавить секционированный номер строки:
from pyspark.sql.window import Window
(testDF
.select("k", "v",
F.rowNumber()
.over(Window
.partitionBy("k")
.orderBy("k")
)
.alias("rowNum")
)
.show()
)
+----+-------+------+
| k| v|rowNum|
+----+-------+------+
|key1|[1,2,3]| 1|
|key1|[1,4,7]| 2|
|key1|[2,2,3]| 3|
|key2|[5,5,5]| 1|
|key2|[5,5,9]| 2|
|key2|[7,5,5]| 3|
+----+-------+------+
это интересная проблема, которую вы поднимаете. Я отвечу на него на Python, но я уверен, что вы сможете легко перевести на Scala.
вот как я бы решить это:
1-упростите свои данные:
temp2 = temp1.map(lambda x: (x[0],(x[1],x[2],x[3])))
temp2 сейчас "ключ-значение" пары. Это выглядит так:
[
((3, 4), (5, 5, 5)),
((3, 4), (5, 5, 9)),
((3, 4), (7, 5, 5)),
((1, 2), (1, 2, 3)),
((1, 2), (1, 4, 7)),
((1, 2), (2, 2, 3))
]
2-затем используйте функцию group-by для воспроизведения эффекта раздела по:
temp3 = temp2.groupByKey()
temp3 теперь является RDD с 2 строки:
[((1, 2), <pyspark.resultiterable.ResultIterable object at 0x15e08d0>),
((3, 4), <pyspark.resultiterable.ResultIterable object at 0x15e0290>)]
3-Теперь вам нужно применить функцию ранга для каждого значения RDD. В python я бы использовал простую функцию сортировки (перечисление создаст столбец row_number):
temp4 = temp3.flatMap(lambda x: tuple([(x[0],(i[1],i[0])) for i in enumerate(sorted(x[1]))])).take(10)
обратите внимание, что для реализации вашего конкретного порядка вам нужно будет подать правильный аргумент " key " (в python я бы просто создал лямбда-функцию, например:
lambda tuple : (tuple[0],-tuple[1],tuple[2])
в конце (без функции ключевого аргумента, она выглядит как что):
[
((1, 2), ((1, 2, 3), 0)),
((1, 2), ((1, 4, 7), 1)),
((1, 2), ((2, 2, 3), 2)),
((3, 4), ((5, 5, 5), 0)),
((3, 4), ((5, 5, 9), 1)),
((3, 4), ((7, 5, 5), 2))
]
надеюсь, что это поможет!
удачи.
val test = Seq(("key1", (1,2,3)),("key1",(4,5,6)), ("key2", (7,8,9)), ("key2", (0,1,2)))
test: Seq[(String, (Int, Int, Int))] = List ((key1, (1,2,3)), (key1, (4,5,6)), (key2, (7,8,9)), (key2, (0,1,2)))
test.foreach(println)
(key1, (1,2,3))
(key1, (4,5,6))
(key2, (7,8,9))
(key2, (0,1,2))
val rdd = sc.parallelize(test, 2)
rdd: org.апаш.искра.РДУ.RDD[(String, (Int, Int, Int))] = ParallelCollectionRDD[41] при распараллеливании на :26
val rdd1 = rdd.groupByKey.map(x => (x._1,x._2.toArray)).map(x => (x._1, x._2.sortBy(x => x._1).zipWithIndex))
rdd1: org.апаш.искра.РДУ.RDD [(String, Array [((Int, Int, Int), Int)])] = MapPartitionsRDD[44] at map at :25
val rdd2 = rdd1.flatMap{
elem =>
val key = elem._1
elem._2.map(row => (key, row._1, row._2))
}
rdd2: org.апаш.искра.РДУ.РДД[(строка, (Инт, Инт, Инт), тип int)] = MapPartitionsRDD[45] при помощью flatMap на :25
rdd2.collect.foreach(println)
(key1, (1,2,3), 0)
(key1, (4,5,6), 1)
(key2, (0,1,2), 0)
(key2, (7,8,9), 1)