Как добавить столбец в Dataset без преобразования из фрейма данных и доступа к нему?
Я знаю метод добавления нового столбца В набор данных Spark с помощью .withColumn()
и UDF
, который возвращает фрейм данных. Я также знаю, что мы можем преобразовать полученный фрейм данных в набор данных.
мои вопросы:
- как безопасность типов DataSet вступает в игру здесь, если мы все еще следуем традиционному подходу DF (i.e передача имен столбцов в виде строки для ввода UDF)
- есть ли "объектно-ориентированный способ" доступа столбцы (без передачи имен столбцов в виде строки), как мы делали с RDD, для добавления нового столбца.
- как получить доступ к новому столбцу в обычных операциях, таких как карта, фильтр и т. д.?
например:
scala> case class Temp(a : Int, b : String) //creating case class
scala> val df = Seq((1,"1str"),(2,"2str),(3,"3str")).toDS // creating DS
scala> val appendUDF = udf( (b : String) => b + "ing") // sample UDF
scala> df.withColumn("c",df("b")) // adding a new column
res5: org.apache.spark.sql.DataFrame = [a: int, b: string ... 1 more field]
scala> res5.as[Temp] // converting to DS
res6: org.apache.spark.sql.Dataset[Temp] = [a: int, b: string ... 1 more field]
scala> res6.map( x =>x.
// list of autosuggestion :
a canEqual equals productArity productIterator toString
b copy hashCode productElement productPrefix
новый столбец c
, что я добавил с помощью .withColumn()
недоступен, потому что столбец c
не входит в класс case Temp
(содержит только a
& b
) в тот момент, когда он преобразуется в DS с помощью res5.as[Temp]
.
как получить доступ к колонке c
?
2 ответов
в типобезопасном мире Dataset
s Вы бы сопоставили структуру с другой.
то есть, для каждого преобразования, нам нужны представления схемы данных (как это необходимо для РДУ). Чтобы получить доступ к " c " выше, нам нужно создать новую схему, которая обеспечивает доступ к ней.
case class A(a:String)
case class BC(b:String, c:String)
val f:A => BC = a=> BC(a.a,"c") // Transforms an A into a BC
val data = (1 to 10).map(i => A(i.toString))
val dsa = spark.createDataset(data)
// dsa: org.apache.spark.sql.Dataset[A] = [a: string]
val dsb = dsa.map(f)
//dsb: org.apache.spark.sql.Dataset[BC] = [b: string, c: string]
просто добавить к @ maasg отличный ответ...
как безопасность типов DataSet вступает в игру здесь, если мы все еще следуем традиционному подходу DF (i.e передача имен столбцов в виде строки для ввода UDF)
позвольте мне ответить на этот вопрос другим вопросом: "Кто мы?" мы все еще следуем...'"? Если вы думаете, что я не согласен и использую DataFrames только тогда, когда я слишком ленив, чтобы создать класс case для описания набора данных для работы с.
мой ответ UDFs заключается в том, чтобы держаться подальше от UDFs, если они не очень просты, и нет ничего Spark Optimizer может оптимизировать. Да, я считаю, что UDFs слишком легко определить и использовать, что я сам увлекся слишком много раз, чтобы (над)использовать их. В Spark SQL 2.0 доступно около 239 функций, которые вы могли бы подумать о решении без UDFs, но стандартных функций.
scala> spark.version
res0: String = 2.1.0-SNAPSHOT
scala> spark.catalog.listFunctions.count
res1: Long = 240
(240 выше, потому что я прописана одна ФУНКЦИЯ.)
вы всегда должны использовать стандартные функции, так как они могут быть оптимизированы. Искра может контролировать то, что вы делаете, и, следовательно, оптимизировать ваши запросы.
вы должны использовать наборы данных (не Dataset[Row]
то есть DataFrame
), поскольку они дают вам доступ к тип-безопасный доступ к полям.
(тем не менее, некоторые "лакомства" набора данных не могут быть оптимизированы, поскольку Программирование набора данных - это пользовательский код Scala, который Spark не может оптимизировать столько, сколько DataFrame код.)
есть ли "объектно-ориентированный способ" доступа к столбцам (без передачи имен столбцов в виде строки), как мы привыкли делать с RDD, для добавления нового столбца.
да. Конечно. Используйте классы case для определения схемы наборов данных и использования полей. Как для доступа, так и для добавления (это то, на что @maasg ответил красиво, поэтому я не буду повторять его слова здесь).
как получить доступ к новому столбцу в обычных операциях, таких как map, фильтра и т. д?
легко...снова. Используйте класс case, который описывает (схему) ваших наборов данных. Как добавить новое "что-то" к существующему объекту? Вы не можете, если это как-то согласился иметь новую колонку, не так ли?
в "объектно-ориентированном способе" доступа к столбцам или добавления нового столбца."если ваш столбец является атрибутом класса case, вы не можете сказать: "это класс, который описывает данные и в то же время говорит, что это класс, который может иметь новый атрибут". Это невозможно в OOP / FP, не так ли?
вот почему добавление нового столбца сводится к использованию другого класса case или use withColumn
. Что в этом плохого? Думаю, есть...просто...в этом нет ничего плохого.