Spark DataFrame - нетипизированный, а DataFrame имеет схему? - PullRequest
0 голосов
/ 12 сентября 2018

Я новичок в Spark, читая о Dataframe, я очень часто находил ниже два утверждения для dataframe-

1) DataFrame не типизирован 2) DataFrame имеет схему (как и таблица базы данных, в которой есть вся информация, относящаяся к атрибуту таблицы - имя, тип, не нуль)

Разве оба утверждения не противоречат? Сначала мы говорим, что Dataframe не типизирован, и в то же время мы также говорим, что Dataframe содержит информацию обо всех столбцах, т.е. схеме, пожалуйста, помогите мне, чего мне здесь не хватает? потому что если у dataframe есть схема, то он также знает о типе столбцов, так как он становится не типизированным?

1 Ответ

0 голосов
/ 12 сентября 2018

Фреймы данных динамически типизированы, а наборы данных и RDD - статически. Это означает, что когда вы определяете набор данных или RDD, вам нужно явно указать класс, который представляет контент. Это может быть полезно, потому что когда вы пишете преобразования в ваш набор данных, компилятор может проверить ваш код на безопасность типов. Возьмите, например, этот набор данных о питомцах. Когда я использую pet.species или pet.name, компилятор знает их типы во время компиляции.

case class Pet(name: String, species: String, age: Int, weight: Double)

val data: Dataset[Pet] = Seq(
  Pet("spot", "dog", 2, 50.5),
  Pet("mittens", "cat", 11, 15.5),
  Pet("mickey", "mouse", 1, 1.5)).toDS
println(data.map(x => x.getClass.getSimpleName).first)
// Pet

val newDataset: Dataset[String] = data.map(pet => s"I have a ${pet.species} named ${pet.name}.")

Когда мы переключаемся на использование DataFrame, схема остается прежней, и данные по-прежнему набираются (или структурируются), но эта информация доступна только во время выполнения. Это называется динамическая типизация. Это препятствует тому, чтобы компилятор ловил ваши ошибки, но это может быть очень полезно, потому что позволяет вам писать SQL-подобные операторы и определять новые столбцы на лету, например, добавляя столбцы к существующему DataFrame, без необходимости определять новый класс для каждого маленькая операция. С другой стороны, вы можете определить неправильные операции, которые приводят к нулям или в некоторых случаях к ошибкам во время выполнения.

val df: DataFrame = data.toDF
df.printSchema()
// root
// |-- name: string (nullable = true)
// |-- species: string (nullable = true)
// |-- age: integer (nullable = false)
// |-- weight: double (nullable = false)

val newDf: DataFrame = df
  .withColumn("some column", ($"age" + $"weight"))
  .withColumn("bad column", ($"name" + $"age"))
newDf.show()
// +-------+-------+---+------+-----------+----------+
// |   name|species|age|weight|some column|bad column|
// +-------+-------+---+------+-----------+----------+
// |   spot|    dog|  2|  50.5|       52.5|      null|
// |mittens|    cat| 11|  15.5|       26.5|      null|
// | mickey|  mouse|  1|   1.5|        2.5|      null|
// +-------+-------+---+------+-----------+----------+
...