30 SQL-запросов, объяснённых через их эквиваленты Pandas

В мире, где с 1974 года доминирует SQL, в 2008 году появился Pandas, предлагающий привлекательные функции, такие как встроенная визуализация и гибкая обработка данных. Pandas быстро стал популярным инструментом для исследования данных, затмив собой SQL.

Но SQL по-прежнему держит свои позиции. Это второй по востребованности и третий по скорости роста язык для Data science (см. здесь ). Таким образом, в то время как Pandas привлекает всеобщее внимание, SQL остаётся жизненно важным навыком для любого специалиста по данным.

Давайте узнаем, как легко выучить SQL, если вы уже знаете Pandas.

https://t.me/sqlhub – изучение sql в одном канале

Подключение к базе данных

Настройка рабочей области SQL и подключение базы данных может быть настоящей головной болью. Во-первых, вам нужно установить предпочитаемый вариант SQL (PostgreSQL, MySQL и т. д.) и загрузить SQL IDE. Выполнение их здесь увело бы нас от цели статьи, поэтому мы воспользуемся сокращением.

В частности, мы будем напрямую запускать SQL-запросы в Jupyter Notebook без дополнительных шагов. Всё, что нам нужно сделать, это установить пакет ipython-sql с помощью pip:

pip install ipython-sql

После установки запустите новый сеанс Jupyter и выполните эту команду в блокноте:

%load_ext sql

и всё готово!

Чтобы проиллюстрировать, как работают основные операторы SQL, мы будем использовать базу данных Chinook SQLite, которая имеет 11 таблиц.

Чтобы загрузить набор данных и его 11 таблиц как отдельные переменные в нашу среду, мы можем запустить:

%sql sqlite:///data/chinook.db

Оператор начинается со встроенной волшебной команды %sql, которая сообщает интерпретатору блокнота, что мы будем запускать команды SQL. За ней следует путь, по которому находится загруженная база данных Chinook.

Допустимые пути всегда должны начинаться с префикса sqlite:/// для баз данных SQLite. Выше мы подключаемся к базе данных, хранящейся в папке «data» текущего каталога. Если вы хотите передать абсолютный путь, префикс должен начинаться с четырех косых черт:sqlite:////

Если вы хотите подключиться к другой разновидности базы данных, вы можете обратиться к этой отличной статье .

Первый взгляд на таблицы

Первое, что мы всегда делаем в Pandas, — это используем функцию .head() для первого взгляда на данные. Давайте узнаем, как это сделать в SQL:

%%sql

SELECT * FROM customers
LIMIT 5
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Первое ключевое слово в приведённом выше запросе — SELECT. Оно эквивалентно оператору скобок в Pandas, где мы выбираем определённые столбцы. Но за ключевым словом SELECT следует * (звездочка). * — оператор SQL, который выбирает всё (все строки и столбцы) из таблицы, указанной после ключевого слова FROM. LIMIT используется для минимизации возвращаемого результата. Таким образом, приведённый выше запрос эквивалентен функции df.head().

Если вы не хотите выбирать все столбцы, вы можете указать одно или несколько имён столбцов после ключевого слова SELECT:

%%sql

SELECT Name, Composer, UnitPrice
FROM tracks
LIMIT 10
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Эквивалентная операция Pandas:

tracks[['Name', 'Composer', 'UnitPrice']].head(10)

Другое полезное ключевое слово в SQL — DISTINCT. Добавление этого ключевого слова перед именем любого столбца возвращает его уникальные значения:

%%sql

SELECT DISTINCT FirstName
FROM employees

-- equivalent to `.unique()`
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Комментарии в SQL пишутся с двойным дефисом.

Подсчёт количества строк

Точно так же, как у Pandas есть атрибут .shape для своих DataFrames, у SQL есть функция COUNT для отображения количества строк в таблице:

%%sql

SELECT COUNT(*) FROM tracks
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Более полезной информацией будет подсчёт количества уникальных значений в определённом столбце. Мы можем сделать это, добавив ключевое слово DISTINCT в COUNT:

%%sql 

SELECT COUNT(DISTINCT FirstName) FROM employees

-- equivalent to `employees['FirstName'].nunique()`
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Фильтрация результатов с помощью предложений WHERE

Просто смотреть и считать строки может любой. Давайте посмотрим, как мы можем фильтровать строки на основе условий.

Во-первых, давайте посмотрим на песни, которые стоят больше доллара:

%%sql

SELECT * FROM tracks
WHERE UnitPrice > 1.0
LIMIT 10

