Skip to content

グラフ表示(plotly)

plotly を使って、 Web アプリケーションの機能としてインタラクティブなグラフ表示を実装する。


グラフの元データ

グラフの元になるデータは xy.txtxy_negative.txt の 2 つ。 1 行目に x_value y_value というヘッダーがあって、 2 行目以降がスペース区切りの数値。

xy.txt
x_value y_value
1.0 10.5
2.0 15.3
3.0 20.1
4.0 18.7
5.0 22.0
6.0 25.4
7.0 30.2
8.0 28.8
9.0 35.0
10.0 40.5
11.0 38.2
12.0 42.1
13.0 45.3
14.0 50.0
15.0 48.7
16.0 55.2
17.0 60.1
18.0 58.4
19.0 62.7
20.0 70.0
21.0 65.8
22.0 72.3
23.0 68.7
24.0 75.2
25.0 80.1
26.0 78.4
27.0 85.0
28.0 90.3
29.0 88.7
30.0 95.2
31.0 100.1
32.0 98.4
33.0 105.0
34.0 110.5
35.0 108.2
36.0 115.3
37.0 120.0
38.0 118.7
39.0 125.4
40.0 130.2
41.0 128.8
42.0 135.0
43.0 140.5
44.0 138.2
45.0 145.3
46.0 150.0
47.0 148.7
48.0 155.2
49.0 160.1
50.0 158.4
51.0 165.0
52.0 170.3
53.0 168.7
54.0 175.2
55.0 180.1
56.0 178.4
57.0 185.0
58.0 190.3
59.0 188.7
60.0 195.2
61.0 200.1
62.0 198.4
63.0 205.0
64.0 210.5
65.0 208.2
66.0 215.3
67.0 220.0
68.0 218.7
69.0 225.4
70.0 230.2
71.0 228.8
72.0 235.0
73.0 240.5
74.0 238.2
75.0 245.3
76.0 250.0
77.0 248.7
78.0 255.2
79.0 260.1
80.0 258.4
81.0 265.0
82.0 270.3
83.0 268.7
84.0 275.2
85.0 280.1
86.0 278.4
87.0 285.0
88.0 290.3
89.0 288.7
90.0 295.2
91.0 300.1
92.0 298.4
93.0 305.0
94.0 310.5
95.0 308.2
96.0 315.3
97.0 320.0
98.0 318.7
99.0 325.4
100.0 330.2
xy_negative.txt
x_value y_value
-10.0 12.0
-9.8 11.6
-9.6 11.2
-9.4 10.8
-9.2 10.4
-9.0 10.0
-8.8 9.6
-8.6 9.2
-8.4 8.8
-8.2 8.4
-8.0 8.0
-7.8 7.6
-7.6 7.2
-7.4 6.8
-7.2 6.4
-7.0 6.0
-6.8 5.6
-6.6 5.2
-6.4 4.8
-6.2 4.4
-6.0 4.0
-5.8 3.6
-5.6 3.2
-5.4 2.8
-5.2 2.4
-5.0 2.0
-4.8 1.6
-4.6 1.2
-4.4 0.8
-4.2 0.4
-4.0 0.0
-3.8 -0.4
-3.6 -0.8
-3.4 -1.2
-3.2 -1.6
-3.0 -2.0
-2.8 -2.4
-2.6 -2.8
-2.4 -3.2
-2.2 -3.6
-2.0 -4.0
-1.8 -4.4
-1.6 -4.8
-1.4 -5.2
-1.2 -5.6
-1.0 -6.0
-0.8 -6.4
-0.6 -6.8
-0.4 -7.2
-0.2 -7.6
0.0 -8.0
0.2 -8.4
0.4 -8.8
0.6 -9.2
0.8 -9.6
1.0 -10.0
1.2 -10.4
1.4 -10.8
1.6 -11.2
1.8 -11.6
2.0 -12.0
2.2 -12.4
2.4 -12.8
2.6 -13.2
2.8 -13.6
3.0 -14.0
3.2 -14.4
3.4 -14.8
3.6 -15.2
3.8 -15.6
4.0 -16.0
4.2 -16.4
4.4 -16.8
4.6 -17.2
4.8 -17.6
5.0 -18.0
5.2 -18.4
5.4 -18.8
5.6 -19.2
5.8 -19.6
6.0 -20.0
6.2 -20.4
6.4 -20.8
6.6 -21.2
6.8 -21.6
7.0 -22.0
7.2 -22.4
7.4 -22.8
7.6 -23.2
7.8 -23.6
8.0 -24.0
8.2 -24.4
8.4 -24.8
8.6 -25.2
8.8 -25.6
9.0 -26.0
9.2 -26.4
9.4 -26.8
9.6 -27.2
9.8 -27.6
10.0 -28.0

