Jupyter Notebook上でSQLをらくらく実行するJupySQL

Jupyter Notebook上でSQLをらくらく実行するJupySQL

Jupyter NotebookJupyter Labといった、Notebookでデータ分析業務をする人が、ここ数年で増えてきました。

Notebook上でデータ分析をするとき、外部のCSVファイルやDBのデータテーブルなどからデータセットを取得する必要があります。

よくあるのが、Pandasを利用し取得したデータセットをデータフレームに格納するケースです。

データ規模が大きいほど、ある問題が起こります。

例えば、メモリの消費の問題です。

データフレームに格納するということは、PCなどのメモリをそれだけ消費します。

多くの場合、読み込んだ直後のデータフレームは、そのままデータ分析に使えることは少なく、何かしらの加工なり処理がなされ整えられます。

ある程度整えられたデータセットの状態で、データフレームとして格納した方が、メモリの消費量は少なくてすみます。

他には、外部DBのパフォーマンス低下の問題もあります。

外部DBからデータセットを取得するとき、何かしらのSQLクエリを実行します。

そのクエリ実行はDBサイドで実施されるため、データベースサーバーに余計な負荷をかけることになります。

そのため、SQLクエリの実行をクライアントサイド(Jupyter Notebookサイド)で実施するのがいいでしょう。

ただ、Jupyter Notebookサイドで実施するにしても、PCのメモリを可能な限り節約したいものです。

それを可能にするのが、DuckDBJupySQLの組み合わせです。

今回は、「Jupyter Notebook上でSQLをらくらく実行するJupySQL×DuckDB」というお話しをします。

DuckDBそのものについては、以下の記事で簡単に紹介していますので、気になる方は一読して頂ければと思います。

JupyLabのインストール

取り急ぎ、JupyLabライブラリー(パッケージ)をインストールしましょう。

今回は、DuckDBと統合し利用するので、DuckDBエンジンもインストールします。

以下、condaでインストールするときのコードです。

conda install -c conda-forge jupysql duckdb-engine

 

以下、pipでインストールするときのコードです。

pip install jupysql duckdb-engine

 

今回利用するサンプルデータ

DB(データベース)によくあるテーブルっぽいデータを今回利用します。

Kaggleに公開されている「フライト遅延データセット」です。

以下の弊社サイトのURLからもダウンロードできます。

2015 Flight Delays and Cancellations
https://www.salesanalytics.co.jp/1tzu

次の3つのデータテーブルからなります。

  • airlines.csv(航空会社マスタテーブル)
  • airports.csv(空港マスタテーブル)
  • flights.csv(フライトのトランザクションデータテーブル)

 

このデータセットの中は、先ほど紹介した以下の記事でも見ていますので、気になる方はチラ見してください。

 

SQLを実行するときの3つのマジックコマンド

次のマジックコマンドを利用し、Notebook上でJupySQLを使っていきます。

  • %sql: 1行のSQL命令文の実行
  • %%sql:複数行のSQL命令文の実行(セル内がSQL環境になる)
  • %sqlplot:グラフ表示

 

JupySQLの簡単な操作

 JupySQLを利用する準備とDuckDBとの統合

先ずは、SQLのエクステンションを読み込みます。

以下、コードです。

# SQL 拡張機能をロード
%load_ext sql

 

次に、「%sql」を使いインメモリ上のDuckDBに接続します。

以下、コードです。

# DuckDB インメモリ データベースを開始
%sql duckdb://

 

これで、準備は完了です。

DB内のテーブルを確認してみます。

以下、コードです。

# DB内のテーブルを確認
%sql SHOW TABLES

 

以下、実行結果です。

 

当然ですが、何もありません。

 

 外部のCSVファイルに対しクエリを実行

外部のCSVファイルに対しSQLのクエリを実行してみます。

airports.csv5レコード表示させます。

以下、コードです。

# 外部のcsvファイルに対しクエリを実行
%sql SELECT * FROM airports.csv LIMIT 5

 

以下、実行結果です。

 

クエリの実行結果は「_」に一時的に格納されています。print文で確認してみます。

以下、コードです。

# クエリの実行結果をprint文で出力
print(_)

 

以下、実行結果です。

