Pour un ensemble de trames de données
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")
d'union tous je fais
df1.unionAll(df2).unionAll(df3)
Existe-t-il un moyen plus élégant et évolutif de le faire pour n'importe quel nombre de trames de données, par exemple à partir de
Seq(df1, df2, df3)
La solution la plus simple consiste à reduce
avec union
(unionAll
in Spark <2.0):
val dfs = Seq(df1, df2, df3)
dfs.reduce(_ union _)
Ceci est relativement concis et ne devrait pas déplacer les données du stockage hors tas mais étend la lignée avec chaque union nécessite un temps non linéaire pour effectuer l'analyse du plan. ce qui peut être un problème si vous essayez de fusionner un grand nombre de DataFrames
.
Vous pouvez également convertir en RDDs
et utiliser 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
}
Il conserve lignée courte l'analyse est peu coûteuse mais sinon elle est moins efficace que la fusion directe de DataFrames
.
Pour pyspark, vous pouvez effectuer les opérations suivantes:
from functools import reduce
from pyspark.sql import DataFrame
dfs = [df1,df2,df3]
df = reduce(DataFrame.unionAll, dfs)
Cela ne vaut rien non plus que l'ordre des colonnes dans les cadres de données soit le même pour que cela fonctionne. Cela peut donner des résultats inattendus si vous n'avez pas les bons ordres de colonnes !!
Si vous utilisez pyspark 2.3 ou supérieur, vous pouvez utiliser unionByName pour ne pas avoir à réorganiser les colonnes.
Sous le capot spark aplatit les expressions d'union. Il faut donc plus de temps lorsque l'union se fait de manière linéaire.
La meilleure solution est spark pour avoir une fonction d'union qui prend en charge plusieurs DataFrames.
Mais le code suivant peut accélérer quelque peu l'union de plusieurs DataFrames (ou DataSets).
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)
}