📚

Python実践機械学習100本ノック3章(with JupyterLab)

2021/01/17に公開

Python実践機械学習100本ノック3章(with JupyterLab)

Python実践機械学習システム100本ノック の3章「可視化の仕組みを構築する10本ノック」に ipywidgets を使ったコード例がありますが、Jupyter NotebookやGoogle Colabではきちんとウィジェットが表示されるものの、JupyterLabでは上手く動いてくれませんでした。

ここでは、JupyterLabで書籍で示されているものと(おおむね)同じウィジェット操作ができるようなコードを紹介します。

環境と事前の準備

Windows環境

  • Windows 10 Pro/Home 1909 64bit
  • Python 3.9.1
  • pip 20.3.3
  • pipenv 2020.11.15
  • Node.js 14.15.3

JupyterLab起動環境

  • Python 3.8.7
  • JupyterLab 3.0.4
  • Jupyterlab-widgets 1.0.0

カーネル

JupyterLab起動環境がカーネルを兼ねている場合は、不足しているパッケージを上の環境にインストールしてください。

  • Python 3.8.7
  • ipykernel 5.4.2
  • ipywidgets 7.6.2
  • pandas 1.2.0
  • matplotlib 3.3.3
  • japanize-matplotlib 1.1.3

ipywidgets の各種ウィジェットについて

Widget List — Jupyter Widgets

下準備

モジュールのインポートとデータの読み込みを行います。

JupyterLab
# Notebookで使うモジュールのインポート
import datetime
import pandas as pd
import matplotlib.pyplot as plt
import japanize_matplotlib
from ipywidgets import (interact, Dropdown, SelectMultiple,
                        IntSlider, ToggleButtons, DatePicker)
JupyterLab
# ここは本の通り。
# まずcsvファイルのデータを読み込む。
m_store = pd.read_csv("m_store.csv")
m_area = pd.read_csv("m_area.csv")

# データをマージする。
order_data = pd.read_csv("tbl_order_202004.csv")
order_data = pd.merge(order_data, m_store, on="store_id", how="left")
order_data = pd.merge(order_data, m_area, on="area_cd", how="left")

# 読みやすくするためのデータを追加。
order_data.loc[order_data["takeout_flag"]==0, "takeout_name"] = "デリバリー"
order_data.loc[order_data["takeout_flag"]==1, "takeout_name"] = "お持ち帰り"
order_data.loc[order_data["status"]==0, "status_name"] = "受付"
order_data.loc[order_data["status"]==1, "status_name"] = "お支払済み"
order_data.loc[order_data["status"]==2, "status_name"] = "お渡し済み"
order_data.loc[order_data["status"]==3, "status_name"] = "キャンセル"

order_data.head()  # 出力結果は省略。

ノック21: 店舗を絞り込んで可視化できるようにしてみよう

ここではドロップダウンリストで店舗名を絞り込んで、pandas.DataFrame.head() や折れ線グラフを表示します。

以降のノックにも共通しますが、ipywidgetsDropdown クラスのインスタンスを、表示を行う関数のデコレーターである interact() に渡します。

ドロップダウンで店舗名を絞り込んだ DataFrame の表示

JupyterLab
store_list = m_store["store_name"].tolist()

drop_down_0 = Dropdown(
    options=store_list,
    description="store_name",
)


@interact(store_name=drop_down_0)
def order_by_store(store_name):
    pick_data = order_data.loc[
        (order_data["store_name"] == store_name)
        & (order_data["status"].isin([1, 2]))
    ]
    return pick_data.head()

セルを実行すると下記のように抽出されたデータの先頭が表示されます。また、ドロップダウンの別の店舗名を選択すると表示される内容が即座に変わることが確認できます。


出力

ドロップダウンで店舗名を絞り込んだ折れ線グラフの表示

JupyterLab
drop_down_1 = Dropdown(
    options=store_list,
    description="store_name",
)


@interact(store_name=drop_down_1)
def graph_by_store(store_name):
    pick_data = order_data.loc[
        (order_data["store_name"] == store_name)
        & (order_data["status"].isin([1, 2]))
    ]

    temp = pick_data[["order_accept_date", "total_amount"]].copy()
    temp.loc[:, "order_accept_date"] = pd.to_datetime(temp["order_accept_date"])
    temp.set_index("order_accept_date", inplace=True)
    temp.resample("D").sum().plot(grid=True)


出力

ドロップダウン補足

なお、上のふたつの例は Dropdown クラスを使わなくても、

JupyterLab
store_list = m_store["store_name"].tolist()


@interact(store_name=store_list)
def order_by_store(store_name):
    # 以下略

のように直接店舗名リストを渡しても同様の結果となります。

ノック22: 複数店舗の詳細を可視化できるようにしてみよう

複数選択した項目で店舗名を絞り込んだ DataFrame の表示

JupyterLab
m_store_list_0 = SelectMultiple(
    options=store_list,
    discription="store_names"
)


@interact(store_names=m_store_list_0)
def order_by_multi(store_names):
    pick_data = pick_data = order_data.loc[
        (order_data["store_name"].isin(store_names))
        & (order_data["status"].isin([1, 2]))
    ]
    return pick_data.head()


出力

複数選択した項目で店舗名を絞り込んだ折れ線グラフの表示

JupyterLab
m_store_list_1 = SelectMultiple(
    options=store_list,
    discription="store_names"
)


@interact(store_names=m_store_list_1)
def graph_by_multi(store_names):
    fig = plt.figure(figsize=(17, 4))
    plt.subplots_adjust(wspace=0.25, hspace=0.6)

    n = len(store_names)
    i = 0
    for trg in store_names:
        pick_data = order_data[
            (order_data["store_name"] == trg)
            & (order_data["status"].isin([1, 2]))
        ]

        temp = pick_data[["order_accept_date",
                          "total_amount",
                          "store_name"]].copy()
        temp.loc[:, "order_accept_date"] = pd.to_datetime(temp["order_accept_date"])
        temp.set_index("order_accept_date", inplace=True)

        i += 1

        # y軸を共通化して比較しやすくしてみた。
        if i == 1:
            ax = fig.add_subplot(1, n, i)
            _ax = ax
        else:
            ax = fig.add_subplot(1, n, i, sharey=_ax)
        ax.plot(temp.resample("D").sum())
        ax.set_title(trg)
        ax.grid()


出力

ノック23: スライドバーを用いてオーダー件数を調べてみよう

スライドバーで閾値を指定して、閾値未満または閾値より大きいオーダー件数の店舗名を表示します。

JupyterLab
slider_0 = IntSlider(
    value=1100, 
    min=1000,
    max=2000,
    step=100,
    description="件数:"
)


@interact(th=slider_0)
def store_lower(th):
    temp = order_data.groupby("store_name")
    print(temp.size()[temp.size() < th])


出力

JupyterLab
slider_1 = IntSlider(
    value=1600,
    min=1000,
    max=2000,
    step=100,
    description="件数:"
)

@interact(th=slider_1)
def store_upper(th):
    temp = order_data.groupby("store_name")
    print(temp.size()[temp.size() > th])


出力

ノック24: トグルボタンで地域データを抽出しよう

トグルボタンで選択した地域で絞り込んだ DataFrame の表示

JupyterLab
area_list = m_area["wide_area"].unique()

toggle_buttons_0 = ToggleButtons(
    options=area_list
)


@interact(area=toggle_buttons_0)
def order_by_area(area):
    pick_data = order_data.loc[
        (order_data["wide_area"] == area)
        & (order_data["status"].isin([1, 2]))
    ]
    return pick_data.head()


出力

トグルボタンで選択した地域で絞り込んだ折れ線グラフの表示

JupyterLab
toggle_buttons_1 = ToggleButtons(
    options=area_list
)


@interact(area=toggle_buttons_1)
def graph_by_area(area):
    pick_data = pick_data = order_data.loc[
        (order_data["wide_area"] == area)
        & (order_data["status"].isin([1, 2]))
    ]

    temp = pick_data[["order_accept_date", "total_amount"]].copy()
    temp.loc[:, "order_accept_date"] = pd.to_datetime(temp["order_accept_date"])
    temp.set_index("order_accept_date", inplace=True)
    temp.resample("D").sum().plot(grid=True)


出力

ノック25: 日付を指定してデータを抽出してみよう

カレンダーのようなUIから日付を指定するための DatePicker を使って日付指定を行います。

JupyterLab
order_data.loc[:, "order_date"] = \
    pd.to_datetime(order_data["order_accept_date"]).dt.date

date_picker_0 = DatePicker(
    value=datetime.datetime(2020, 4, 1)
)


@interact(date=date_picker_0)
def order_by_date(date):
    pick_data = order_data.loc[
        (order_data["order_date"] == date)
        & (order_data["status"].isin([1, 2]))
    ]
    print(f"{len(pick_data)=}\n")
    return pick_data.head()


出力

JupyterLab
date_picker_1 = DatePicker(
    value=datetime.date(2020, 4, 1)
)
date_picker_2 = DatePicker(
    value=datetime.date(2020, 4, 30)
)


@interact(start=date_picker_1, end=date_picker_2)
def order_between_date(start, end):
    pick_data = order_data.loc[
        (order_data["order_date"] >= start)
        & (order_data["order_date"] <= end)
        & (order_data["status"].isin([1, 2]))
    ]
    print(f"{len(pick_data)=}\n")
    return pick_data.head()


出力

まとめ

ひととおり書籍3章で扱われているウィジェットをJupyterLabでも使えるようにできました。書籍本文のよりもコード記述量が少なくなり、シンプルになりますね!やはりJupyterLabはイケてる👍

Discussion