Часто используемые простые, важные и распространенные методы столбцов для эффективного управления фреймами данных / наборами данных.

Большинство операций, которые мы выполняем в Spark, обычно связаны с интенсивным использованием объектов столбцов. Spark имеет богатые функции для манипулирования и преобразования данных столбца. Столбец Dataframe / Dataset в Spark похож на столбец в традиционной базе данных.

Рассмотрим приведенный ниже пример фрейма данных, id и name - это столбцы здесь, и они представляют столбец в наборе данных / фрейме данных, который является контейнером для выражения Catalyst (мы можем поговорить о катализаторе как-нибудь в другой раз). В двух словах, катализатор - это структура для работы с деревом, а выражения - это узлы дерева выражений. Для пользователя Spark API id и name подобны столбцам таблицы User, как старый добрый SQL.

scala> val got = Seq((1,"Bran"),(2,"Jon")).toDF("id","name")
got: org.apache.spark.sql.DataFrame = [id: int, name: string]
scala> got.show
+---+----+
| id|name|
+---+----+
|  1|Bran|
|  2| Jon|
+---+----+

В Spark есть множество методов для поддержки большого количества операций со столбцами.

Чтобы получить доступ к столбцу из набора данных / фрейма данных:

Допустим, вы хотите получить доступ только к name из got фрейма данных. Есть несколько способов сделать это.

scala>  import org.apache.spark.sql.functions.col
import org.apache.spark.sql.functions.col
scala>   got.select(col("id"))
res17: org.apache.spark.sql.DataFrame = [id: int]
scala>   import org.apache.spark.sql.functions.column
import org.apache.spark.sql.functions.column
scala>   got.select(column("id"))
res18: org.apache.spark.sql.DataFrame = [id: int]
scala>   import spark.implicits.StringToColumn
import spark.implicits.StringToColumn
scala>   got.select($"id")
res19: org.apache.spark.sql.DataFrame = [id: int]
scala>   import spark.implicits.symbolToColumn
import spark.implicits.symbolToColumn
scala>   got.select('id)
res20: org.apache.spark.sql.DataFrame = [id: int]
scala>   got.select('id).show
+---+
| id|
+---+
|  1|
|  2|
+---+

Примечание: если вы используете Spark-Shell, весь этот импорт осуществляется бесплатно. В противном случае некоторые операции импорта придется выполнять вручную. Неявный импорт может быть непростым.

В общем, я считаю, что люди предпочитают сделать import spark.implicits._ и покончить с этим. Кроме того, если вам интересно, какие имплициты используются в scala. Есть удобный способ сделать это.

scala> import scala.reflect.runtime.universe.reify
import scala.reflect.runtime.universe.reify
scala> println(reify(Seq((1,"Bran"),(2,"Jon")).toDF("id","name")))
Expr[org.apache.spark.sql.DataFrame]($iw.$line3$read.$iw.$iw.spark.implicits.localSeqToDatasetHolder(Seq.apply(Tuple2.apply(1, "Bran"), Tuple2.apply(2, "Jon")))($iw.$line3$read.$iw.$iw.spark.implicits.newProductEncoder(Predef.this.implicitly)).toDF("id", "name"))

Кроме того, у нас могут быть типизированные столбцы, которые в основном представляют собой столбцы с кодировщиком выражений, указанным для ожидаемого типа ввода и возврата.

scala> val name = $"name".as[String]
name: org.apache.spark.sql.TypedColumn[Any,String] = name
scala> val name = $"name"
name: org.apache.spark.sql.ColumnName = name

Существует более 50 методов (67 в последний раз, когда я насчитал), которые можно использовать для преобразований объекта столбца. Мы рассмотрим некоторые из важных методов, которые обычно используются.

!==, +, <=, >, apply, asc_nulls_last, bitwiseXOR, desc_nulls_first, eqNullSafe, expr, gt, isNotNull, like, multiply, otherwise, startsWith, unary_-, %, -, <=>, >=, as, between, cast, desc_nulls_last, equalTo, geq, hashCode, isNull, lt, name, over, substr, when, &&, /, =!=, alias, asc, bitwiseAND, contains, divide, equals, getField, isInCollection, isin, minus, notEqual, plus, toString, ||, *, <, ===, and, asc_nulls_first, bitwiseOR, desc, endsWith, explain, getItem, isNaN, leq, mod, or, rlike, unary_!

Предположим, у нас есть набор данных GOT!

scala>  val gotData = Seq((101,"Bran",10,"Stark"),(221,"Jon",16,null),(11,"Ned",50,"Stark"),(21,"Tyrion",40,"Lanister")).toDF("id","name","age","house")
gotData: org.apache.spark.sql.DataFrame = [id: int, name: string ... 2 more fields]
scala> gotData.show
+---+------+---+--------+
| id|  name|age|   house|
+---+------+---+--------+
|101|  Bran| 10|   Stark|
|221|   Jon| 16|    null|
| 11|   Ned| 50|   Stark|
| 21|Tyrion| 40|Lanister|
+---+------+---+--------+

1) +

Допустим, вам нужен текущий возраст звезд GOT, добавьте 8 лет к возрасту каждого персонажа и получите новый столбец с текущим возрастом. У нас есть + для этого:

scala> gotData.withColumn("char_current_age", col("age").+(8)).show()
scala> gotData.withColumn("char_current_age", col("age") +8 ).show()
+---+------+---+--------+----------------+
| id|  name|age|   house|char_current_age|
+---+------+---+--------+----------------+
|101|  Bran| 10|   Stark|              18|
|221|   Jon| 16|    null|              24|
| 11|   Ned| 50|   Stark|              58|
| 21|Tyrion| 40|Lanister|              48|
+---+------+---+--------+----------------+

Вы также можете создать новый фрейм данных, добавив его в столбцы. Но добавляемые типы данных должны, иначе результирующий фрейм данных будет нулевым. Скажем, если вы сделаете gotData(“age”) + gotData(“name”), результирующий фрейм данных будет нулевым фреймом. Кроме того, theplus - это метод, эквивалентный Java.

scala> gotData.select( gotData("age") + gotData("id") ).show
+----------+
|(age + id)|
+----------+
|       111|
|       237|
|        61|
|        61|
+----------+
scala> gotData.select( gotData("age") plus  gotData("id") )

2) -

