Ответ 1
- Разница между
df.select("foo")
иdf.select($"foo")
является сигнатурой. Первый принимает по крайней мере одинString
, более поздний один или болееColumns
. За пределами этого нет никакой практической разницы. -
myDataSet.map(foo.someVal)
проверяет тип, но поскольку любая операцияDataset
используетRDD
объектов и сравнивается с операциямиDataFrame
, есть значительные накладные расходы. Давайте рассмотрим простой пример:case class FooBar(foo: Int, bar: String) val ds = Seq(FooBar(1, "x")).toDS ds.map(_.foo).explain
== Physical Plan == *SerializeFromObject [input[0, int, true] AS value#123] +- *MapElements <function1>, obj#122: int +- *DeserializeToObject newInstance(class $line67.$read$$iw$$iw$FooBar), obj#121: $line67.$read$$iw$$iw$FooBar +- LocalTableScan [foo#117, bar#118]
Как вы можете видеть, этот план выполнения требует доступа ко всем полям и имеет значение
DeserializeToObject
. -
Нет. В общем, другие методы не являются синтаксическим сахаром и создают значительно другой план выполнения. Например:
ds.select($"foo").explain
== Physical Plan == LocalTableScan [foo#117]
По сравнению с планом, показанным перед тем, как он сможет напрямую обращаться к столбцу. Это не столько ограничение API, сколько результат в функциональной семантике.
-
Как я мог бы df.select( "foo" ) безопасный тип без оператора карты?
Такого варианта нет. В то время как типизированные столбцы позволяют преобразовать статически
Dataset
в другой статически типизированныйDataset
:ds.select($"bar".as[Int])
нет безопасного типа. Существуют и другие попытки включить оптимизированные по типу операции типа как типизированные агрегаты, но этот экспериментальный API.
-
почему я должен использовать UDF/UADF вместо карты
Это полностью зависит от вас. Каждая распределенная структура данных в Spark предоставляет свои преимущества и недостатки (см., Например, Spark UDAF с ArrayType в качестве проблем с производительностью bufferSchema).
Лично я считаю статически типизированный Dataset
наименее полезным:
-
Не предоставляйте такой же диапазон оптимизаций, как
Dataset[Row]
(хотя они совместно используют формат хранения и некоторые оптимизации плана выполнения, это не в полной мере извлекает выгоду из создания кода или хранения вне кучи), а также доступ ко всем аналитические возможностиDataFrame
. -
Типизированные преобразования - это черные ящики и эффективно создают барьер анализа для оптимизатора. Например, выбор (фильтры) не может быть перенесен по типизированному преобразованию:
ds.groupBy("foo").agg(sum($"bar") as "bar").as[FooBar].filter(x => true).where($"foo" === 1).explain
== Physical Plan == *Filter (foo#133 = 1) +- *Filter <function1>.apply +- *HashAggregate(keys=[foo#133], functions=[sum(cast(bar#134 as double))]) +- Exchange hashpartitioning(foo#133, 200) +- *HashAggregate(keys=[foo#133], functions=[partial_sum(cast(bar#134 as double))]) +- LocalTableScan [foo#133, bar#134]
По сравнению с:
ds.groupBy("foo").agg(sum($"bar") as "bar").as[FooBar].where($"foo" === 1).explain
== Physical Plan == *HashAggregate(keys=[foo#133], functions=[sum(cast(bar#134 as double))]) +- Exchange hashpartitioning(foo#133, 200) +- *HashAggregate(keys=[foo#133], functions=[partial_sum(cast(bar#134 as double))]) +- *Filter (foo#133 = 1) +- LocalTableScan [foo#133, bar#134]
Это влияет на функции, такие как предикат или нажатие кнопки.
-
Существует не так гибко, как
RDDs
с небольшим подмножеством поддерживаемых типов. - "Тип безопасности" с
Encoders
является спорным, когдаDataset
преобразуется с использованием методаas
. Поскольку форма данных не кодируется с использованием сигнатуры, компилятор может проверить только существованиеEncoder
.
Похожие вопросы: