PythonでHTMLにグラフ描画!?PyScript+Matplotlib+Bootstrapの組み合わせ方法

Pythonを使ってHTML上にグラフを描画する方法について、戸惑っていませんか?

Webページにグラフを表示したい場合,PyScript,Matplotlib,Bootstrapという複数のツールを組み合わせる方法で実現可能です

この記事では,PyScript,Matplotlib,Bootstrapを組み合わせて,PythonでHTMLにグラフを描画する方法を分かりやすく解説します。Webページ上で美しいグラフを表示することができます。

読み終えた後は,自身のWebプロジェクトに合わせてPyScriptとMatplotlibを活用し,Bootstrapとの連携により魅力的なグラフを実装できるようになります

目次

完成形のコードと結果: イントロ

まずPyScript,Matplotlib,Bootstrapを組み合わせてダッシュボード画面を作成したHTMLコードとWebページの結果をご紹介します

この記事はインタラクティブな動作をせず,静的なグラフです

それぞれの機能は下記の通りで,より詳細については次章以降をご参照ください

構成と各ライブラリの機能
Pyscript

Python コードをブラウザで直接実行可能にするライブラリ

インタラクティブな Python REPL をHTMLに追加したり,ダッシュボードを共有できる

Matplotlib

Pythonのデータ可視化ライブラリ。

グラフやプロットを簡単に作成し,データを視覚的に表現。初心者にも使いやすい。

Bootstrap

オープンソースのWebデザインフレームワーク

HTML/CSS/JSで簡単に美しいウェブページを作成し、初心者にも使いやすい

<html>
  <head lang="en">
    <title>Plot graphs with Python</title>
    <meta charset="utf-8">
    <!-- pyscript -->
    <link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
    <script defer src="https://pyscript.net/latest/pyscript.js"></script>
    <!-- Bootstrap for CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
  </head>
  <body>

    <!-- Pythonのライブラリをインストールします -->
    <py-config>
      packages = ["matplotlib", "pandas", "numpy"]
    </py-config>
    
    <header class="navbar sticky-top bg-dark flex-md-nowrap p-0 shadow" data-bs-theme="dark">
      <a class="navbar-brand col-md-3 col-lg-2 me-0 px-3 fs-6 text-white" href="#">Powered by Python</a>
    </header>
    
    <!-- ここからサイドバーのコードです -->
    <div class="container-fluid">
      <div class="row">
        <div class="sidebar border border-right col-md-3 col-lg-2 p-0 bg-body-tertiary">
          <div class="offcanvas-lg offcanvas-end bg-body-tertiary" tabindex="-1" id="sidebarMenu" aria-labelledby="sidebarMenuLabel">
            <div class="offcanvas-header">
              <h5 class="offcanvas-title" id="sidebarMenuLabel">Company name</h5>
              <button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#sidebarMenu" aria-label="Close"></button>
            </div>
            <div class="offcanvas-body d-md-flex flex-column p-0 pt-lg-3 overflow-y-auto fw-bold">
              <ul class="nav flex-column">
                <li class="nav-item">
                  <a class="nav-link d-flex align-items-center gap-2  btn btn-light" aria-current="page" href="#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-house" viewBox="0 0 16 16">
                      <path d="M8.707 1.5a1 1 0 0 0-1.414 0L.646 8.146a.5.5 0 0 0 .708.708L2 8.207V13.5A1.5 1.5 0 0 0 3.5 15h9a1.5 1.5 0 0 0 1.5-1.5V8.207l.646.647a.5.5 0 0 0 .708-.708L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.707 1.5ZM13 7.207V13.5a.5.5 0 0 1-.5.5h-9a.5.5 0 0 1-.5-.5V7.207l5-5 5 5Z"/>
                    </svg>
                    Home
                  </a>
                </li>
                <li class="nav-item">
                  <a class="nav-link d-flex align-items-center gap-2 btn btn-light" href="#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-caret-right" viewBox="0 0 16 16">
                      <path d="M6 12.796V3.204L11.481 8 6 12.796zm.659.753 5.48-4.796a1 1 0 0 0 0-1.506L6.66 2.451C6.011 1.885 5 2.345 5 3.204v9.592a1 1 0 0 0 1.659.753z"/>
                    </svg>
                    Start
                  </a>
                </li>
                <li class="nav-item">
                  <a class="nav-link d-flex align-items-center gap-2 btn btn-light" href="#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-stop" viewBox="0 0 16 16">
                      <path d="M3.5 5A1.5 1.5 0 0 1 5 3.5h6A1.5 1.5 0 0 1 12.5 5v6a1.5 1.5 0 0 1-1.5 1.5H5A1.5 1.5 0 0 1 3.5 11V5zM5 4.5a.5.5 0 0 0-.5.5v6a.5.5 0 0 0 .5.5h6a.5.5 0 0 0 .5-.5V5a.5.5 0 0 0-.5-.5H5z"/>
                    </svg>
                    Stop
                  </a>
                </li>
                <li class="nav-item">
                  <a class="nav-link d-flex align-items-center gap-2 btn btn-light" href="#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-stars" viewBox="0 0 16 16">
                      <path d="M7.657 6.247c.11-.33.576-.33.686 0l.645 1.937a2.89 2.89 0 0 0 1.829 1.828l1.936.645c.33.11.33.576 0 .686l-1.937.645a2.89 2.89 0 0 0-1.828 1.829l-.645 1.936a.361.361 0 0 1-.686 0l-.645-1.937a2.89 2.89 0 0 0-1.828-1.828l-1.937-.645a.361.361 0 0 1 0-.686l1.937-.645a2.89 2.89 0 0 0 1.828-1.828l.645-1.937zM3.794 1.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387A1.734 1.734 0 0 0 4.593 5.69l-.387 1.162a.217.217 0 0 1-.412 0L3.407 5.69A1.734 1.734 0 0 0 2.31 4.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387A1.734 1.734 0 0 0 3.407 2.31l.387-1.162zM10.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732L9.1 2.137a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L10.863.1z"/>
                    </svg>
                    Reset
                  </a>
                </li>
              </ul>
    
              <h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-body-secondary text-uppercase">
                <span>Edit a graph</span>
                <a class="link-secondary" href="#" aria-label="Add a new report">
                </a>
              </h6>
              <ul class="nav flex-column mb-auto">
                <li class="nav-item">
                  <a class="nav-link d-flex align-items-center gap-2" href="#">
                    Select Datasets
                  </a>
                  <div class="d-flex mx-3">
                    <div class="form-check form-check-inline">
                      <input class="form-check-input" type="checkbox" id="inlineCheckbox1" value="option1">
                      <label class="form-check-label" for="inlineCheckbox1">1</label>
                    </div>
                    <div class="form-check form-check-inline">
                      <input class="form-check-input" type="checkbox" id="inlineCheckbox2" value="option2">
                      <label class="form-check-label" for="inlineCheckbox2">2</label>
                    </div>
                    <div class="form-check form-check-inline">
                      <input class="form-check-input" type="checkbox" id="inlineCheckbox3" value="option3">
                      <label class="form-check-label" for="inlineCheckbox3">3</label>
                    </div>
                  </div>
                </li>
                <li class="nav-item">
                  <a class="nav-link d-flex align-items-center gap-2" href="#">
                    X range
                  </a>
                  <div class="d-flex align-items-center col-8 gap-2 mx-3">
                    <div class="col-1">0</div>
                    <div class="col">
                      <input type="range" class="w-100" id="customRange1">
                    </div>
                    <div class="col-1">10</div>
                  </div>
                </li>
                <li class="nav-item">
                  <a class="nav-link d-flex align-items-center gap-2" href="#">
                    Y range
                  </a>
                  <div class="d-flex align-items-center col-8 gap-2 mx-3">
                    <div class="col-1">0</div>
                    <div class="col">
                      <input type="range" class="w-100" id="customRange1">
                    </div>
                    <div class="col-1">10</div>
                  </div>
                </li>
                <li class="nav-item">
                  <a class="nav-link d-flex align-items-center gap-2 btn btn-light" href="#">
                    Save
                  </a>
                  <div class="d-flex mx-3">
                    <div class="form-check form-check-inline">
                      <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1">
                      <label class="form-check-label" for="inlineRadio1">png</label>
                    </div>
                    <div class="form-check form-check-inline">
                      <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio2" value="option2">
                      <label class="form-check-label" for="inlineRadio2">jpg</label>
                    </div>
                  </div>
                </li>
              </ul>
    
              <hr class="my-3">
    
              <ul class="nav flex-column mb-auto">
                <li class="nav-item">
                  <a class="nav-link d-flex align-items-center gap-2" href="#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-gear-wide-connected" viewBox="0 0 16 16">
                      <path d="M7.068.727c.243-.97 1.62-.97 1.864 0l.071.286a.96.96 0 0 0 1.622.434l.205-.211c.695-.719 1.888-.03 1.613.931l-.08.284a.96.96 0 0 0 1.187 1.187l.283-.081c.96-.275 1.65.918.931 1.613l-.211.205a.96.96 0 0 0 .434 1.622l.286.071c.97.243.97 1.62 0 1.864l-.286.071a.96.96 0 0 0-.434 1.622l.211.205c.719.695.03 1.888-.931 1.613l-.284-.08a.96.96 0 0 0-1.187 1.187l.081.283c.275.96-.918 1.65-1.613.931l-.205-.211a.96.96 0 0 0-1.622.434l-.071.286c-.243.97-1.62.97-1.864 0l-.071-.286a.96.96 0 0 0-1.622-.434l-.205.211c-.695.719-1.888.03-1.613-.931l.08-.284a.96.96 0 0 0-1.186-1.187l-.284.081c-.96.275-1.65-.918-.931-1.613l.211-.205a.96.96 0 0 0-.434-1.622l-.286-.071c-.97-.243-.97-1.62 0-1.864l.286-.071a.96.96 0 0 0 .434-1.622l-.211-.205c-.719-.695-.03-1.888.931-1.613l.284.08a.96.96 0 0 0 1.187-1.186l-.081-.284c-.275-.96.918-1.65 1.613-.931l.205.211a.96.96 0 0 0 1.622-.434l.071-.286zM12.973 8.5H8.25l-2.834 3.779A4.998 4.998 0 0 0 12.973 8.5zm0-1a4.998 4.998 0 0 0-7.557-3.779l2.834 3.78h4.723zM5.048 3.967c-.03.021-.058.043-.087.065l.087-.065zm-.431.355A4.984 4.984 0 0 0 3.002 8c0 1.455.622 2.765 1.615 3.678L7.375 8 4.617 4.322zm.344 7.646.087.065-.087-.065z"/>
                    </svg>
                    Settings
                  </a>
                </li>
                <li class="nav-item">
                  <a class="nav-link d-flex align-items-center gap-2" href="#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-door-closed" viewBox="0 0 16 16">
                      <path d="M3 2a1 1 0 0 1 1-1h8a1 1 0 0 1 1 1v13h1.5a.5.5 0 0 1 0 1h-13a.5.5 0 0 1 0-1H3V2zm1 13h8V2H4v13z"/>
                      <path d="M9 9a1 1 0 1 0 2 0 1 1 0 0 0-2 0z"/>
                    </svg>
                    Sign out
                  </a>
                </li>
              </ul>
            </div>
          </div>
        </div>
    
        <!-- ここからダッシュボードのコードです -->
        <main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
          <div class="pt-3 pb-2 mb-3 border-bottom">
            <h1 class="h2">Dashboard</h1>
          </div>
          <div class="my-4 w-100">
            <py-script>
              import pandas as pd
              import numpy as np
              import matplotlib.pyplot as plt
        
              def plt_style():
                plt.rcParams['figure.autolayout'] = True
                plt.rcParams['figure.figsize'] = [6.4, 4.8]
                plt.rcParams['font.family'] ='serif'
                plt.rcParams['font.size'] = 12
                plt.rcParams['xtick.direction'] = 'in'
                plt.rcParams['ytick.direction'] = 'in'
                plt.rcParams['axes.linewidth'] = 1.0
                plt.rcParams['errorbar.capsize'] = 6
                plt.rcParams['lines.markersize'] = 6
                plt.rcParams['lines.markerfacecolor'] = 'white'
                plt.rcParams['mathtext.fontset'] = 'cm'
        
              def data():
                x = np.linspace(0, 10, 100)
                y1 = 4 + 2 * np.sin(2 * x)
                y2 = 4 + 2 * np.cos(2 * x)
                return x, y1, y2
                
              def plot(x, y1, y2):
                fig, ax = plt.subplots()
        
                ax.plot(x, y1, linestyle='-', label='Sample 1')
                ax.plot(x, y2, linestyle='--', label='Sample 2')
               
                ax.set_xlim(0, 8)
                ax.set_ylim(0, 8)
                ax.set_xlabel('X label')
                ax.set_ylabel('Y label')
                ax.legend()
                ax.set_title('Simple line')
                
                display(fig)
              
              plt_style()
              x, y1, y2 = data()
              plot(x, y1, y2)
            </py-script>
            <div id="graph-area"></div>
          </div>

          <!-- ここからデータ表のコードです -->
          <h2>Data Table</h2>
          <div class="table-responsive small">
            <table class="table table-striped table-sm">
              <thead>
                <tr>
                  <th scope="col">Time (s)</th>
                  <th scope="col">Sample 1 (V)</th>
                  <th scope="col">Sample 2 (V)</th>
                  <th scope="col">Sample 3 (V)</th>
                </tr>
              </thead>
              <tbody>
                <tr>
                  <td>0</td>
                  <td>4</td>
                  <td>6</td>
                  <td>3</td>
                </tr>
                <tr>
                  <td>1</td>
                  <td>6</td>
                  <td>3.5</td>
                  <td>4</td>
                </tr>
                <tr>
                  <td>2</td>
                  <td>2.5</td>
                  <td>2.5</td>
                  <td>1</td>
                </tr>
                <tr>
                  <td>3</td>
                  <td>3</td>
                  <td>5.8</td>
                  <td>5</td>
                </tr>
                <tr>
                  <td>4</td>
                  <td>5.8</td>
                  <td>3.8</td>
                  <td>3</td>
                </tr>
                <tr>
                  <td>5</td>
                  <td>2.8</td>
                  <td>2.3</td>
                  <td>8</td>
                </tr>
                <tr>
                  <td>6</td>
                  <td>2.5</td>
                  <td>5.2</td>
                  <td>2</td>
                </tr>
                <tr>
                  <td>7</td>
                  <td>5.6</td>
                  <td>4.3</td>
                  <td>3</td>
                </tr>
                <tr>
                  <td>8</td>
                  <td>3</td>
                  <td>2.2</td>
                  <td>6</td>
                </tr>
              </tbody>
            </table>
          </div>
        </main>
      </div>
    </div>
    <!-- Bootstrap for Javascript -->
    <!-- bodyを閉じる直前に配置します -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
  </body>
