it-swarm.com.ru

Как сжать два (или более) DataFrame в Spark

У меня есть две DataFramea и b.a похожа 

Column 1 | Column 2
abc      |  123
cde      |  23 

b это как 

Column 1 
1      
2      

Я хочу Zip a и b (или даже больше) DataFrames, который становится что-то вроде:

Column 1 | Column 2 | Column 3
abc      |  123     |   1
cde      |  23      |   2

Как мне это сделать?

8
worldterminator

Подобная операция не поддерживается API-интерфейсом DataFrame. Можно Zip два RDD, но для того, чтобы он работал, вы должны соответствовать как количеству разделов, так и количеству элементов в разделе. Предполагая, что это так:

import org.Apache.spark.sql.DataFrame
import org.Apache.spark.sql.Row
import org.Apache.spark.sql.types.{StructField, StructType, LongType}

val a: DataFrame = sc.parallelize(Seq(
  ("abc", 123), ("cde", 23))).toDF("column_1", "column_2")
val b: DataFrame = sc.parallelize(Seq(Tuple1(1), Tuple1(2))).toDF("column_3")

// Merge rows
val rows = a.rdd.Zip(b.rdd).map{
  case (rowLeft, rowRight) => Row.fromSeq(rowLeft.toSeq ++ rowRight.toSeq)}

// Merge schemas
val schema = StructType(a.schema.fields ++ b.schema.fields)

// Create new data frame
val ab: DataFrame = sqlContext.createDataFrame(rows, schema)

Если вышеуказанные условия не выполняются, единственная опция, которая приходит на ум, это добавить индекс и присоединиться:

def addIndex(df: DataFrame) = sqlContext.createDataFrame(
  // Add index
  df.rdd.zipWithIndex.map{case (r, i) => Row.fromSeq(r.toSeq :+ i)},
  // Create schema
  StructType(df.schema.fields :+ StructField("_index", LongType, false))
)

// Add indices
val aWithIndex = addIndex(a)
val bWithIndex = addIndex(b)

// Join and clean
val ab = aWithIndex
  .join(bWithIndex, Seq("_index"))
  .drop("_index")
20
zero323

В реализации Dataframes в Scala нет простого способа объединить два кадра данных в один. Мы можем просто обойти это ограничение, добавив индексы к каждой строке фреймов данных. Затем мы можем сделать внутреннее соединение по этим показателям. Это мой код заглушки этой реализации:

val a: DataFrame = sc.parallelize(Seq(("abc", 123), ("cde", 23))).toDF("column_1", "column_2")
val aWithId: DataFrame = a.withColumn("id",monotonicallyIncreasingId)

val b: DataFrame = sc.parallelize(Seq((1), (2))).toDF("column_3")
val bWithId: DataFrame = b.withColumn("id",monotonicallyIncreasingId)

aWithId.join(bWithId, "id")

Небольшое легкое чтение - Проверьте, как это делает Python!

1
Sohum Sachdev

А как насчет чистого SQL?

SELECT 
    room_name, 
    sender_nickname, 
    message_id, 
    row_number() over (partition by room_name order by message_id) as message_index, 
    row_number() over (partition by room_name, sender_nickname order by message_id) as user_message_index
from messages
order by room_name, message_id
0
Thomas Decaux

Я знаю, что OP использовал Scala, но если вам, как и мне, нужно знать, как это сделать в pyspark, попробуйте следующий код Python. Как и первое решение @ zero323, оно опирается на RDD.Zip() и поэтому завершится ошибкой, если оба DataFrames не имеют одинаковое количество разделов и одинаковое количество строк в каждом разделе.

from pyspark.sql import Row
from pyspark.sql.types import StructType

def zipDataFrames(left, right):
    CombinedRow = Row(*left.columns + right.columns)

    def flattenRow(row):
        left = row[0]
        right = row[1]
        combinedVals = [left[col] for col in left.__fields__] + [right[col] for col in right.__fields__]
        return CombinedRow(*combinedVals)

    zippedRdd = left.rdd.Zip(right.rdd).map(lambda row: flattenRow(row))        
    combinedSchema = StructType(left.schema.fields + right.schema.fields)        
    return zippedRdd.toDF(combinedSchema)

joined = zipDataFrames(a, b)
0
snark