Перейти к содержимому
TelegramGitHub

DuckDB: Up and Running, глава 7: DuckDB и JupySQL

Главная мысль: не тащите все данные в pandas, чтобы построить график. Пусть SQL посчитает итог в DuckDB. В ноутбук приходит только маленький результат. JupySQL даёт для этого SQL-ячейки и команды для графиков.

Обычный путь аналитика такой: прочитать данные из базы или CSV в DataFrame, потом строить график в matplotlib.

  • Запросы к рабочей базе могут её замедлить. Она не рассчитана на аналитику.
  • Весь набор данных лежит в памяти, даже если для графика нужны 5 чисел.
  • matplotlib тоже держит в памяти объекты рисунка.

Автор предлагает другой порядок. Фильтрация и агрегация идут в движке DuckDB. График строится по маленькой таблице.

JupySQL — расширение для Jupyter Notebook. Это форк ipython-sql, его развивает команда Ploomber. Оно добавляет SQL-ячейки.

КомандаЧто делает
%sqlОдна строка SQL (line magic)
%%sqlЯчейка с многострочным SQL (cell magic)
%sqlplotГрафик по таблице или по сохранённому запросу
%sqlcmdСлужебные команды: tables, columns, profile

Установка: pip install jupysql duckdb-engine. Пакет duckdb-engine — это драйвер DuckDB для SQLAlchemy. Для графиков нужен ещё matplotlib.

%load_ext sql
%sql duckdb://
%sql duckdb:///MyDB.db
  • duckdb:// — база в памяти. После перезапуска ядра всё пропадёт.
  • duckdb:///MyDB.db — база в файле. Таблицы останутся.
  • Не пишите комментарий в той же строке, что %load_ext sql. Он станет частью имени модуля, будет ошибка.
  • %sql? показывает справку.
  • Можно читать файлы прямо в SQL: %sql SELECT * FROM 'airlines.csv'.
  • Результат — объект ResultSet. Метод .DataFrame() делает из него pandas.
  • JupySQL показывает только 10 строк. Чтобы увидеть всё: %config SqlMagic.displaylimit = None.
  • Запрос из файла не сохраняется в базу. Чтобы сохранить, нужен CREATE TABLE ... AS.
  • %sqlcmd tables — список таблиц. %sqlcmd columns -t имя — схема. %sqlcmd profile -t имя — статистика по колонкам.

%%sql --save имя даёт запросу имя. Потом его можно использовать как таблицу.

  • Опция --no-execute только сохраняет запрос. Он не запускается.
  • Snippets живут в ноутбуке. В файл DuckDB они не попадают, даже в файловой базе.
  • Сохранённый запрос можно подставить в %sqlplot через --table или --with.
ГрафикКомандаКогда нужен
ГистограммаhistogramРаспределение числа. Есть --bins, --breaks, --binwidth
Box plotboxplotМедиана, квартили и выбросы. Опция --orient h делает его горизонтальным
КруговаяpieДоли целого. Мало категорий, лучше топ-5
СтолбчатаяbarСравнение категорий. --show-numbers пишет числа на столбцах
  • Каждая команда возвращает объект осей matplotlib. Присвойте его переменной: ax = %sqlplot .... Потом меняйте цвета, заголовок и подписи обычным кодом.
  • Два графика на одной оси дают сравнение. В примере книги так сравнивают выживших и невыживших по возрастным группам Titanic.
  • Возрастные группы считает SQL с CASE WHEN. График получает 7 строк, а не 900.

JupySQL подключается и к серверам: MySQL, PostgreSQL, SQL Server, Oracle. Для MySQL нужен драйвер mysqlclient. Книга показывает три способа хранить доступ.

СпособКак работаетМинус
Переменная DATABASE_URLСтрока подключения SQLAlchemy, пароль вводится через getpass()Пароль нужно вводить при каждом запуске
Файл .iniСекции [mysqldb] в ~/.jupysql/connections.ini. Выбор: %sql --section mysqldbПароль лежит открытым текстом
keyringПароль в хранилище паролей операционной системыНужна настройка. Автор рекомендует этот способ

Список подключений: %sql --connections. Переключиться: %sql имя_секции.

ТерминЗначение
JupySQLРасширение Jupyter для SQL-ячеек
Magic commandКоманда ноутбука с % или %%
SnippetСохранённый именованный запрос
duckdb-engineДрайвер DuckDB для SQLAlchemy
Connection stringСтрока с типом базы, пользователем, хостом и именем базы
keyringБиблиотека доступа к хранилищу паролей ОС
OutlierВыброс: значение далеко от остальных
  • Агрегируйте в SQL, рисуйте по результату. Так ноутбук не упирается в память.
  • Для быстрой EDA JupySQL удобен. Для отчётов лучше BI-инструмент или dbt с витринами.
  • Не храните пароль в ноутбуке и в git. Файл connections.ini добавьте в .gitignore или используйте keyring.
  • Ноутбук с %%sql читается как SQL-скрипт. Его проще показать коллеге, чем код на pandas.
  • Книга использует аэропорты, Boston и Titanic. Ниже практика на придуманных данных.

Запустите duckdb в терминале или откройте shell.duckdb.org. Блоки идут по порядку. Магические команды JupySQL (%sql, %sqlplot) работают только в Jupyter. Мы отрабатываем ту часть, которую делает SQL: итоги для графиков.

-- 200 пассажиров: возраст, класс и признак «рейс прибыл вовремя»
CREATE TABLE passengers AS
SELECT
i AS passenger_id,
(5 + (i * 37) % 70)::INT AS age,
CASE WHEN (i * 11) % 5 < 2 THEN 'business' ELSE 'economy' END AS cabin,
(i * 7) % 3 = 0 AS on_time
FROM range(1, 201) t(i);
-- должно быть 200 строк, возраст от 5 до 74
SELECT count(*) AS n, min(age) AS min_age, max(age) AS max_age FROM passengers;

То, что %sqlplot bar рисует, — это такая таблица. Возрастные группы считает SQL.

CREATE TABLE age_groups AS
SELECT
CASE
WHEN age < 20 THEN '0-19'
WHEN age < 40 THEN '20-39'
WHEN age < 60 THEN '40-59'
ELSE '60+'
END AS age_group,
count(*) FILTER (WHERE on_time) AS on_time_count,
count(*) FILTER (WHERE NOT on_time) AS late_count
FROM passengers
GROUP BY age_group
ORDER BY age_group;
SELECT * FROM age_groups;

1. Посчитайте число пассажиров в каждом cabin. Это данные для круговой диаграммы.

Решение
SELECT cabin, count(*) AS n FROM passengers GROUP BY cabin ORDER BY n DESC;

economy — 120, business — 80. Для pie chart хватает двух строк.

2. Найдите медиану и квартили age. Это данные для box plot.

Решение
SELECT
quantile_cont(age, 0.25) AS q1,
median(age) AS q2,
quantile_cont(age, 0.75) AS q3
FROM passengers;

Q1 = 21,75, медиана = 40, Q3 = 56,25. Box plot строится из этих трёх чисел, min и max.

3. Постройте данные для гистограммы с шагом 10 лет: колонка bucket и число пассажиров.

Решение
SELECT (age // 10) * 10 AS bucket, count(*) AS n
FROM passengers
GROUP BY bucket
ORDER BY bucket;

Восемь строк от 0 до 70. В крайних корзинах по 14 человек, в остальных 27–30. Все 200 строк в pandas не нужны.

  1. Почему лучше агрегировать данные в SQL, а не в pandas, перед построением графика?
  2. Чем отличаются %sql и %%sql?
  3. Что делает опция --no-execute у %%sql --save? Где живёт snippet?
  4. Какой из трёх способов хранить пароль MySQL лучший и почему?

Предыдущая: Глава 6. JSON. Следующая: Глава 8. Удалённые данные.