--tracks[tracks['UnitPrice' > 1.0]]
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Условные операторы записываются в предложении WHERE, которое всегда следует после FROM и перед ключевыми словами LIMIT. Использование условий очень похоже на то, как мы это делаем в Pandas, но я осмелюсь сказать, что версия SQL более читабельна.

Вы также можете использовать функцию COUNT при использовании условных выражений. Например, давайте посмотрим количество песен с ценой от 1 до 10 долларов:

%%sql

SELECT COUNT(*) FROM tracks
WHERE UnitPrice > 1.0 AND UnitPrice < 10

-- tracks[(tracks['UnitPrice'] > 1) & (tracks['UnitPrice'] < 10)]
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Выше мы связали два условия логическим оператором AND. Аналогично используются и другие логические операторы (OR, NOT).

Теперь давайте посмотрим все счета-фактуры, в которых в качестве города выставления счётов указан Париж или Берлин:

%%sql

SELECT BillingAddress, BillingCity, Total
FROM invoices
WHERE BillingCity = 'Paris' OR BillingCity = 'Berlin'
LIMIT 5

--invoices[(invoices['BillingCity'] == 'Paris') |
--         (invoices['BillingCity'] == 'Berlin')]
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Оператор равенства в SQL требует только одного знака «=» (равно). Оператор неравенства представлен операторами ‘!=’ или ‘<>’:

%%sql

SELECT BillingAddress, BillingCity, Total
FROM invoices
WHERE BillingCity != 'Paris' AND BillingCity <> 'Berlin'
LIMIT 5
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Упрощённая фильтрация с помощью BETWEEN и IN

Подобные условные операторы используются очень часто, и их запись с помощью простых логических значений становится громоздкой. Например, в Pandas есть функция .isin(), которая проверяет, принадлежит ли значение списку значений.

Если бы мы хотели выбрать счета для пяти городов, нам пришлось бы написать пять связанных условий. К счастью, SQL поддерживает аналогичный оператор IN, поэтому .isin() нам не нужно:

%%sql

SELECT * 
  FROM invoices
 WHERE BillingCity IN ('Berlin', 'Paris', 'New York', 'Boston', 'London')
 LIMIT 5
    
--invoices[invoices['BillingCity'].isin(
--      ('Berlin', 'Paris', 'New York', 'Boston', 'London')
--)]
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Список значений после IN следует указывать в виде кортежа, а не списка. Вы также можете отменить условие с помощью ключевого слова NOT:

%%sql

SELECT * 
  FROM invoices
 WHERE BillingCity NOT IN ('Berlin', 'Paris', 'New York', 'Boston', 'London')
 LIMIT 5
    
--invoices[~invoices['BillingCity'].isin(
--    ('Berlin', 'Paris', 'New York', 'Boston', 'London')
--)]
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Другой распространённой операцией фильтрации числовых столбцов является выбор значений в пределах диапазона. Для этого можно использовать ключевое слово BETWEEN, которое эквивалентно pd.Series.between():

%%sql

SELECT BillingCity, Total
FROM invoices
WHERE Total BETWEEN 5 AND 15
LIMIT 5

--invoices[invoices['Total'].between(5, 15)]
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Проверка на нули

В каждом источнике данных есть пропущенные значения, и базы данных не являются исключением. Точно так же, как есть несколько способов исследовать отсутствующие значения в Pandas, существуют определённые ключевые слова, которые проверяют наличие нулевых значений в SQL. Следующий запрос подсчитывает количество строк с пропущенными значениями в BillingState:

%%sql

SELECT COUNT(*) FROM
invoices
WHERE BillingState IS NULL

--invoices['BillingState'].isnull().sum()
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Вы можете добавить ключевое слово NOT между IS и NULL, чтобы отбросить пропущенные значения определённого столбца:

%%sql

SELECT InvoiceDate, BillingCountry 
FROM invoices
WHERE Total IS NOT NULL
LIMIT 10
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Лучшее сопоставление строк с LIKE

В предложении WHERE мы отфильтровали столбцы на основе точных текстовых значений. Но часто нам может понадобиться отфильтровать текстовые столбцы на основе шаблона. В Pandas и чистом Python мы использовали бы регулярные выражения для сопоставления с образцом, которые очень эффективны, но для освоения регулярных выражений требуется время.

В качестве альтернативы SQL предлагает подстановочный знак ‘%’ в качестве заполнителя для соответствия любому символу 0 или более раз. Например, строка «gr%» соответствует «greet», «groom», «greed» и «%ex%» соответствует любому тексту с «ex» в середине и т. д. Давайте посмотрим, как использовать её с SQL:

%%sql

SELECT Name, Composer, UnitPrice 
  FROM tracks
 WHERE Name LIKE 'B%'
 LIMIT 5

--tracks[tracks['Name'].str.startswith('B')]
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Приведённый выше запрос находит все песни, начинающиеся с «B». Строка, содержащая подстановочный знак, должна идти после ключевого слова LIKE.

Теперь давайте найдём все песни, в названии которых есть слово «beautiful»:

%%sql

SELECT Name, Composer, UnitPrice
  FROM tracks
 WHERE Name LIKE '%beautiful%'

--tracks[tracks['Name'].str.contains('beautiful')]
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Вы также можете использовать другие логические операторы рядом с LIKE:

%%sql

SELECT Name, Composer, UnitPrice
  FROM tracks
 WHERE (Name LIKE 'F%') AND (UnitPrice > 1.0)

--tracks[(tracks['Name'].str.startswith('F')) & (tracks['UnitPrice'] > 1.0)]
30 SQL-запросов, объяснённых через их эквиваленты Pandas

В SQL есть много других подстановочных знаков, которые имеют разные функции. Вы можете увидеть полный список и их использование здесь .

Агрегатные функции в SQL

Также возможно выполнять основные арифметические операции над столбцами. Эти операции называются агрегатными функциями в SQL, и наиболее распространёнными из них являются AVG, SUM, MIN, MAX. Их функциональность должна быть ясна из их названий:

%%sql 

SELECT SUM(Total), MAX(Total), 
       MIN(Total), AVG(Total)
FROM invoices
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Агрегатные функции дают только один результат для столбца, в котором вы их использовали. Это означает, что вы не можете выполнять агрегирование по одному столбцу и выбирать другие неагрегированные столбцы:

%%sql

SELECT AVG(Total), BillingCity, BillingAddress
FROM invoices;

-- the result will be a single row because
-- of the presence of aggregate functions
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Вы можете так же легко комбинировать агрегатные функции с условными операторами, используя предложения WHERE:

%%sql

SELECT AVG(Total), BillingCity
FROM invoices
WHERE BillingCity = 'Paris'

-- invoices[invoices['BillingCity']]['Total'].mean()
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Также можно использовать арифметические операторы, такие как +, -, *, / в столбцах и простые числа. При использовании со столбцами операция выполняется поэлементно:

%%sql

SELECT SUM(Total) / COUNT(Total)
FROM invoices

-- finding the mean manually
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Одно замечание об арифметических операциях: если вы выполняете операции только с целыми числами, SQL считает, что вы ожидаете целое число в качестве ответа:

%%sql
SELECT 10 / 3
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Вместо того, чтобы возвращать 3,33…, результат равен 3. Чтобы получить результат с плавающей запятой, вы должны использовать в запросе хотя бы одно число с плавающей запятой или использовать все числа с плавающей запятой, чтобы быть в безопасности:

%%sql
SELECT 10.0 / 3.0
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Используя эти знания, рассчитаем среднюю продолжительность песни в минутах:

%%sql

SELECT Milliseconds / 1000.0 / 60.0
FROM tracks
LIMIT 10
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Если вы обратите внимание на приведённый выше столбец, его имя написано как «запрос, используемый для создания этого столбца ». Из-за этого поведения использование длинных вычислений, таких как определение стандартного отклонения или дисперсии столбца, может быть проблемой, поскольку имя столбца будет таким же большим, как и сам запрос.

Чтобы избежать этого, SQL допускает использование псевдонимов, подобно тому, как операторы импорта имеют псевдонимы в Python. Например:

%%sql

SELECT SUM(Total) as sum_total
FROM invoices
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Использование ключевого слова as после одного элемента в операторе SELECT сообщает SQL, что мы используем псевдоним. Вот ещё примеры:

%%sql

SELECT SUM(Total) as sum_total,
       MAX(Total) as max_total,
       MIN(Total) as min_total,
       SUM(Total) / COUNT(Total) as mean_total
FROM invoices
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Вы можете так же легко использовать псевдонимы для столбцов с длинными именами.

Упорядочивание результатов в SQL

Так же, как у Pandas есть метод sort_values, SQL поддерживает упорядочение столбцов через предложение ORDER BY. Передача имени столбца после предложения сортирует результаты в порядке возрастания:

%%sql

SELECT Name, Composer, UnitPrice
FROM tracks
WHERE Composer <> 'None'
ORDER BY Composer
LIMIT 10

-- tracks.sort_values('Compose')[['Name', 'Compose', 'UnitPrice']]
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Упорядочим таблицу треков по возрастанию имени композитора. Обратите внимание, что оператор ORDER BY всегда должен стоять после предложения WHERE. Также можно передать два или более столбца в ORDER BY:

%%sql

