Dash応用編:第4回 データのフィルタリングと検索機能

Python
この記事は約17分で読めます。

こんにちは、JS2IIUです。急に秋らしくなってきました。今回もよろしくお願いします。

はじめに

第4回では、Dashアプリケーションでのデータのフィルタリングと検索機能の実装について解説します。これにより、ユーザーはデータを直感的に絞り込み、必要な情報を迅速に取得できるようになります。特に、大量データの分析や探索が求められるダッシュボードでは、データのフィルタリングと検索機能が不可欠です。

この記事では、DataTableコンポーネントやdcc.Dropdowndcc.Inputを用いてインタラクティブにデータを操作する方法を具体例を交えて紹介します。

DataTableを用いた基本的なフィルタリング

DashのDataTableコンポーネントは、テーブルデータを表示するための強力なツールで、内蔵されたフィルタリング機能を使ってデータを絞り込むことができます。

以下のコードは、単純なDataTableでフィルタリングを行う例です。それぞれの列で昇順降順ソート、文字列フィルタリングの機能が簡単に実装できています。

import dash
from dash import dash_table, html
import pandas as pd

# データフレームの作成
data = {
    "名前": ["Alice", "Bob", "Charlie", "David", "Eve"],
    "年齢": [25, 30, 35, 40, 22],
    "職業": ["エンジニア", "デザイナー", "データサイエンティスト", "マネージャー", "マーケター"]
}
df = pd.DataFrame(data)

app = dash.Dash(__name__)

# レイアウト
app.layout = html.Div([
    html.H1('フィルタリング機能付きDataTable'),
    dash_table.DataTable(
        id='table',
        columns=[{"name": i, "id": i} for i in df.columns],
        data=df.to_dict('records'),
        filter_action="native",  # フィルタリングを有効にするオプション
        sort_action="native",    # ソートを有効にするオプション
        page_size=5              # ページサイズを設定
    )
])

if __name__ == '__main__':
    app.run_server(debug=True)

このコードでは、filter_action="native"を設定することで、ユーザーがテーブルの各列をフィルタリングできるようになります。DataTableはDashの標準機能で、簡単にフィルタリングとソートが行えるように設計されています。

複数の条件を用いた高度なフィルタリング

次に、ユーザーが複数の条件を指定してフィルタリングできる例を紹介します。この例では、dcc.Dropdownコンポーネントを使い、職業や年齢の範囲でデータを絞り込むことが可能です。

import dash
from dash import dcc, html
from dash.dependencies import Input, Output
import pandas as pd

# データフレームの作成
data = {
    "名前": ["Alice", "Bob", "Charlie", "David", "Eve"],
    "年齢": [25, 30, 35, 40, 22],
    "職業": ["エンジニア", "デザイナー", "データサイエンティスト", "マネージャー", "マーケター"]
}
df = pd.DataFrame(data)

app = dash.Dash(__name__)

app.layout = html.Div([
    html.H1('複数条件フィルタリング'),
    dcc.Dropdown(
        id='職業フィルター',
        options=[{'label': i, 'value': i} for i in df['職業'].unique()],
        placeholder='職業を選択'
    ),
    dcc.RangeSlider(
        id='年齢フィルター',
        min=20, max=50, step=1,
        marks={i: str(i) for i in range(20, 51, 5)},
        value=[20, 50]
    ),
    html.Div(id='フィルタリング結果')
])

@app.callback(
    Output('フィルタリング結果', 'children'),
    [Input('職業フィルター', 'value'),
     Input('年齢フィルター', 'value')]
)
def update_table(selected_job, age_range):
    filtered_df = df.copy()

    if selected_job:
        filtered_df = filtered_df[filtered_df['職業'] == selected_job]

    filtered_df = filtered_df[(filtered_df['年齢'] >= age_range[0]) & (filtered_df['年齢'] <= age_range[1])]

    # フィルタリング結果をHTMLのリストに変換して返す
    if filtered_df.empty:
        return "該当する結果がありません。"
    else:
        return [
            html.Div([
                html.P(f"名前: {row['名前']}, 年齢: {row['年齢']}, 職業: {row['職業']}")
            ]) for index, row in filtered_df.iterrows()
        ]

if __name__ == '__main__':
    app.run_server(debug=True)

この例では、ユーザーが職業を選択し、年齢の範囲を指定することで、該当するデータを絞り込みます。dcc.Dropdownを用いて職業のフィルタリングを行い、dcc.RangeSliderで年齢の範囲を選択することができ、コールバック関数内で条件に基づいてデータが動的にフィルタリングされます。

1. データフレームの作成

data = {
    "名前": ["Alice", "Bob", "Charlie", "David", "Eve"],
    "年齢": [25, 30, 35, 40, 22],
    "職業": ["エンジニア", "デザイナー", "データサイエンティスト", "マネージャー", "マーケター"]
}
df = pd.DataFrame(data)
  • ここでは、サンプルデータを用いてPandasデータフレームを作成しています。データには「名前」「年齢」「職業」の3つの列があり、5人の人物に関する情報が含まれています。
  • このデータを後でフィルタリングし、ユーザーが指定した条件に基づいて結果を表示します。

2. Dashアプリの設定とレイアウト

app = dash.Dash(__name__)

app.layout = html.Div([
    html.H1('複数条件フィルタリング'),
    dcc.Dropdown(
        id='職業フィルター',
        options=[{'label': i, 'value': i} for i in df['職業'].unique()],
        placeholder='職業を選択'
    ),
    dcc.RangeSlider(
        id='年齢フィルター',
        min=20, max=50, step=1,
        marks={i: str(i) for i in range(20, 51, 5)},
        value=[20, 50]
    ),
    html.Div(id='フィルタリング結果')
])
  • app = dash.Dash(__name__)でDashアプリケーションを作成します。
  • アプリのレイアウトはhtml.Divで定義され、その中に3つの主要なコンポーネントが含まれています:
  1. html.H1: タイトル部分。「複数条件フィルタリング」という見出しを表示します。
  2. dcc.Dropdown: 職業フィルターとして使用されます。options引数で、データフレームの「職業」列に含まれる一意の職業を取得し、ユーザーが職業を選択できるようにしています。placeholderには選択前のメッセージ「職業を選択」が表示されます。
  3. dcc.RangeSlider: 年齢範囲フィルター。ユーザーは20歳から50歳までの年齢範囲を指定できます。marksはスライダーに表示する目盛りを定義し、value=[20, 50]で初期値として20~50の範囲を設定しています。
  4. html.Div(id='フィルタリング結果'): フィルタリング結果を表示する領域です。このDivは後ほどコールバックで更新されます。

3. コールバック関数

@app.callback(
    Output('フィルタリング結果', 'children'),
    [Input('職業フィルター', 'value'),
     Input('年齢フィルター', 'value')]
)
def update_table(selected_job, age_range):
    filtered_df = df.copy()

    if selected_job:
        filtered_df = filtered_df[filtered_df['職業'] == selected_job]

    filtered_df = filtered_df[(filtered_df['年齢'] >= age_range[0]) & (filtered_df['年齢'] <= age_range[1])]

    if filtered_df.empty:
        return "該当する結果がありません。"
    else:
        return [
            html.Div([
                html.P(f"名前: {row['名前']}, 年齢: {row['年齢']}, 職業: {row['職業']}")
            ]) for index, row in filtered_df.iterrows()
        ]
  • @app.callback: Dashのコールバックデコレーターです。この関数は、特定の入力(Input)の値が変更されるたびに実行され、出力(Output)が更新されます。
  • Output('フィルタリング結果', 'children'): フィルタリング結果を表示するhtml.Divchildrenプロパティを更新します。
  • Input('職業フィルター', 'value')Input('年齢フィルター', 'value'): ドロップダウンで選択された職業と、スライダーで指定された年齢範囲がコールバック関数の引数selected_jobage_rangeに渡されます。
  • フィルタリング処理:
  1. 最初に、filtered_df = df.copy()で元のデータフレームdfのコピーを作成します。
  2. 職業フィルターが選択されている場合、filtered_df = filtered_df[filtered_df['職業'] == selected_job]で、選択された職業に一致する行だけを残します。
  3. filtered_df = filtered_df[(filtered_df['年齢'] >= age_range[0]) & (filtered_df['年齢'] <= age_range[1])]で、スライダーで指定された年齢範囲内の行をフィルタリングします。
  • フィルタリング結果の表示:
  • フィルタリング後、結果が空であれば「該当する結果がありません。」という文字列を返します。
  • もし結果があれば、データフレームの各行をhtml.Divhtml.PでHTML形式に変換し、名前、年齢、職業を表示します。このリストがchildrenに渡され、結果として表示されます。

4. アプリの実行

if __name__ == '__main__':
    app.run_server(debug=True)
  • この部分では、アプリケーションが直接実行された場合に、Dashサーバーが起動し、Webブラウザでアプリケーションを確認できるようにします。
  • debug=Trueはデバッグモードを有効にし、アプリケーションの変更が自動的にリロードされるようにします。

まとめ

このコードは、Dashを使って複数条件でフィルタリングされたデータをリアルタイムで表示するシンプルなWebアプリケーションです。職業フィルターと年齢範囲フィルターに基づいて、選択した条件に一致する人物の情報が動的に表示されます。

検索バーの実装

データが多い場合、ユーザーが簡単に特定のデータを見つけるために検索バーを実装することも重要です。次に、dcc.Inputコンポーネントを使ったテキストベースの検索機能を紹介します。

検索窓に文字を入力すると即座にフィルタリングされたデータのみが表示されます。

import dash
from dash import dcc, html
from dash.dependencies import Input, Output
import pandas as pd

# データフレームの作成
data = {
    "名前": ["Alice", "Bob", "Charlie", "David", "Eve"],
    "年齢": [25, 30, 35, 40, 22],
    "職業": ["エンジニア", "デザイナー", "データサイエンティスト", "マネージャー", "マーケター"]
}
df = pd.DataFrame(data)

app = dash.Dash(__name__)

app.layout = html.Div([
    html.H1('検索バーでフィルタリング'),
    dcc.Input(id='名前検索', type='text', placeholder='名前を検索'),
    html.Div(id='検索結果')
])

