/plotly_embedding_in_zenn.py Secret
Last active
January 9, 2024 15:26
Create a plotly graph for embedding in zenn.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import textwrap | |
from pathlib import Path | |
from typing import Tuple | |
import numpy as np | |
import pandas as pd | |
import plotly.graph_objects as go | |
from bs4 import BeautifulSoup | |
from scipy.interpolate import RectBivariateSpline | |
def create_sample_data(n: int = 25): | |
# データセットの取得 | |
z = pd.read_csv( | |
"https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv", | |
).values | |
# テスト用のデータ水増し | |
x = np.linspace(0, 1, n) | |
y = np.linspace(0, 1, n) | |
z = RectBivariateSpline( | |
x=np.linspace(0, 1, z.shape[0]), | |
y=np.linspace(0, 1, z.shape[1]), | |
z=z, | |
)(x, y) | |
return x, y, z | |
def to_full_html(div: str, margin: str = "0", overflow: str = "auto") -> str: | |
"""グラフの div タグを埋め込み用にカスタマイズする。 | |
Args: | |
div: 入力の div タグの中身。 | |
margin: グラフの外側に付くマージン。0 を指定するとマージンなしになる。 | |
overflow: スクロールバーの挙動を制御する。hidden を指定すると(スクロールバーが必要でも)表示しない。 | |
Returns: 生成した html 文字列。 | |
""" | |
return textwrap.dedent( | |
f"""\ | |
<html> | |
<head><meta charset="utf-8" /></head> | |
<body style="margin: {margin}; overflow: {overflow}"> | |
{div} | |
</body> | |
</html> | |
""" | |
) | |
def separate_js(html_content: str, js_src: str) -> Tuple[str, str]: | |
"""HTML と JavaScript を分離して返す。 | |
Args: | |
html_content: HTML 文字列。 | |
js_src: 分離した JavaScript コードの置換対象。 | |
<script src="{js_src}"></script> に置き換えられる。 | |
Returns: JavaScript を取り除いた HTML 文字列と、分離した JavaScript 文字列のタプル。 | |
""" | |
html = BeautifulSoup(html_content, "html.parser") | |
# グラフデータを含む script タグを探す。 | |
# この機能を使うのはサイズが大きい時なので、一番サイズが大きいものにする。 | |
graph_script = max(html.find_all("script"), key=lambda s: len(s.string or "")) | |
# タグの分離と差し替え。 | |
graph_content = graph_script.string.strip() | |
graph_script.clear() | |
graph_script["src"] = js_src | |
return str(html), graph_content | |
def main(): | |
output_html = Path("./temp.html") | |
# output_js = Path("./temp.js") | |
data_size = 25 | |
# データ生成。 | |
x, y, z = create_sample_data(n=data_size) | |
# 適当な桁数で丸める。必要な桁数はデータ次第なので要確認。 | |
x = x.round(3) | |
y = y.round(3) | |
z = z.round(1) | |
# グラフの作成。 | |
fig = go.Figure( | |
data=[go.Surface(x=x, y=y, z=z)], | |
layout=go.Layout( | |
margin={"l": 0, "r": 0, "b": 0, "t": 0}, # マージンの除去。 | |
# paper_bgcolor="lightgray", # 背景色 | |
), | |
) | |
# HTML化。加工するので div タグだけ生成する。 | |
div = fig.to_html( | |
full_html=False, | |
include_plotlyjs="cdn", | |
# サイズを指定する場合はここで。埋め込みの大きさではないので注意。 | |
# スクロールバーを表示させたくない場合は下記の設定を使う。 | |
# codepen: "351px" | |
# jsfiddle: "381px" | |
# default_width="100%", | |
# default_height="100%", | |
# ダウンロードボタンを無効化する。 | |
# config={"modeBarButtonsToRemove": ["toImage"]}, | |
) | |
html = to_full_html( | |
div, | |
overflow="hidden", | |
) | |
# HTML と JavaScript を分離したい場合こうする。 | |
# html, js = separate_js(html, js_src=str(output_js)) | |
output_html.write_text(html) | |
# output_js.write_text(js) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment