对于一组数据帧
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)
答案 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)
}