@app.callback(
    Output('検索結果', 'children'),
    [Input('名前検索', 'value')]
)
def update_output(search_value):
    if search_value:
        filtered_df = df[df['名前'].str.contains(search_value, case=False)]
    else:
        filtered_df = df

    # フィルタされたデータをHTMLの段落に変換して返す
    if filtered_df.empty:
        return "該当する結果がありません。"
    else:
        return [
            html.P(f"名前: {row['名前']}, 年齢: {row['年齢']}, 職業: {row['職業']}")
            for _, row in filtered_df.iterrows()
        ]

if __name__ == '__main__':
    app.run_server(debug=True)

このコードでは、dcc.Inputを使ってユーザーが入力したテキストに基づいて、名前に一致するデータを動的にフィルタリングします。str.contains()メソッドを使って、部分一致検索を実現しています。

フィルタリングと検索の組み合わせ

次に、フィルタリングと検索機能を組み合わせた例を紹介します。ここでは、複数の条件(職業、年齢、名前)に基づいてデータを絞り込む高度な実装を行います。これにより、ユーザーは複数のフィルタリング条件と検索機能を組み合わせて、特定のデータを素早く探し出すことができます。

以下のコードでは、dcc.Dropdownによる職業のフィルタリング、dcc.RangeSliderによる年齢範囲の絞り込み、dcc.Inputを使った名前検索が全て連動します。

import dash
from dash import dcc, html
from dash.dependencies import Input, Output
import pandas as pd

# データフレームの作成
data = {
    "名前": ["Alice", "Bob", "Charlie", "David", "Eve"],
    "年齢": [25, 30, 35, 40, 22],
    "職業": ["エンジニア", "デザイナー", "データサイエンティスト", "マネージャー", "マーケター"]
}
df = pd.DataFrame(data)

app = dash.Dash(__name__)

app.layout = html.Div([
    html.H1('フィルタリングと検索の組み合わせ'),
    dcc.Dropdown(
        id='職業フィルター',
        options=[{'label': i, 'value': i} for i in df['職業'].unique()],
        placeholder='職業を選択'
    ),
    dcc.RangeSlider(
        id='年齢フィルター',
        min=20, max=50, step=1,
        marks={i: str(i) for i in range(20, 51, 5)},
        value=[20, 50]
    ),
    dcc.Input(id='名前検索', type='text', placeholder='名前を検索'),
    html.Div(id='結果')
])

@app.callback(
    Output('結果', 'children'),
    [Input('職業フィルター', 'value'),
     Input('年齢フィルター', 'value'),
     Input('名前検索', 'value')]
)
def update_table(selected_job, age_range, search_value):
    # データフレームをコピー
    filtered_df = df.copy()

    # 職業のフィルタリング
    if selected_job:
        filtered_df = filtered_df[filtered_df['職業'] == selected_job]

    # 年齢範囲でのフィルタリング
    filtered_df = filtered_df[(filtered_df['年齢'] >= age_range[0]) & (filtered_df['年齢'] <= age_range[1])]

    # 名前の部分一致検索
    if search_value:
        filtered_df = filtered_df[filtered_df['名前'].str.contains(search_value, case=False)]

    # 結果の表示
    return html.Ul([html.Li(f"{row['名前']} - {row['年齢']} - {row['職業']}") for _, row in filtered_df.iterrows()])

if __name__ == '__main__':
    app.run_server(debug=True)

コード解説

フィルタリングの流れ

  • dcc.Dropdownで職業を選択し、該当する行を絞り込みます。
  • dcc.RangeSliderで年齢の範囲を指定し、選択された年齢範囲に該当するデータをフィルタリングします。
  • dcc.Inputで名前を入力し、部分一致する名前を検索します。

Pandasの操作

  • filtered_df = df.copy()でデータフレームのコピーを作成し、オリジナルのデータを保護しながらフィルタリング処理を行います。
  • フィルタリングはPandasの条件式を用いて行われ、複数の条件を組み合わせてデータを効率よく絞り込みます。
  • 名前検索には、str.contains()メソッドを使用し、大文字小文字の区別を無効にしています(case=False)。

結果の表示

  • 最終的なフィルタリング結果をリスト形式で表示しています。html.Ulタグを使って、各行のデータを順にリストアイテムとしてレンダリングしています。

まとめ

本記事では、Dashを用いて複数条件のフィルタリングと検索機能を組み合わせた高度なインタラクティブダッシュボードを実装しました。フィルタリング条件や検索バーの利用は、大量データを扱うダッシュボードにおいて、ユーザーに直感的で使いやすいインターフェースを提供するための重要な要素です。

次回は、外部データソースを使用してリアルタイムでデータを取り込み、動的に更新されるダッシュボードの実装方法について解説します。

参考文献


最後まで読んでいただきありがとうございました。

Dash関連記事まとめ

DashはJavaScriptライブラリであるReactの上に構築されたPythonフレームワークであるが、DashはRでも動作し、最近ではJuliaもサポートしている。
Wikipedia – Plotly/Dash から引用、翻訳

コメント

タイトルとURLをコピーしました