1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【完全版】Dash by Plotly チュートリアル

Posted at

はじめに

Dashは、Pythonでインタラクティブなウェブアプリケーションを構築するための強力なフレームワークです。
データ可視化やダッシュボードの作成に最適であり、Plotlyのグラフライブラリとシームレスに統合されています。

本ガイドでは、初心者から中級者までを対象に、Dashの基本から高度な機能までを網羅的に解説します。
実践的なプロジェクト例やベストプラクティスも紹介するので、実際のビジネスシナリオに応用可能なスキルを身につけることができます。

Dashとは

Dashは、以下の特徴を持つPythonフレームワークです:

  • シンプルな構文:HTMLやJavaScriptの知識がなくてもウェブアプリが作成可能。
  • インタラクティブなUI:スライダー、ドロップダウン、グラフなど多彩なコンポーネントをサポート。
  • Plotlyとの統合:高品質なグラフ作成が容易。
  • 拡張性:Dashの基本コンポーネントに加え、dash-bootstrap-componentsなどのライブラリを利用可能。
  • リアクティブな更新:ユーザーの操作に応じてリアルタイムでアプリケーションを更新。

Dashは、データサイエンティストやアナリストが迅速にダッシュボードやデータ可視化ツールを構築するために設計されています。


環境の準備

必要なもの

  • Python:バージョン3.7以上
  • pip:Pythonパッケージ管理ツール
  • 仮想環境ツール(例:venv, conda)

インストール手順

  1. Pythonのインストール

    公式サイトからPythonをインストールします:Python Downloads

  2. 仮想環境の作成(推奨)

    プロジェクトごとに依存関係を管理するために仮想環境を作成します。

    python -m venv dash_env
    

    仮想環境をアクティブにします:

    • Windows:

      dash_env\Scripts\activate
      
    • macOS/Linux:

      source dash_env/bin/activate
      
  3. 必要なパッケージのインストール

    pip install dash dash-bootstrap-components pandas plotly gunicorn
    
    • dash: Dashフレームワーク本体
    • dash-bootstrap-components: Bootstrapを用いたスタイリングコンポーネント
    • pandas: データ操作用ライブラリ
    • plotly: インタラクティブなグラフ作成ライブラリ
    • gunicorn: WSGI HTTPサーバー(デプロイ時に使用)
  4. 依存関係の確認

    インストールされたパッケージを確認します:

    pip freeze
    

    必要に応じてrequirements.txtを作成します:

    pip freeze > requirements.txt
    

基本的なDashアプリの作成

まずは、シンプルな「Hello Dash」アプリを作成してDashの基本を理解しましょう。

1. プロジェクトディレクトリの作成

mkdir dash_tutorial
cd dash_tutorial

2. アプリケーションファイルの作成

app.pyという名前で新しいPythonファイルを作成します。

# app.py

import dash
from dash import html

# Dashアプリの初期化
app = dash.Dash(__name__)

# レイアウトの定義
app.layout = html.Div(children=[
    html.H1(children='Hello Dash'),

    html.Div(children='''
        Dash: A web application framework for Python.
    '''),
])

# アプリの実行
if __name__ == '__main__':
    app.run_server(debug=True)

image.png

3. アプリの実行

ターミナルで以下のコマンドを実行します:

python app.py

ブラウザで http://127.0.0.1:8050/ にアクセスすると、「Hello Dash」と表示されたページが見えます。

4. コード解説

  • インポート: dashhtmlコンポーネントをインポートします。
  • アプリの初期化: dash.Dash(__name__)でアプリケーションを初期化します。
  • レイアウトの定義: app.layoutにHTMLコンポーネントを配置し、アプリの構造を定義します。
  • アプリの実行: app.run_server(debug=True)で開発用サーバーを起動します。debug=Trueにより、コードの変更が自動で反映され、エラーメッセージが詳細に表示されます。

レイアウトの構築

DashアプリのUIは、レイアウトによって定義されます。レイアウトは、Dashコンポーネントを階層的に配置することで構成されます。ここでは、基本的なコンポーネントとレイアウトの作成方法を詳しく見ていきます。

