Spark 1.5.0を使用し、次のコードを指定すると、unionAllが列名に基づいてDataFrame
sを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 !?
それはまったくバグのようには見えません。表示されるのは、標準SQLの動作と、 PostgreSQL 、 MySQL 、 Oracle 、および 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
この問題は、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|
// +----+----+----+
SPARK-981 で説明したように、データ型と列の数がフレーム間で同じである限り、unionAll操作は機能するはずです。追加の議論についてはコメントをご覧ください。