</html>

VSCodeの開発環境構築: 準備ステップ0

pyscriptを使った開発はHTMLを使用し,ブラウザで結果を確認します

そのため,開発環境をPythonだけではなくHTMLに対応させる必要があります

この記事ではVisual Studio Code (VSCode) でHTMLの開発環境を整える方法について簡単に解説します

拡張機能であるLive Serverを導入する

拡張機能であるLive Serverは,左のサイドバーのExtensionsから検索してインストールします

Live Serverでリアルタイムにブラウザを更新する

Live Serverは,HTMLを選択して,Go Liveをクリックすると起動します

Go Liveの場所は上節の画像右下の青色で囲った箇所です

これで下記のようにブラウザが立ち上がり,HTMLファイルを保存するたびに更新されるはずです

PyScriptの導入と使い方: 簡単ステップ1

PyScriptはCDNを使って簡単にHTMLに導入してPythonコードを記述することができます

まず,<head>タグ内にPyScriptのCDNを追加します

次に,<body>タグ内に<py-script>タグを追加してPythonコードを記述します

追加したコード

PyScriptのCDN <head>

<link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
<script defer src="https://pyscript.net/latest/pyscript.js"></script>

Pythonコードの記述用タグ <body>

<py-script>
    # この中にPythonコードを記述します
    print('Hello, World!')
</py-script>
<html>
  <head lang="en">
    <title>Plot graphs with Python</title>
    <meta charset="utf-8">
    <!-- pyscriptのCDN -->
    <link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
    <script defer src="https://pyscript.net/latest/pyscript.js"></script>
  </head>
  <body>
    <py-script>
        # この中にPythonコードを記述します
        print('Hello, World!')
    </py-script>
  </body>
</html>

Matplotlibでのグラフ描画: 一手間ステップ2

前章でPythonコードを書く準備ができたので,ここではMatplotlibでグラフを描画する方法を解説します

Pythonライブラリをインストールする

MatplotlibのようなPythonのライブラリを使用するには,<py-config>タグで事前に読み込みます

この記事では,Matplotlib, pandas, numpyを読み込みました

追加したコード

Pythonのライブラリ <body>

<py-config>
  packages = ["matplotlib", "pandas", "numpy"]
</py-config>
<body>
  <!-- Pythonのライブラリをインストールします -->
  <py-config>
    packages = ["matplotlib", "pandas", "numpy"]
  </py-config>
  <py-script>
      # この中にPythonコードを記述します
  </py-script>
  <div id="graph-area"></div>
</body>

Pythonコードを記述する

Pythonコードで異なる点は最後のグラフ表示をさせる記述で,display(fig)を使います