エンドポイントの構成

エンドポイントは 2 つ。

  • / — グラフ用データファイルを選択して、 /graph へ POST する
  • /graph — 受け取ったデータから、インタラクティブなグラフを表示する

plotly の API

このアプリで使ってる plotly の API は下記の通り。


コード

main.py

from fastapi import FastAPI, Query, Form
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from starlette.requests import Request
import plotly.graph_objects as go

app = FastAPI()

templates = Jinja2Templates(directory="templates")


# xy.txt, xy_negative.txt のデータを読み込む関数; ファイル名(or ファイルパス)をリストに入れて渡す
def read_xy_data(file_path):
    x, y = [], []
    try:
        with open(file_path, "r") as file:
            for line in file:
                try:
                    x_val, y_val = map(float, line.split())
                    x.append(x_val)
                    y.append(y_val)
                except ValueError:
                    # 数値以外のデータは無視; ファイルの1行目にある、x_value y_valueっていう文字列を無視できる
                    continue
    except FileNotFoundError:
        print(f"File not found: {file_path}")
    return x, y


@app.get("/", response_class=HTMLResponse)
async def home(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})


@app.post("/graph", response_class=HTMLResponse)
async def graph(request: Request, files: list[str] = Form(...)):
    fig = go.Figure()

    # 指定されたファイルを順番に処理
    for file in files:
        x, y = read_xy_data(file)

        if not x or not y:
            return templates.TemplateResponse(
                "error.html",
                {
                    "request": request,
                    "file": file,  # ← error.htmlでファイル名を表示するために渡してるだけ
                },
                status_code=500,
            )

        # データをグラフに追加
        fig.add_trace(go.Scatter(x=x, y=y, mode='lines+markers', name=f'{file}'))

    # レイアウト設定
    fig.update_layout(
        title="Example: Title",
        xaxis_title="Example: X-axis",
        yaxis_title="Example: Y-axis",
        template="plotly_white",
        showlegend=True,  # これがないと1つのファイルを選択したときに凡例が表示されなくなる
        updatemenus=[  # Y軸をリニアと対数で切り替えるためのボタン
            {
                'buttons': [
                    {
                        'args': [{'yaxis.type': 'linear'}],  # 辞書で指定するのではなくて"."を使って書けばこの要素だけオーバーライドできる(relayout)
                        'label': 'y: Linear Scale',
                        'method': 'relayout'
                    },
                    {
                        'args': [{'yaxis.type': 'log'}],  # 辞書で指定するのではなくて"."を使って書けばこの要素だけオーバーライドできる(relayout)
                        'label': 'y: Logarithmic Scale',
                        'method': 'relayout'
                    }
                ],
                'direction': 'down',
                'pad': {'r': 10, 't': 10},
                'showactive': True,
                'x': 0.17,
                'xanchor': 'left',
                'y': 1.15,
                'yanchor': 'top'
            }
        ]
    )

    # グラフをHTML化
    graph_html = fig.to_html()

    return HTMLResponse(content=graph_html, status_code=200)

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Select Data Files</title>
</head>
<body>
    <h1>Select Data Files</h1>
    <form action="/graph" method="post" target="_blank">
        <label for="xy_txt">xy.txt</label>
        <input type="checkbox" name="files" value="xy.txt" id="xy_txt">
        <br>
        <label for="xy_negative_txt">xy_negative.txt</label>
        <input type="checkbox" name="files" value="xy_negative.txt" id="xy_negative_txt">
        <br><br>
        <button type="submit">See Graphs</button>
    </form>
</body>
</html>

表示されるグラフ

拡大縮小も Y 軸の切り替え( Linear / Log )もできるし、凡例の部分を押すと、そのグラフの表示 / 非表示も切り替えられる。

plotly で表示したインタラクティブなグラフ


軸目盛の調整

exponentformat で「 10 の何乗」みたいな指数表記を調整できる。

fig.update_layout(
    yaxis=dict(
        title="y",
        exponentformat="power"  # ここで指定する
    ),
)

cf. layout.yaxis.exponentformat

exponentformat で指数表記を調整したグラフ