基本的なコンポーネント

  • html.Div: HTMLの<div>タグに対応。コンテンツをグループ化するために使用。
  • html.H1, html.H2, ...: 見出しタグ。テキストの見出しを作成。
  • html.P: 段落タグ。テキストの段落を作成。
  • dcc.Graph: グラフ表示用コンポーネント。Plotlyのグラフを表示。

レイアウトの例

以下は、テキストとグラフを含むレイアウトの例です。

# app.py

import dash
from dash import html, dcc
import plotly.express as px
import pandas as pd

# サンプルデータの作成
df = pd.DataFrame({
    "Fruit": ["Apples", "Oranges", "Bananas", "Apples", "Oranges", "Bananas"],
    "Amount": [4, 1, 2, 2, 4, 5],
    "City": ["SF", "SF", "SF", "Montreal", "Montreal", "Montreal"]
})

fig = px.bar(df, x="Fruit", y="Amount", color="City", barmode="group")

app = dash.Dash(__name__)

app.layout = html.Div(children=[
    html.H1(children='Dashによるデータ可視化'),

    html.Div(children='''
        Dash: Pythonでインタラクティブなウェブアプリを作成。
    '''),

    dcc.Graph(
        id='example-graph',
        figure=fig
    )
])

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

image.png

コード解説

  • データの準備: pandasを使用してサンプルデータフレームを作成します。
  • グラフの作成: plotly.expressを用いて棒グラフを作成します。
  • レイアウトの定義: html.H1でタイトルを、html.Divで説明文を、dcc.Graphでグラフを表示します。

コールバックの設定

Dashの強力な機能の一つがコールバックです。
コールバックを使用することで、ユーザーの操作に応じてアプリケーションの動的な変更が可能になります。
ここでは、基本的なコールバックの構造と具体的な例を詳しく解説します。

コールバックの基本構造

コールバックは、ユーザーの入力に応じて出力を更新するための関数です。以下が基本的な構造です。

from dash.dependencies import Input, Output

@app.callback(
    Output('output-component', 'property'),
    [Input('input-component', 'property')]
)
def update_output(input_value):
    # 処理
    return new_value

具体例:ドロップダウンによるグラフの更新

以下は、ドロップダウンメニューで都市を選択し、選択した都市のデータのみを表示する例です。

# app.py

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

# サンプルデータの作成
df = pd.DataFrame({
    "Fruit": ["Apples", "Oranges", "Bananas", "Apples", "Oranges", "Bananas"],
    "Amount": [4, 1, 2, 2, 4, 5],
    "City": ["SF", "SF", "SF", "Montreal", "Montreal", "Montreal"]
})

app = dash.Dash(__name__)

app.layout = html.Div([
    html.H1("ドロップダウンで都市を選択"),

    dcc.Dropdown(
        id='city-dropdown',
        options=[
            {'label': 'San Francisco', 'value': 'SF'},
            {'label': 'Montreal', 'value': 'Montreal'}
        ],
        value='SF'  # デフォルト値
    ),

    dcc.Graph(id='bar-graph')
])

@app.callback(
    Output('bar-graph', 'figure'),
    [Input('city-dropdown', 'value')]
)
def update_graph(selected_city):
    filtered_df = df[df['City'] == selected_city]
    fig = px.bar(filtered_df, x="Fruit", y="Amount", color="Fruit", title=f"{selected_city}の果物の量")
    return fig

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

image.png

コード解説

  • ドロップダウンの作成: dcc.Dropdownコンポーネントを使用し、選択肢として「San Francisco」と「Montreal」を提供します。
  • コールバックの定義: @app.callbackデコレーターを使用して、ドロップダウンの値が変更されたときにグラフを更新する関数を定義します。
  • グラフの更新: 選択された都市に基づいてデータをフィルタリングし、新しいグラフを生成します。

コールバックのベストプラクティス

  • 関数名の明確化: コールバック関数の名前は、その機能を明確に表すものにする。
  • ドキュメンテーション: 関数内で行っている処理をコメントやドキュメンテーションで明確に説明する。
  • エラーハンドリング: 入力値が予期しない場合に備えて、エラーハンドリングを実装する。