python単体で動作させるにはplt.show()を使ってください

下記のタブにコードとフローチャートの解説をしています

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

def plt_style():
    # step1 グラフのフォーマットを整える
    plt.rcParams['figure.autolayout'] = True
    plt.rcParams['figure.figsize'] = [6.4, 4.8]
    plt.rcParams['font.family'] ='serif'
    plt.rcParams['font.size'] = 12
    plt.rcParams['xtick.direction'] = 'in'
    plt.rcParams['ytick.direction'] = 'in'
    plt.rcParams['axes.linewidth'] = 1.0
    plt.rcParams['errorbar.capsize'] = 6
    plt.rcParams['lines.markersize'] = 6
    plt.rcParams['lines.markerfacecolor'] = 'white'
    plt.rcParams['mathtext.fontset'] = 'cm'

def data():
    # step2 データの作成
    x = np.linspace(0, 10, 100)
    y1 = 4 + 2 * np.sin(2 * x)
    y2 = 4 + 2 * np.cos(2 * x)
    return x, y1, y2
  
def plot(x, y1, y2):
    # step3 グラフフレームの作成
    fig, ax = plt.subplots()
    # step4 グラフの描画
    ax.plot(x, y1, linestyle='-', label='Sample 1')
    ax.plot(x, y2, linestyle='--', label='Sample 2')
  
    ax.set_xlim(0, 8)
    ax.set_ylim(0, 8)
    ax.set_xlabel('X label')
    ax.set_ylabel('Y label')
    ax.legend()
    ax.set_title('Simple line')
    # step5 HTML上にグラフ表示
    display(fig)

# 各関数の実行
plt_style()
x, y1, y2 = data()
plot(x, y1, y2)

BootstrapでのWebデザイン: 魅惑のステップ3

Webデザインを簡単かつ高速で行うためのフレームワークとしてBootstrapを使用しました

BootstrapはCDNを使って簡単にHTMLに導入できます

まず,<head>タグ内にCSSのためCDNを追加します

次に,<body>タグの終了直前にJavaScriptのためのCDNを追加します

デザインの詳細については省きますが,この記事ではこちらのテーマを基に作成しました

追加したコード

BootstrapのCSSのためCDN <head>

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">

BootstrapのJavaScriptのためCDN <body>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
<html>
  <head lang="en">
    <title>Plot graphs with Python</title>
    <meta charset="utf-8">
    <!-- pyscriptのCDN -->
    <link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
    <script defer src="https://pyscript.net/latest/pyscript.js"></script>
    <!-- BootstrapのCSSのためのCDN -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
  </head>
  <body>
    <!-- Pythonのライブラリをインストールします -->
    <py-config>
      packages = ["matplotlib", "pandas", "numpy"]
    </py-config>
    <py-script>
        # この中にPythonコードを記述します
    </py-script>
    <div id="graph-area"></div>
    <!-- BootstrapのJavascriptのためのCDN -->
    <!-- bodyを閉じる直前に配置します -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
  </body>
</html>

参考文献

Pyscript

Matplotlib

Bootstrap

Live Server

シェアしてくださると嬉しいです!
  • URLをコピーしました!

コメント

コメントする

目次