В прошлой статье мы говорили о вычислении SQL-выражений в Apache Spark, а также немного затронули тему фильтрации данных. В этот раз углубимся в эту тему, поскольку без фильтрации не обходится никакой анализ данных. Сегодня расскажем, как фильтровать числовые, строковые значения, а также массивы в PySpark.
Что такое фильтрация данных, и как она используется в Spark
Фильтрация данных — это выборка тех записей таблицы (DataFrame), которые удовлетворяют заданному условию. В PySpark для этого используется методы filter
или where
(они делают то же самое). Кроме того, имеется возможность напрямую писать SQL-запросы через spark.sql
(см. тут).
df = spark.createDataFrame([ (['py', 'c', 'cpp'], 'Anton', 'Moscow', 23, 19), (['c', 'java', 'hs'], 'Anna', 'Omsk', 27, 25), (['py', 'cl',], 'Andry', 'Moscow', 24, 22), (['cpp',], 'Alex', 'Moscow', 32, 25), (['cpp', 'py'], 'Boris', 'Omsk', 55, 37), (['py', 'cl'], 'Vera', 'Moscow', 89, 41), ], ['lang', 'name', 'city', 'salary', 'age']) df.show() """ +-------------+-----+------+------+ | lang| name| city|salary| +-------------+-----+------+------+ | [py, c, cpp]|Anton|Moscow| 23| |[c, java, hs]| Anna| Omsk| 27| | [py, cl]|Andry|Moscow| 24| | [cpp]| Alex|Moscow| 32| | [cpp, py]|Boris| Omsk| 55| | [py, cl]| Vera|Moscow| 89| +-------------+-----+------+------+ """
Далее мы будем вводить выражения для фильтрации в двух редакциях: через точечную нотацию и через SQL-выражения. Чаще программисты используют точечную нотацию, однако у него есть недостаток: он не сработает, если в имени столбца есть что-то отличное от цифр и латинских букв. Например, может прошествовать пробел. В этом случае можно либо переименовать столбцы, либо использовать SQL-выражения. На всякий случай упомянем, как переименовать столбец в PySpark (если вы решили использовать точечную нотацию):
# Есть ещё масса других способов, но это самый простой df2 = df.withColumnRenamed('old_name', 'new')
Фильтрация чисел
Код курса
MLSP
Ближайшая дата курса
по запросу
Продолжительность
ак.часов
Стоимость обучения
0 руб.
Каким образом можно фильтровать числа? Число может быть равно какому-то значению, быть больше или меньше, входить в интервал. Здесь все предельно просто:
# SQL-выражение df.filter('age > 25') # Точечная нотация df.filter(df.age > 25) """ +---------+-----+------+------+---+ | lang| name| city|salary|age| +---------+-----+------+------+---+ |[cpp, py]|Boris| Omsk| 55| 37| | [py, cl]| Vera|Moscow| 89| 41| +---------+-----+------+------+---+ """
Для того чтобы получить записи, входящие в заданный интервал, то нужно всего лишь добавить логическое И (AND), ИЛИ (OR), как это делается в SQL. А вот для точеной нотации придется использовать побитовые логические операции: &
(И), |
(ИЛИ), а подвыражения вставлять в скобки. Код на Python:
df.filter('age > 25 AND salary >= 60') df.filter((df.age > 25) & (df.salary >= 60)) """ +--------+----+------+------+---+ | lang|name| city|salary|age| +--------+----+------+------+---+ |[py, cl]|Vera|Moscow| 89| 41| +--------+----+------+------+---+ """
Добавим также, в Python “равно” и “не равно” выражаются в виде ==
и !=
соответственно. В SQL часто применяются паскалевская нотация: =
(равно) и <>
(не равно). Так вот в SQL-выражениях можно использовать и в стиле Python, и в стиле Pascal:
# С точкой только == или != df.filter( ((df.age == 25) & (df.salary != 55)) | ((df.age == 37) & (df.salary != 40)) ) df.filter('(age == 25 AND salary != 55) OR (age = 37 AND salary <> 40)').show() """ +-------------+-----+------+------+---+ | lang| name| city|salary|age| +-------------+-----+------+------+---+ |[c, java, hs]| Anna| Omsk| 27| 25| | [cpp]| Alex|Moscow| 32| 25| | [cpp, py]|Boris| Omsk| 55| 37| +-------------+-----+------+------+---+ """
Еще можно использовать отрицание. В точечной нотации оно будет обозначаться через ~
. В SQL-выражениях — через NOT
. Хотя использовать его с числовыми значениями вызовет путаницу, но иметь в виду стоит.
df.filter('NOT age = 25 AND salary > 55') df.filter(~(df.age = 25) & (df.salary > 55))
Фильтрация строковых значений
Со строковыми значениями можно использовать ту же операцию сравнения (==
, !=
или =
, <>
в SQL-выражениях):
df.filter((df.city == 'Moscow') & (df.name != 'Alex')) df.filter('city == "Moscow" AND name != "Alex"') """ +------------+-----+------+------+---+ | lang| name| city|salary|age| +------------+-----+------+------+---+ |[py, c, cpp]|Anton|Moscow| 23| 19| | [py, cl]|Andry|Moscow| 24| 22| | [py, cl]| Vera|Moscow| 89| 41| +------------+-----+------+------+---+ """
Однако нужно понимать, что значения должны обязательно попадать в условия. В Spark можно найти записи, которые начинаются, заканчиваются заданными символами или их содержит. В точечной нотации для этого используются методы startswith
, endswith
и contains
.
df.filter((df.name.startswith('An')) & (df.city.contains('sc'))) """ +------------+-----+------+------+---+ | lang| name| city|salary|age| +------------+-----+------+------+---+ |[py, c, cpp]|Anton|Moscow| 23| 19| | [py, cl]|Andry|Moscow| 24| 22| +------------+-----+------+------+---+ """
А вот в SQL-выражениях придется использовать LIKE
и RLIKE
(Regex LIKE
). При использовании LIKE
знак %
обозначает сколько угодно символов, знак _
— один любой символ. С их помощью можно найти записи, которые начинаются каким-то символом, им заканчивается и/или содержит символы. В точечной нотации также есть методы like
и rlike
. Код выше может быть переписан так:
df.filter((df.name.like('An%')) & (df.city.like('%sc%'))) df.filter('name LIKE "An%" AND city LIKE "%sc%"')
С другой стороны, RLIKE
или метод rlike
позволяет использовать регулярные выражения. У нас даже имеется отдельная статья по регулярным выражениям. Отметим только, что нужно в начале выражения указывать знак ^
, обозначающее начало текста (например, RLIKE('^*')
).
Входит ли значение в заданный список
Возможно вам нужно проверить входит ли значения в заданный список. Например, нужно найти только всех Анн и Борисов. Тогда вам понадобится оператор IN
или метод isin
:
df.filter(df.name.isin(['Anna', 'Boris'])) df.filter('name in ("Anna", "Boris")') """ +-------------+-----+----+------+---+ | lang| name|city|salary|age| +-------------+-----+----+------+---+ |[c, java, hs]| Anna|Omsk| 27| 25| | [cpp, py]|Boris|Omsk| 55| 37| +-------------+-----+----+------+---+ """
Есть также операторы IS NULL
, IS NOT NULL
и методы isNull
и isNotNull
, предназначенные для проверки на отсутствующие значения.
Фильтруем массивы
Код курса
GRAS
Ближайшая дата курса
по запросу
Продолжительность
ак.часов
Стоимость обучения
0 руб.
Для проверки того, входит ли элемент в массив, используется функция ARRAY_CONTAINS
. Отдельного метода под нее нет. Поэтому она может содержаться в SQL-выражении, либо в виде импортированной функции из pyspark.sql.functions
.
df.filter('ARRAY_CONTAINS(lang, "py")').show() """ +------------+-----+------+------+---+ | lang| name| city|salary|age| +------------+-----+------+------+---+ |[py, c, cpp]|Anton|Moscow| 23| 19| | [py, cl]|Andry|Moscow| 24| 22| | [cpp, py]|Boris| Omsk| 55| 37| | [py, cl]| Vera|Moscow| 89| 41| +------------+-----+------+------+---+ """
Выбор значения в зависимости от условия (аналог CASE WHEN)
В прошлой статье мы использовали запрос с оператором CASE WHEN
, который на основе заданного условия, выдает тот или иной результат. Данный оператор вносит модифицирующее действие: создается новый столбец с результатами. Итак, в PySpark для этого используется цепочка методов when
, а ELSE
реализуется через otherwise
. Данная конструкция менее читаемая, чем полноценный запрос с CASE WHEN
. Вот так он выглядит:
case_when = ( F.when(F.expr('ARRAY_CONTAINS(lang, "c") OR ARRAY_CONTAINS(lang, "cpp")'), 'C/C++') .when(F.expr('ARRAY_CONTAINS(lang, "py")'), 'Python') .otherwise('uknown') ) df.withColumn('FullLang', case_when) """ +----+-----+------+------+--------+ |lang| name| city|salary|FullLang| +----+-----+------+------+--------+ | py|Anton|Moscow| 23| Python| | c| Anna| Omsk| 27| C/C++| | py|Andry|Moscow| 24| Python| | cpp| Alex|Moscow| 32| C/C++| | cpp|Boris| Omsk| 55| C/C++| | py| Vera|Moscow| 89| Python| +----+-----+------+------+--------+ """
О том, как использовать Apache Spark для фильтрации и анализа данных вы узнаете на наших образовательных курсах в лицензированном учебном центре обучения и повышения квалификации руководителей и ИТ-специалистов (менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data) в Москве: