web-dev-qa-db-ja.com

Spark `DataFrame`の` unionAll`の何が問題になっていますか?

Spark 1.5.0を使用し、次のコードを指定すると、unionAllが列名に基づいてDataFramesをunionすることを期待しています。コードでは、FunSuiteを使用して渡すSparkContext sc

object Entities {

  case class A (a: Int, b: Int)
  case class B (b: Int, a: Int)

  val as = Seq(
    A(1,3),
    A(2,4)
  )

  val bs = Seq(
    B(5,3),
    B(6,4)
  )
}

class UnsortedTestSuite extends SparkFunSuite {

  configuredUnitTest("The truth test.") { sc =>
    val sqlContext = new SQLContext(sc)
    import sqlContext.implicits._
    val aDF = sc.parallelize(Entities.as, 4).toDF
    val bDF = sc.parallelize(Entities.bs, 4).toDF
    aDF.show()
    bDF.show()
    aDF.unionAll(bDF).show
  }
}

出力:

+---+---+
|  a|  b|
+---+---+
|  1|  3|
|  2|  4|
+---+---+

+---+---+
|  b|  a|
+---+---+
|  5|  3|
|  6|  4|
+---+---+

+---+---+
|  a|  b|
+---+---+
|  1|  3|
|  2|  4|
|  5|  3|
|  6|  4|
+---+---+

結果に"b"と "a"が混在する列が含まれるのはなぜですか?列名に基づいて列を揃える代わりに? serious bug !?

20
Martin Senne

それはまったくバグのようには見えません。表示されるのは、標準SQLの動作と、 PostgreSQLMySQLOracle 、および MS SQL を含むすべての主要なRDMBSです。まったく同じように動作します。 SQL Fiddleの名前にリンクされた例があります。

PostgreSQLマニュアル を引用するには:

2つのクエリのユニオン、インターセクション、または差を計算するには、2つのクエリが「ユニオン互換」である必要があります。つまり、同じ数の列を返し、対応する列は互換性のあるデータ型を持ちます

セット操作の最初のテーブルを除く列名は、単に無視されます。

この動作は、基本的な構成要素がタプルであるリレーショナル代数から直接発生します。タプルは順序付けられているため、2セットのタプルの和集合は、ここで得られる出力と同等です(重複処理を無視します)。

名前を使用して一致させたい場合は、このようなことを行うことができます

import org.Apache.spark.sql.DataFrame
import org.Apache.spark.sql.functions.col

def unionByName(a: DataFrame, b: DataFrame): DataFrame = {
  val columns = a.columns.toSet.intersect(b.columns.toSet).map(col).toSeq
  a.select(columns: _*).unionAll(b.select(columns: _*))
}

名前と型の両方をチェックするには、columnsを次のものに置き換えるだけで十分です。

a.dtypes.toSet.intersect(b.dtypes.toSet).map{case (c, _) => col(c)}.toSeq
36
zero323

この問題は、spark2.3で修正されています。データセットにunionByNameのサポートを追加しています。

https://issues.Apache.org/jira/browse/SPARK-21043

問題/バグなし-ケースクラスBを非常によく観察すると、明確になります。ケースクラスA->注文(a、b)について言及している場合、およびケースクラスB->注文(b、a)について言及している場合--->これは注文ごとに予想されます

ケースクラスA(a:Int、b:Int)ケースクラスB(b:Int、a:Int)

ありがとう、Subbu

UnionByNameを使用します。

ドキュメントからの抜粋:

def unionByName(other:Dataset [T]):Dataset [T]

この関数とユニオンの違いは、この関数が列を名前で(位置ではなく)解決することです:

val df1 = Seq((1, 2, 3)).toDF("col0", "col1", "col2")
val df2 = Seq((4, 5, 6)).toDF("col1", "col2", "col0")
df1.union(df2).show

// output:
// +----+----+----+
// |col0|col1|col2|
// +----+----+----+
// |   1|   2|   3|
// |   4|   5|   6|
// +----+----+----+
0

SPARK-981 で説明したように、データ型と列の数がフレーム間で同じである限り、unionAll操作は機能するはずです。追加の議論についてはコメントをご覧ください。

0
Rohan Aletty