web-dev-qa-db-fra.com

Spark unionToutes les trames de données multiples

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) 
22
echo

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.

33
zero323

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.

17
TH22

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)
 }
0
S. Biedermann