@app.callback(
    Output('bar-graph', 'figure'),
    [Input('city-dropdown', 'value')]
)
def update_graph(selected_city):
    """
    ドロップダウンで選択された都市に基づいてグラフを更新します。

    Parameters:
    selected_city (str): ユーザーが選択した都市の値

    Returns:
    plotly.graph_objs._figure.Figure: 更新された棒グラフの図
    """
    if selected_city is None:
        # デフォルトの図を返す
        return px.bar(title="都市が選択されていません")
    
    filtered_df = df[df['City'] == selected_city]
    if filtered_df.empty:
        # データが存在しない場合の処理
        return px.bar(title=f"{selected_city}のデータが存在しません")
    
    fig = px.bar(filtered_df, x="Fruit", y="Amount", color="Fruit", title=f"{selected_city}の果物の量")
    return fig

高度なコールバックの利用

基本的なコールバックに加え、Dashでは複数の入力や出力、状態管理、メモ化などを利用した高度なコールバックが可能です。これにより、複雑なインタラクションやデータ処理を効率的に実現できます。

複数の入力と出力

一つのコールバック関数で複数の入力を受け取り、複数の出力を返すことができます。

from dash.dependencies import Input, Output

@app.callback(
    [Output('graph-1', 'figure'),
     Output('graph-2', 'figure')],
    [Input('dropdown-1', 'value'),
     Input('dropdown-2', 'value')]
)
def update_graphs(value1, value2):
    # それぞれのグラフを作成
    fig1 = create_figure1(value1)
    fig2 = create_figure2(value2)
    return fig1, fig2

状態管理

Stateを使用することで、コールバック関数内で追加のパラメータを参照できます。これは、ユーザーの入力をトリガーにしつつ、他のコンポーネントの状態を参照する場合に有用です。

from dash.dependencies import Input, Output, State

@app.callback(
    Output('output-div', 'children'),
    [Input('submit-button', 'n_clicks')],
    [State('input-text', 'value')]
)
def update_output(n_clicks, value):
    if n_clicks is None:
        return "ボタンがクリックされていません。"
    return f"入力された値: {value}"

メモ化によるパフォーマンス最適化

大量のデータ処理や重い計算を行うコールバック関数には、メモ化を適用することでパフォーマンスを向上させることができます。Dashは内部でキャッシュを利用しており、同じ入力に対して再計算を避けることができます。

from dash.dependencies import Input, Output
from functools import lru_cache

@app.callback(
    Output('heavy-graph', 'figure'),
    [Input('input-slider', 'value')]
)
@lru_cache(maxsize=32)
def update_heavy_graph(value):
    # 重いデータ処理
    processed_data = heavy_data_processing(value)
    fig = create_figure(processed_data)
    return fig

条件付きコールバック

特定の条件下でのみコールバックを実行する方法です。これにより、不要な処理を避けることができます。

@app.callback(
    Output('conditional-output', 'children'),
    [Input('toggle-switch', 'value')],
    [State('input-field', 'value')]
)
def conditional_callback(toggle, input_value):
    if toggle:
        return f"トグルがオンです。入力値: {input_value}"
    else:
        return "トグルがオフです。"

スタイリングとデザイン

Dashアプリケーションの外観を向上させるためには、CSSやスタイリングコンポーネントを活用することが重要です。ここでは、基本的なスタイリング方法とdash-bootstrap-componentsの利用方法を紹介します。

1. 基本的なスタイリング

Dashでは、style属性を使用して各コンポーネントに直接スタイルを適用できます。

html.Div(
    children='スタイリングされたテキスト',
    style={
        'color': 'blue',
        'fontSize': 24,
        'textAlign': 'center',
        'padding': '20px'
    }
)

2. 外部CSSファイルの利用

プロジェクトにassetsフォルダを作成し、その中にCSSファイルを配置することで、全体的なスタイリングを一括で適用できます。

  • ディレクトリ構造

    dash_tutorial/
    ├── app.py
    └── assets/
        └── style.css
    
  • style.cssの例

    body {
        background-color: #f9f9f9;
        font-family: Arial, sans-serif;
    }
    
    h1 {
        color: #333333;
        text-align: center;
    }
    
    .custom-div {
        border: 1px solid #dddddd;
        padding: 10px;
        border-radius: 5px;
        background-color: #ffffff;
    }
    
  • app.pyでの利用

    app.layout = html.Div([
        html.H1("スタイリングされたDashアプリ"),
        html.Div(
            "これはカスタムスタイルが適用されたコンテンツです。",
            className='custom-div'
        )
    ])
    

3. Dash Bootstrap Componentsの利用

dash-bootstrap-componentsを使用することで、Bootstrapのスタイルを簡単に適用できます。これにより、レスポンシブで洗練されたデザインを実現できます。

  • インストール

    pip install dash-bootstrap-components
    
  • 基本的な利用方法

    import dash
    from dash import html
    import dash_bootstrap_components as dbc
    
    # Bootstrapテーマの適用
    app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
    
    app.layout = dbc.Container([
        dbc.Row([
            dbc.Col(html.H1("Bootstrapを使用したDashアプリ"), className="mb-2")
        ]),
        dbc.Row([
            dbc.Col(dbc.Button("クリック me", color="primary"), width="auto")
        ])
    ], fluid=True)
    
    if __name__ == '__main__':
        app.run_server(debug=True)
    
  • コンポーネントのスタイリング

    dbc.Button, dbc.Row, dbc.Colなどのコンポーネントを使用して、グリッドレイアウトやボタンのスタイルを簡単に設定できます。

4. カスタムCSSフレームワークの利用

必要に応じて、他のCSSフレームワーク(例:Bulma, Materialize)を利用することも可能です。外部のCSSファイルをexternal_stylesheetsに追加することで統合できます。

app = dash.Dash(__name__, external_stylesheets=[
    'https://cdnjs.cloudflare.com/ajax/libs/bulma/0.9.3/css/bulma.min.css'
])

インタラクティブなコンポーネントの追加

Dashには多くのインタラクティブなコンポーネントが用意されています。ユーザーの入力や操作に応じてアプリケーションを動的に変化させるために、これらのコンポーネントを効果的に活用しましょう。

主なインタラクティブコンポーネント

  1. スライダー(Slider)

    数値の範囲を選択できるコンポーネント。

    dcc.Slider(
        id='my-slider',
        min=0,
        max=20,
        step=1,
        value=10,
        marks={i: str(i) for i in range(21)}
    )
    
  2. テキスト入力(Input)

    ユーザーがテキストを入力できるコンポーネント。

    dcc.Input(
        id='my-input',
        type='text',
        value='初期値'
    )
    
  3. チェックボックス(Checklist)

    複数の選択肢から選択できるコンポーネント。

    dcc.Checklist(
        id='my-checklist',
        options=[
            {'label': 'オプション1', 'value': 'OPT1'},
            {'label': 'オプション2', 'value': 'OPT2'}
        ],
        value=['OPT1']
    )
    
  4. ラジオボタン(RadioItems)

    複数の選択肢から一つを選択できるコンポーネント。

    dcc.RadioItems(
        id='my-radio',
        options=[
            {'label': 'Red', 'value': 'R'},
            {'label': 'Green', 'value': 'G'},
            {'label': 'Blue', 'value': 'B'}
        ],
        value='R'
    )
    
  5. 日付ピッカー(DatePicker)

    ユーザーが日付を選択できるコンポーネント。

    dcc.DatePickerSingle(
        id='my-date-picker',
        date='2024-01-01'
    )
    

具体例:スライダーを使用した動的グラフの更新

以下は、スライダーでデータポイントの数を選択し、グラフを更新する例です。

# app.py

import dash
from dash import html, dcc
from dash.dependencies import Input, Output
import plotly.express as px
import pandas as pd
import numpy as np

app = dash.Dash(__name__)

# サンプルデータの作成
np.random.seed(0)
x = np.linspace(0, 10, 100)
y = np.sin(x) + np.random.randn(100) * 0.5
df = pd.DataFrame({'x': x, 'y': y})