+-----------+-------------------------------------+-------------+-------+---------+----------+------------+
| IATA_CODE |               AIRPORT               |     CITY    | STATE | COUNTRY | LATITUDE | LONGITUDE  |
+-----------+-------------------------------------+-------------+-------+---------+----------+------------+
|    ABE    | Lehigh Valley International Airport |  Allentown  |   PA  |   USA   | 40.65236 |  -75.4404  |
|    ABI    |       Abilene Regional Airport      |   Abilene   |   TX  |   USA   | 32.41132 |  -99.6819  |
|    ABQ    |  Albuquerque International Sunport  | Albuquerque |   NM  |   USA   | 35.04022 | -106.60919 |
|    ABR    |      Aberdeen Regional Airport      |   Aberdeen  |   SD  |   USA   | 45.44906 | -98.42183  |
|    ABY    |  Southwest Georgia Regional Airport |    Albany   |   GA  |   USA   | 31.53552 | -84.19447  |
+-----------+-------------------------------------+-------------+-------+---------+----------+------------+

 

SQLが複雑というか長文になってしまうとき、どうしても複数行になってしまうことがあります。

このようなときは、「%%sql」です。セル内がSQL環境になります。

flights.csvファイルに対し、AIRLINEごとの件数をカウントし、それを降順で表示します。

以下、コードです。

%%sql 
SELECT 
    count(*) as Count,
    AIRLINE
FROM flights.csv
GROUP BY AIRLINE
ORDER BY Count DESC;

 

以下、実行結果です。

 

「_」に一時的に格納されている結果をprint文で確認してみます。

以下、コードです。

# クエリの実行結果をprint文で出力
print(_)

 

以下、実行結果です。

+---------+---------+
|  Count  | AIRLINE |
+---------+---------+
| 1261855 |    WN   |
|  875881 |    DL   |
|  725984 |    AA   |
|  588353 |    OO   |
|  571977 |    EV   |
|  515723 |    UA   |
|  294632 |    MQ   |
|  267048 |    B6   |
|  198715 |    US   |
|  172521 |    AS   |
|  117379 |    NK   |
|  90836  |    F9   |
|  76272  |    HA   |
|  61903  |    VX   |
+---------+---------+

 

 クエリの保存と実行

SQLのクエリを保存し、後で利用したり、他のテーブルと組み合わせ使ったりすることもあるでしょう。

先ほどのクエリを「flights_count」という名前を保存してみます。

以下、コードです。

%%sql --save flights_count --no-execute
SELECT 
    count(*) as Count,
    AIRLINE
FROM flights.csv
GROUP BY AIRLINE
ORDER BY Count DESC

 

%%sql」の後ろに「–save 〇〇」をつけることで「〇〇」という名で保存されます。

さらに、後ろに「–no-execute」を付けることで即時実行を回避しています。「–no-execute」を付けないと、保存されるとともにクエリも実行されます。

 

では次に、保存したクエリを使い、実行してみます。

以下、コードです。

%%sql --with flights_count
SELECT * FROM flights_count;

 

以下、実行結果です。

 

 クエリの実行結果を格納しDataFrameへ

クエリは実行したあと「_」に一時的に保存されますが、他のクエリを実行すると消え去ります。

実行結果をもう少し長く保持したいこともあります。

%%sql」の後に「〇〇 <<」を付けることで、「〇〇」に結果が格納されます。

以下、コードです。

%%sql result <<
SELECT 
    count(*) as Count,
    AIRLINE
FROM flights.csv
GROUP BY AIRLINE
ORDER BY Count DESC;

 

格納したクエリの実行結果を表示してみます。

以下、コードです。

# クエリの実行結果をprint文で出力
print(result)

 

以下、実行結果です。

+---------+---------+
|  Count  | AIRLINE |
+---------+---------+
| 1261855 |    WN   |
|  875881 |    DL   |
|  725984 |    AA   |
|  588353 |    OO   |
|  571977 |    EV   |
|  515723 |    UA   |
|  294632 |    MQ   |
|  267048 |    B6   |
|  198715 |    US   |
|  172521 |    AS   |
|  117379 |    NK   |
|  90836  |    F9   |
|  76272  |    HA   |
|  61903  |    VX   |
+---------+---------+

 

DataFrameに変換し表示させてみます。

以下、コードです。

# DataFrameに変換し出力
print(result.DataFrame())

 

以下、実行結果です。

      Count AIRLINE
0   1261855      WN
1    875881      DL
2    725984      AA
3    588353      OO
4    571977      EV
5    515723      UA
6    294632      MQ
7    267048      B6
8    198715      US
9    172521      AS
10   117379      NK
11    90836      F9
12    76272      HA
13    61903      VX

 

 クエリの実行結果をCSVファイとして出力

クエリの実行結果をCSVファイルとして外部に出力したいこともあります。

以下、コードです。

%%sql
COPY(
    SELECT 
        count(*) as Count,
        AIRLINE
    FROM flights.csv
    GROUP BY AIRLINE
    ORDER BY Count DESC
)
TO 'flights_count.csv' (HEADER, DELIMITER ',');

 

flights_count.csv」というファイル名のCSVファイルが作成されたかと思います。

この外部に出力したCSVファイルを、逆に読み込んでみます。

以下、コードです。

%sql SELECT * FROM flights_count.csv

 

以下、実行結果です。

 

 データテーブルを追加

ここで、DBの中を見てみます。

以下、コードです。

# DB内のテーブルを確認
%sql SHOW TABLES

 

以下、実行結果です。

 

空です。

外部データをSQLで操作しただけなので当然と言えば、当然です。

 

外部のCSVファイルから3つのテーブルを作成します。

以下、コードです。

%%sql
CREATE TABLE airports AS
SELECT * 
FROM airports.csv;

CREATE TABLE airlines AS
SELECT * 
FROM airlines.csv;

CREATE TABLE flights AS
SELECT * 
FROM flights.csv;

 

DBの中を確認してみます。

以下、コードです。

# DB内のテーブルを確認
%sql SHOW TABLES

 

以下、実行結果です。

 

3つのテーブルが存在することが分かります。

 

さらに、作成したテーブルから別のテーブルを作成してみます。

以下、コードです。

%%sql
CREATE TABLE flights_count AS
SELECT 
    count(*) as Count,
    AIRLINE
FROM flights
GROUP BY AIRLINE
ORDER BY Count DESC;

 

DBの中を確認してみます。

以下、コードです。

# DB内のテーブルを確認
%sql SHOW TABLES

 

以下、実行結果です。

 

テーブルが4つになっていることが分かります。

 

DuckDBをファイルに残し後から使う

 DuckDBをファイルとして保存

インメモリなDBであるDuckDBは、Notebookを終了させたりカーネルをリスタートさせたりすると、当然ですが消えてなくなります。インメモリだからです。

ファイルとして保存し後から使えないだろうかと考える人もいることでしょう。

ファイルとして保存するには、DuckDBを利用するスタート時点である設定をする必要があります。

 

例えば、「flight.duckdb」というファイル名で残すとします。

以下、コードです。

#
# DuckDBをファイルとして保存
#

# SQL 拡張機能をロード
%load_ext sql

# DBをファイルとして残す場合
%sql duckdb:///flight.duckdb

 

先ほどは、最初にDuckDB インメモリ データベースを開始するとき、「%sql duckdb://」でした。

しかし、今回は「%sql duckdb:///flight.duckdb」としています。要は、「%sql duckdb://」の後ろに「/ファイル名」とファイル名を指定しただけです。

 

この状態で、先ほどと同じ4つのテーブルを作成します。

以下、コードです。

%%sql
CREATE TABLE airports AS
SELECT * 
FROM airports.csv;

CREATE TABLE airlines AS
SELECT * 
FROM airlines.csv;

CREATE TABLE flights AS
SELECT * 
FROM flights.csv;

CREATE TABLE flights_count AS
SELECT 
    count(*) as Count,
    AIRLINE
FROM flights
GROUP BY AIRLINE
ORDER BY Count DESC;

 

以下、実行結果です。

 

先ほどまでと異なり、「* duckdb:///flight.duckdb」が表示されています。

 

念のため、DB内を見てみます。

以下、コードです。

# DB内のテーブルを確認
%sql SHOW TABLES

 

以下、実行結果です。

 

 保存したDuckDBファイルへ接続し開始

最後に、保存したDuckDBDBファイルを読み込んでみます。

開始時に次のようにします。

以下、コードです。

#
# 保存したDuckDBファイルへ接続し開始
#

# SQL 拡張機能をロード
%load_ext sql

# DuckDBファイルへ接続
%sql duckdb:///flight.duckdb

 

DB内を見てみます。

以下、コードです。

# DB内のテーブルを確認
%sql SHOW TABLES

 

以下、実行結果です。

 

まとめ

今回は、「Jupyter Notebook上でSQLをらくらく実行するJupySQL×DuckDB」というお話しをしました。

DuckDBNotebook上でフル活用したいなら、JupySQLがとっても便利です。

興味を持った方は、試してみてください。

Pythonデータ分析のためのDuckDB(組み込み用途のOLAP系のRDBMS)