Spark unionAll多个数据帧

时间:2016-06-03 11:00:04

标签: scala apache-spark apache-spark-sql

对于一组数据帧

val df1 = sc.parallelize(1 to 4).map(i => (i,i*10)).toDF("id","x")
val df2 = sc.parallelize(1 to 4).map(i => (i,i*100)).toDF("id","y")
val df3 = sc.parallelize(1 to 4).map(i => (i,i*1000)).toDF("id","z")

将他们所有人联合起来

df1.unionAll(df2).unionAll(df3)

对于任意数量的数据帧,是否有更优雅和可扩展的方法,例如从

Seq(df1, df2, df3) 

3 个答案:

答案 0 :(得分:31)

最简单的解决方案是reduce union(Spark< 2.0中的unionAll):

val dfs = Seq(df1, df2, df3)
dfs.reduce(_ union _)

这是相对简洁的,不应该从堆外存储移动数据,但扩展沿着每个联合的沿袭需要非线性时间来执行计划分析。如果您尝试合并大量DataFrames,那么会出现什么问题。

您还可以转换为RDDs并使用SparkContext.union

dfs match {
  case h :: Nil => Some(h)
  case h :: _   => Some(h.sqlContext.createDataFrame(
                     h.sqlContext.sparkContext.union(dfs.map(_.rdd)),
                     h.schema
                   ))
  case Nil  => None
}

它使 lineage short 分析成本低,但是效率低于直接合并DataFrames

答案 1 :(得分:7)

对于pyspark,您可以执行以下操作:

from functools import reduce
from pyspark.sql import DataFrame

dfs = [df1,df2,df3]
df = reduce(DataFrame.unionAll, dfs)

使数据框中的列顺序保持一致也毫无意义。如果您没有正确的列顺序,这可以默默地产生意想不到的结果!

如果您使用的是pyspark 2.3或更高版本,则可以使用unionByName,因此不必重新排序列。

答案 2 :(得分:0)

引擎盖下的火花使联合表达式变平。因此,线性进行并集会花费更长的时间。

最好的解决方案是使用具有支持多个DataFrame的并集函数的火花。

但是以下代码可能会加快多个DataFrame(或DataSet)的联合。

  def union[T : ClassTag](datasets : TraversableOnce[Dataset[T]]) : Dataset[T] = {
      binaryReduce[Dataset[T]](datasets, _.union(_))
  }
  def binaryReduce[T : ClassTag](ts : TraversableOnce[T], op: (T, T) => T) : T = {
      if (ts.isEmpty) {
         throw new IllegalArgumentException
      }
      var array = ts toArray
      var size = array.size
      while(size > 1) {
         val newSize = (size + 1) / 2
         for (i <- 0 until newSize) {
             val index = i*2
             val index2 = index + 1
             if (index2 >= size) {
                array(i) = array(index)  // last remaining
             } else {
                array(i) = op(array(index), array(index2))
             }
         }
         size = newSize
     }
     array(0)
 }