app.layout = html.Div([
    html.H1("スライダーでデータポイントを選択"),

    dcc.Slider(
        id='point-slider',
        min=10,
        max=100,
        step=10,
        value=50,
        marks={i: str(i) for i in range(10, 101, 10)}
    ),

    dcc.Graph(id='scatter-plot')
])

@app.callback(
    Output('scatter-plot', 'figure'),
    [Input('point-slider', 'value')]
)
def update_scatter(n_points):
    """
    スライダーで選択したデータポイント数に基づいて散布図を更新します。

    Parameters:
    n_points (int): 選択されたデータポイント数

    Returns:
    plotly.graph_objs._figure.Figure: 更新された散布図の図
    """
    filtered_df = df.head(n_points)
    fig = px.scatter(filtered_df, x='x', y='y', title=f'最初の {n_points} データポイント')
    return fig

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

image.png

コード解説

  • スライダーの作成: dcc.Sliderを使用して、10から100までの範囲でデータポイント数を選択できるスライダーを作成します。
  • コールバックの定義: スライダーの値が変更されると、update_scatter関数が呼び出され、選択されたデータポイント数に基づいて散布図を更新します。
  • グラフの更新: 選択された数のデータポイントをフィルタリングし、新しい散布図を生成します。

追加例:テキスト入力による動的更新

ユーザーがテキストを入力すると、それに基づいてメッセージを更新する例です。

import dash
from dash import html, dcc
from dash.dependencies import Input, Output

app = dash.Dash(__name__)

app.layout = html.Div([
    html.H1("テキスト入力による動的更新"),

    dcc.Input(
        id='text-input',
        type='text',
        value='こんにちは',
        style={'width': '50%'}
    ),

    html.Div(id='output-div', style={'marginTop': '20px', 'fontSize': '24px'})
])

@app.callback(
    Output('output-div', 'children'),
    [Input('text-input', 'value')]
)
def update_output(input_value):
    """
    テキスト入力に基づいて出力を更新します。

    Parameters:
    input_value (str): ユーザーが入力したテキスト

    Returns:
    str: 更新されたメッセージ
    """
    return f"入力されたテキスト: {input_value}"

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

コード解説

  • テキスト入力の作成: dcc.Inputを使用して、ユーザーがテキストを入力できるフィールドを作成します。
  • コールバックの定義: テキスト入力が変更されると、update_output関数が呼び出され、入力されたテキストを表示します。

実践的なプロジェクト例

実際のビジネスシナリオに基づいたプロジェクト例を通じて、Dashの知識を実践的に応用しましょう。ここでは、販売データのダッシュボードとリアルタイムデータモニタリングアプリの例を紹介します。

プロジェクト1: 販売データダッシュボード

概要

  • 目的: 月別の販売データを可視化し、製品ごとの売上を分析する。
  • 機能:
    • 月の選択(ドロップダウン)
    • 製品カテゴリーのフィルタリング(チェックボックス)
    • 売上の棒グラフと折れ線グラフの表示

ステップ1: データの準備

import pandas as pd

# サンプル販売データの作成
data = {
    'Month': ['January', 'February', 'March', 'April', 'May', 'June'],
    'Product A': [100, 150, 200, 250, 300, 350],
    'Product B': [80, 120, 160, 200, 240, 280],
    'Product C': [60, 90, 120, 150, 180, 210]
}

df = pd.DataFrame(data)

ステップ2: レイアウトの作成

import dash
from dash import html, dcc
from dash.dependencies import Input, Output
import plotly.graph_objs as go

app = dash.Dash(__name__)

app.layout = html.Div([
    html.H1("販売データダッシュボード"),

    html.Div([
        html.Label("月を選択:"),
        dcc.Dropdown(
            id='month-dropdown',
            options=[{'label': month, 'value': month} for month in df['Month']],
            value='January',
            clearable=False
        ),
    ], style={'width': '48%', 'display': 'inline-block'}),

    html.Div([
        html.Label("製品カテゴリーを選択:"),
        dcc.Checklist(
            id='product-checklist',
            options=[
                {'label': 'Product A', 'value': 'Product A'},
                {'label': 'Product B', 'value': 'Product B'},
                {'label': 'Product C', 'value': 'Product C'}
            ],
            value=['Product A', 'Product B', 'Product C'],
            labelStyle={'display': 'inline-block'}
        ),
    ], style={'width': '48%', 'display': 'inline-block', 'float': 'right'}),

    dcc.Graph(id='sales-graph')
])