SELECT Name, Composer, UnitPrice
FROM tracks
WHERE Composer <> 'None'
ORDER BY UnitPrice, Composer, Name
LIMIT 10

-- tracks.sort_values(['UnitPrice', 'Composer', 'Name'])\
-- [['Name', 'Compose', 'UnitPrice']]
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Вы также можете изменить порядок, передав ключевое слово DESC после имени каждого столбца:

%%sql

SELECT Name, Composer, UnitPrice
FROM tracks
WHERE Composer != 'None'
ORDER BY UnitPrice DESC, Composer DESC, Name ASC
LIMIT 10

-- tracks.sort_values(['UnitPrice', 'Composer', 'Name'])\
-- [['Name', 'Compose', 'UnitPrice']]
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Приведённый выше запрос возвращает три столбца после упорядочения UnitPrice и Compose в порядке убывания и имени в порядке возрастания ( ключевое слово ASC по умолчанию).

Группировка в SQL

Одной из самых мощных функций Pandas является расширение groupby. Вы можете использовать его, чтобы преобразовать таблицу практически в любую форму, которую вы хотите. Его очень близкий родственник в SQL – GROUP BY, который можно использовать для достижения той же функциональности. Например, следующий запрос подсчитывает количество песен в каждом жанре:

%%sql

SELECT GenreID, COUNT(*) as genre_count
FROM tracks
GROUP BY GenreId
LIMIT 10

-- tracks.groupby('GenreId')['GenreID'].count()
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Разница между GROUP BY в SQL и groupby в Pandas заключается в том, что SQL не позволяет выбирать столбцы, которые не были указаны в предложении GROUP BY. Например, добавление дополнительного свободного столбца в приведённый выше запрос приводит к ошибке:

%%sql

SELECT Name, GenreID, COUNT(*) as genre_count
FROM tracks
GROUP BY GenreId

syntax error at or near "GROUP"
LINE 1: GROUP BY GenreId;

Однако вы можете выбрать столько столбцов в операторе SELECT, сколько хотите, если вы используете для них какой-либо тип агрегатной функции:

%%sql

SELECT GenreId, AlbumId, 
       COUNT(*) as genre_count,
       AVG(Milliseconds) / 1000.0 / 60.0 as avg_duration,
       AVG(UnitPrice) as avg_price
  FROM tracks
 GROUP BY GenreId, AlbumId
 LIMIT 10
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Приведённый выше запрос включает почти все темы, которые мы изучили до этого момента. Мы группируем как по идентификатору альбома, так и по идентификатору жанра, и для каждой группы мы рассчитываем среднюю продолжительность и цену песни. Мы также эффективно используем псевдонимы.

Мы можем сделать запрос ещё более мощным, упорядочив его по средней продолжительности и количеству жанров:

%%sql

SELECT GenreId, AlbumId, 
       COUNT(*) as genre_count,
       AVG(Milliseconds) / 1000.0 / 60.0 as avg_duration,
       AVG(UnitPrice) as avg_price
  FROM tracks
 GROUP BY GenreId, AlbumId
 ORDER BY avg_duration DESC, genre_count DESC
 LIMIT 10
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Обратите внимание на то, как мы используем псевдонимы агрегатных функций в предложении ORDER BY. После того как вы назовёте столбец или результат агрегатной функции псевдонимом, вы сможете ссылаться на них только по псевдониму для остальной части запроса.

Использование условных выражений с HAVING

По умолчанию SQL не разрешает условную фильтрацию с использованием агрегатных функций в предложении WHERE. Например, мы хотим выбрать только те жанры, в которых количество песен превышает 100. Попробуем сделать это с предложением WHERE:

%%sql

SELECT GenreId
FROM tracks
GROUP BY GenreId
WHERE COUNT(GenreId) > 10

syntax error at or near "WHERE"
LINE 1: where COUNT(GenreId) > 10

Правильный способ фильтрации строк на основе результатов агрегатных функций — использование предложения HAVING:

%%sql

SELECT GenreId
FROM tracks
GROUP BY GenreId
HAVING COUNT(GenreId) > 100
30 SQL-запросов, объяснённых через их эквиваленты Pandas

Предложение HAVING обычно используется с GROUP BY. Всякий раз, когда вы хотите отфильтровать строки с помощью агрегатных функций, используйте предложение HAVING!

Краткое содержание

К настоящему времени вы должны были понять, насколько мощным может быть SQL и насколько более читабельным он становится по сравнению с Pandas. Несмотря на то, что мы узнали тонну, мы едва лишь поцарапали поверхность.

Для практических задач я рекомендую Data Lemur или LeetCode, если вы любите приключения.

+1
8
+1
2
+1
0
+1
0
+1
0

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *