it-swarm.com.ru

Что не так с `unionAll` из Spark` DataFrame`?

Используя Spark 1.5.0 и приведенный ниже код, я ожидаю, что unionAll объединит DataFrames на основе имени их столбца. В коде я использую некоторый 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" вместо выравнивания оснований столбцов по именам столбцов? Звучит как серьезно ошибка !?

18
Martin Senne

Это совсем не похоже на ошибку. То, что вы видите, - это стандартное поведение SQL, и все основные RDMBS, включая PostgreSQL , MySQL , Oracle и MS SQL ведут себя точно так же. Вы найдете примеры SQL Fiddle, связанные с именами.

Цитировать Руководство PostgreSQL :

Чтобы вычислить объединение, пересечение или разность двух запросов, эти два запроса должны быть «совместимыми», что означает, что они возвращают одинаковое количество столбцов, а соответствующие столбцы имеют совместимые типы данных.

Имена столбцов, исключая первую таблицу в операции set, просто игнорируются.

Это поведение происходит непосредственно из реляционной алгебры, где основным строительным блоком является кортеж. Поскольку кортежи упорядочены, объединение двух наборов кортежей эквивалентно (игнорируя обработку дубликатов) полученному здесь выводу.

Если вы хотите сопоставить, используя имена, вы можете сделать что-то вроде этого

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
35
zero323

Эта проблема исправлена ​​в spark2.3. Они добавляют поддержку unionByName в наборе данных. 

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

никаких проблем/ошибок - если вы внимательно наблюдаете за своим классом дела B, тогда вам будет ясно .. Класс дела A -> вы упомянули порядок (a, b) и Класс дела B -> Вы упомянули порядок (б, а) ---> это ожидается в соответствии с заказом

кейс класса A (a: Int, b: Int) кейс класса B (b: Int, a: Int)

спасибо, Subbu

1
SUBBAREDDY JANGALAPALLI

Как обсуждалось в SPARK-9813 , кажется, что если типы данных и количество столбцов одинаковы для всех кадров, операция unionAll должна работать. Пожалуйста, смотрите комментарии для дополнительного обсуждения.

0
Rohan Aletty