ステップ3: コールバックの実装

@app.callback(
    Output('sales-graph', 'figure'),
    [Input('month-dropdown', 'value'),
     Input('product-checklist', 'value')]
)
def update_graph(selected_month, selected_products):
    filtered_df = df[df['Month'] == selected_month]

    data = []
    for product in selected_products:
        data.append(go.Bar(
            x=[product],
            y=[filtered_df[product].values[0]],
            name=product
        ))

    layout = go.Layout(
        title=f"{selected_month}の販売データ",
        xaxis={'title': '製品'},
        yaxis={'title': '売上'},
        barmode='group'
    )

    return {'data': data, 'layout': layout}

ステップ4: アプリの実行

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

image.png

プロジェクト2: リアルタイムデータモニタリングアプリ

概要

  • 目的: センサーデータをリアルタイムでモニタリングし、異常値を検出する。
  • 機能:
    • データの自動更新(インターバルコンポーネント)
    • リアルタイムグラフの表示
    • 異常値のアラート表示

ステップ1: データの準備

import pandas as pd
import numpy as np
import datetime

# 初期データの作成
timestamps = [datetime.datetime.now() - datetime.timedelta(seconds=60 - i) for i in range(60)]
values = np.random.normal(loc=50, scale=5, size=60)
df = pd.DataFrame({'Time': timestamps, 'Value': values})

ステップ2: レイアウトの作成

import dash
from dash import html, dcc
from dash.dependencies import Input, Output
import plotly.graph_objs as go

app = dash.Dash(__name__)

app.layout = html.Div([
    html.H1("リアルタイムデータモニタリング"),

    dcc.Graph(id='live-graph', animate=True),

    dcc.Interval(
        id='graph-update',
        interval=1*1000,  # 1秒ごとに更新
        n_intervals=0
    ),

    html.Div(id='alert-div', style={'color': 'red', 'fontWeight': 'bold', 'fontSize': '24px'})
])

ステップ3: コールバックの実装

@app.callback(
    [Output('live-graph', 'figure'),
     Output('alert-div', 'children')],
    [Input('graph-update', 'n_intervals')]
)
def update_graph(n):
    global df
    # 新しいデータポイントの追加
    new_time = datetime.datetime.now()
    new_value = np.random.normal(loc=50, scale=5)
    df = df.append({'Time': new_time, 'Value': new_value}, ignore_index=True)
    df = df.tail(60)  # 最新60秒のデータを保持

    # グラフの作成
    data = go.Scatter(
        x=df['Time'],
        y=df['Value'],
        mode='lines+markers'
    )

    # 異常値の検出
    threshold = 60
    alert = ""
    if new_value > threshold:
        alert = f"異常値検出: {new_value:.2f} (閾値: {threshold}"

    return {'data': [data],
            'layout': go.Layout(
                xaxis=dict(range=[df['Time'].min(), df['Time'].max()]),
                yaxis=dict(range=[df['Value'].min()-10, df['Value'].max()+10]),
                title='リアルタイムセンサーデータ'
            )}, alert

ステップ4: アプリの実行

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

プロジェクトの拡張アイデア

  • データの保存: データベースにデータを保存し、後から分析できるようにする。
  • ユーザー通知: 異常値が検出された際にメールやSMSで通知を送信。
  • 多様なグラフの追加: ヒートマップや箱ひげ図など、異なるタイプのグラフを追加。
  • ユーザーインターフェースの改善: モーダルウィンドウやツールチップを追加して、ユーザー体験を向上。

まとめ

Dashは、Pythonを使用してインタラクティブなウェブアプリケーションやダッシュボードを簡単に作成できる強力なフレームワークです。
本ガイドでは、Dashの基本的な使い方から高度な機能、セキュリティ対策、デプロイメント方法までを包括的に解説しました。
さらに、実践的なプロジェクト例を通じて、実際のビジネスシナリオに応用可能なスキルを習得しました。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?