scala> gotData.withColumn("older_than_bran", col("age") - 10  ).show()
+---+------+---+--------+---------------+
| id|  name|age|   house|older_than_bran|
+---+------+---+--------+---------------+
|101|  Bran| 10|   Stark|              0|
|221|   Jon| 16|    null|              6|
| 11|   Ned| 50|   Stark|             40|
| 21|Tyrion| 40|Lanister|             30|
+---+------+---+--------+---------------+
scala> gotData.withColumn("older_than_bran", col("age") minus  10  )

3)

Точно так же у нас есть * для умножения, / для деления и % для получения мода столбца.

scala> gotData.withColumn("multiply_10", col("age") *  10)
scala> gotData.withColumn("multiply_10", col("age") multiply 10)
scala> gotData.withColumn("divide_10", col("age") / 10)
scala> gotData.withColumn("divide_10", col("age") divide 10)
scala> gotData.withColumn("mod_10", col("age") mod  10)
scala> gotData.withColumn("mod_10", col("age") %  10)

4) isin (есть в)

Он создает новый столбец с логическим значением, и оценивается предоставленная коллекция ._* требуется, когда мы проверяем, присутствует ли элемент в коллекции, поскольку метод asin ожидает переменных аргументов (Any*). Это особый случай приписывания типа, который сообщает компилятору Scala обрабатывать один аргумент типа последовательности как varargs.

scala> val teenage = (15 to 25)
teenage: scala.collection.immutable.Range.Inclusive = Range(15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)
scala>   gotData.withColumn("teenage",col("age").isin(teenage:_*)).show()
+---+------+---+--------+-------+
| id|  name|age|   house|teenage|
+---+------+---+--------+-------+
|101|  Bran| 10|   Stark|  false|
|221|   Jon| 16|    null|   true|
| 11|   Ned| 50|   Stark|  false|
| 21|Tyrion| 40|Lanister|  false|
+---+------+---+--------+-------+
scala>   gotData.withColumn("teenage",col("age").isin(15 to 25:_*))
scala>   gotData.withColumn("is_Stark",col("house").isin("Stark"))

Мы можем использовать isInCollection, если вы хотите просто сослаться на коллекцию. Он дает тот же результат. isInCollection выполняет преобразование в varargs нотацию.

scala>   gotData.withColumn("teenage",col("age").isInCollection(15 to 25))

5) like (нравится)

Это подобное выражение SQL, которое проверяет, содержит ли столбец эти конкретные данные, и возвращает логическое значение. Это также чувствительно к регистру. like(“b%”) вернет false.

scala> gotData.withColumn("starts with B",col("name").like("B%")).show()
+---+------+---+--------+-------------+
| id|  name|age|   house|starts with B|
+---+------+---+--------+-------------+
|101|  Bran| 10|   Stark|         true|
|221|   Jon| 16|    null|        false|
| 11|   Ned| 50|   Stark|        false|
| 21|Tyrion| 40|Lanister|        false|
+---+------+---+--------+-------------+
scala> gotData.withColumn("has O ",col("name").like("%o%")).show()
+---+------+---+--------+------+
| id|  name|age|   house|has O |
+---+------+---+--------+------+
|101|  Bran| 10|   Stark| false|
|221|   Jon| 16|    null|  true|
| 11|   Ned| 50|   Stark| false|
| 21|Tyrion| 40|Lanister|  true|
+---+------+---+--------+------+

Точно так же есть функция rlike, которая может использоваться для сопоставления с регулярным выражением.

scala> gotData.withColumn("has O ",col("name").rlike("^(Bran|Jon)")) .show()
+---+------+---+--------+------+
| id|  name|age|   house|has O |
+---+------+---+--------+------+
|101|  Bran| 10|   Stark|  true|
|221|   Jon| 16|    null|  true|
| 11|   Ned| 50|   Stark| false|
| 21|Tyrion| 40|Lanister| false|
+---+------+---+--------+------+

Добавление ! отменяет логический вывод и может использоваться для реализации not like и not rlike функций.

gotData.withColumn("has O ",!col("name").like("%o%"))
gotData.withColumn("has O ",!col("name").rlike("^(Bran|Jon)"))

6) isNull и isNotNull

scala>gotData.withColumn("house_unknown",col("house").isNull).show()
+---+------+---+--------+-------------+
| id|  name|age|   house|house_unknown|
+---+------+---+--------+-------------+
|101|  Bran| 10|   Stark|        false|
|221|   Jon| 16|    null|         true|
| 11|   Ned| 50|   Stark|        false|
| 21|Tyrion| 40|Lanister|        false|
+---+------+---+--------+-------------+
scala>gotData.withColumn("houseNotnull",col("house").isNotNull)
.show()

Я расскажу о некоторых других важных методах, которые могут пригодиться в следующей части этой статьи. Спасибо за прочтение! Пожалуйста, поделитесь статьей, если она вам понравилась. Любые комментарии и предложения приветствуются! Ознакомьтесь с другими моими статьями здесь.