plotlyで魅せるPythonグラフ(2/3)3D・地理・動的可視化

plotlyで魅せるPythonグラフ(2/3)3D・地理・動的可視化

データが溢れる現代社会において、情報を効果的に伝達する手段としてのデータ可視化の重要性は、ますます高まっています。

Pythonの可視化ライブラリの中でも、plotlyは特にインタラクティブ可視化に優れています。

前回、このplotlyを使用したPythonでのインタラクティブデータ可視化について、基礎的な内容を紹介しました。

plotlyで魅せるPythonグラフ(1/3)基礎のキソ

データの世界は平面だけではありません。

3次元空間で舞い、地球の表面を駆け、時間とともに躍動します。Plotlyは、そんな多彩なデータの姿を鮮やかに描き出すパレットです。

今回は、3D可視化からインタラクティブな地理マッピング、動的なアニメーション、そして複合的なグラフ表現まで、Plotlyの機能を解説します。

3D可視化

3次元データの可視化は、複雑な関係性や傾向を理解する上で非常に強力なツールです。

Plotlyは、直感的で操作性の高い3D可視化機能を提供しています。

 

 3D散布図

3D散布図は、3つの変数間の関係を視覚化するのに適しています。

以下、コード例です。

import plotly.express as px
import numpy as np

# データの生成
n = 1000
random_x = np.random.randn(n)
random_y = np.random.randn(n)
random_z = np.random.randn(n)
color = np.random.randn(n)

# 3D散布図の作成
fig = px.scatter_3d(
    x=random_x, 
    y=random_y, 
    z=random_z, 
    color=color,
    title="3D Scatter Plot")

fig.show()

 

このコードでは、3次元空間にランダムな点を配置し、4つ目の変数を色で表現しています。ユーザーはマウスでグラフを回転させ、異なる角度から データを観察できます。

 

以下、実行結果です。

 

 3D曲面プロット

3D曲面プロットは、2つの独立変数と1つの従属変数の関係を表現するのに適しています。

以下、コード例です。

import plotly.graph_objects as go
import numpy as np

# データの生成
x = np.outer(np.linspace(-2, 2, 30), np.ones(30))
y = x.copy().T
z = np.sin(x ** 2 + y ** 2)

# 3D曲面プロットの作成
fig = go.Figure(data=[go.Surface(z=z, x=x, y=y)])

fig.update_layout(
    title='3D Surface Plot', 
    autosize=False,
    width=500, 
    height=500,
    margin=dict(l=65, r=50, b=65, t=90))

fig.show()

 

この例では、2変数関数 z = sin(x^2 + y^2) の3D曲面を描画しています。カラーマップは z の値に応じて自動的に適用されます。

 

以下、実行結果です。

 

 3Dメッシュプロット

3Dメッシュプロットは、3D曲面プロットの一種で、表面をワイヤーフレームで表現します。これにより、曲面の構造をより詳細に観察できます。

以下、コード例です。

import plotly.graph_objects as go
import numpy as np

# データの生成
x = np.linspace(-5, 5, 20)
y = np.linspace(-5, 5, 20)
x, y = np.meshgrid(x, y)
z = np.sin(np.sqrt(x**2 + y**2))

# 3Dメッシュプロットの作成
fig = go.Figure(
    data=[go.Mesh3d(
        x=x.flatten(), 
        y=y.flatten(), 
        z=z.flatten(), 
        intensity=z.flatten(),
        colorscale='Viridis')])

fig.update_layout(
    scene = dict(
        xaxis_title='X',
        yaxis_title='Y',
        zaxis_title='Z'),
        title='3D Mesh Plot: z = sin(sqrt(x^2 + y^2))')

fig.show()

 

この例では、3次元空間内の複雑な関数 sin(\sqrt{x^2 + y^2}) を可視化しています。カラースケールは関数の値に基づいており、構造の理解を助けます。

 

以下、実行結果です。

 

地理データの可視化

地理データの可視化は、位置に基づくデータの傾向や関係性を理解する上で非常に重要です。

Plotlyは、地理データを効果的に可視化するための多様なツールを提供しています。

 

 地図プロット(Mapbox)

Mapboxを使用した地図プロットは、詳細な背景地図上にデータを表示することができます。

以下、コード例です。

import plotly.express as px
import pandas as pd

# サンプルデータの作成 
data = {
    'centroid_lat': [35.658033, 35.683044, 35.690921],  # 渋谷、代々木、新宿の緯度
    'centroid_lon': [139.701635, 139.702042, 139.700258],  # 渋谷、代々木、新宿の経度
    'car_hours': [10, 20, 30],
    'peak_hour': [8, 18, 20]
}
df = pd.DataFrame(data)

# Mapboxトークンの設定(実際の使用時は自分のトークンに置き換えてください)
px.set_mapbox_access_token('your_mapbox_token_here')

# 地図プロットの作成
fig = px.scatter_mapbox(
    df, 
    lat="centroid_lat", 
    lon="centroid_lon", 
    color="peak_hour", 
    size="car_hours",
    hover_name="centroid_lat", 
    zoom=12) 

fig.update_layout(title='Car Sharing Data in JAPAN')
fig.show()

 

以下、実行結果です。

 

ちなみに、このコードを実行するには、有効なMapboxアクセストークンが必要です。

Mapboxアクセストークンについて説明します。

Mapboxアクセストークンとは?

Mapboxアクセストークンとは、Mapboxの地図サービスを利用するために必要な認証キーです。このトークンを使用することで、開発者はMapboxの詳細な地図データや機能にアクセスできるようになります。

Mapboxアクセストークンの取得方法

  1. Mapboxのウェブサイト(https://www.mapbox.com/)にアクセスし、アカウントを作成します。
  2. ダッシュボードにログインし、「Access tokens」セクションに移動します。
  3. 新しいトークンを生成するか、既存のデフォルトトークンを使用します。

 

Mapboxアクセストークンを使用することで、高品質で詳細な地図背景を持つ地理データの可視化が可能になります。

ただし、無料利用には制限があるため、大規模なプロジェクトや商用利用の場合は、Mapboxの料金プランを確認する必要があります。

 

 コロプレス図(Choropleth maps)

コロプレス図は、地理的な領域をデータ値に応じて色分けする手法です。

以下、コード例です。

import plotly.express as px

# サンプルデータの読み込み
df = px.data.gapminder()

# 2007年のデータのみを使用
df_2007 = df[df.year == 2007]

# コロプレス図の作成
fig = px.choropleth(
    df_2007, 
    locations="iso_alpha",
    color="lifeExp",
    hover_name="country",
    projection="natural earth",
    title="Life Expectancy in 2007",
    color_continuous_scale=px.colors.sequential.Plasma)

fig.show()

 

この例では、2007年の世界各国の平均寿命をコロプレス図で表現しています。

 

以下、実行結果です。

 

 散布地図(Scattergeo)

散布地図は、地理的な位置に点をプロットする手法で、位置に基づいたデータの分布を表現するのに適しています。

以下、コード例です。

import plotly.express as px

# サンプルデータの読み込み
df = px.data.gapminder()

# 2007年のデータのみを使用
df_2007 = df[df.year == 2007]

# 散布地図の作成
fig = px.scatter_geo(
    df_2007, 
    locations="iso_alpha", 
    color="continent",
    hover_name="country", 
    size="pop",
    projection="natural earth",
    title="Population by Country in 2007")

fig.show()

 

この例では、2007年の世界各国の人口を散布地図で表現しています。点の大きさは人口の大きさを、色は大陸を示しています。

 

以下、実行結果です。

アニメーションとスライダー

データの時間的変化や異なる条件下での変化を視覚化することは、動的なデータ分析において非常に重要です。

Plotlyは、アニメーションとスライダーを使用して、このような動的な可視化を簡単に作成できる機能を提供しています。

 

 フレームアニメーション

フレームアニメーションは、時系列データや異なる条件下でのデータの変化を表現するのに適しています。

以下、コード例です。

import plotly.express as px

# Gapminderデータセットの読み込み
df = px.data.gapminder()

# アニメーション付き散布図の作成
fig = px.scatter(
    df, x="gdpPercap", 
    y="lifeExp", 
    size="pop", 
    color="continent",    
    hover_name="country", 
    log_x=True, 
    size_max=60,
    animation_frame="year", 
    animation_group="country",
    title="GDP per Capita vs Life Expectancy (1952-2007)",
    range_x=[100, 100000], 
    range_y=[25, 90])

fig.show()

 

このコードは、1952年から2007年までの各国のGDP per Capitaと平均寿命の関係を、アニメーション付きの散布図で表現しています。

 

以下、実行結果です。

 

 スライダーの追加

スライダーを使用すると、ユーザーが特定の変数を調整しながらデータの変化を観察できます。

以下、コード例です。

import plotly.graph_objects as go
import numpy as np

# データの生成
x = np.linspace(0, 10, 100)

# 図の作成
fig = go.Figure()

# 複数のトレースを追加
for step in range(5):
    fig.add_trace(
        go.Scatter(
            visible=False,
            line=dict(color="#00CED1", width=6),
            name=f"ν = {step}",
            x=x,
            y=np.sin(step * x)
        )
    )

# 最初のトレースを表示
fig.data[0].visible = True

# スライダーの作成
steps = []
for i in range(len(fig.data)):
    step = dict(
        method="update",
        args=[{"visible": [False] * len(fig.data)},
              {"title": f"Sine Wave: ν = {i}"}],
    )
    step["args"][0]["visible"][i] = True
    steps.append(step)

sliders = [dict(
    active=0,
    currentvalue={"prefix": "Frequency: "},
    pad={"t": 50},
    steps=steps
)]

fig.update_layout(
    sliders=sliders,
    title="Sine Wave with Adjustable Frequency"
)

fig.show()

 

このコードは、周波数を変更できるスライダー付きの正弦波グラフを作成します。ユーザーはスライダーを動かすことで、異なる周波数の正弦波を観察できます。

 

以下、実行結果です。

 

 ボタンによる制御

ボタンを追加することで、ユーザーが特定のアクションを実行したり、表示を切り替えたりできます。

以下、コード例です。

import plotly.graph_objects as go
import numpy as np

# データの生成
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)

# 図の作成
fig = go.Figure()

# 複数のトレースを追加
fig.add_trace(go.Scatter(x=x, y=y1, name="Sine"))
fig.add_trace(go.Scatter(x=x, y=y2, name="Cosine"))

# ボタンの作成
fig.update_layout(
    updatemenus=[
        dict(
            type="buttons",
            direction="right",
            active=0,
            x=0.57,
            y=1.2,
            buttons=list([
                dict(label="Both",
                     method="update",
                     args=[{"visible": [True, True]},
                           {"title": "Sine and Cosine"}]),
                dict(label="Sine",
                     method="update",
                     args=[{"visible": [True, False]},
                           {"title": "Sine"}]),
                dict(label="Cosine",
                     method="update",
                     args=[{"visible": [False, True]},
                           {"title": "Cosine"}]),
            ]),
        )
    ],
    title="Trigonometric Functions"
)

fig.show()

 

このコードは、正弦波と余弦波のグラフを表示し、ボタンを使って表示する関数を切り替えられるようにしています。

 

以下、実行結果です。

 

複合グラフとサブプロット

複数のグラフを組み合わせたり、一つの図の中に複数のサブプロットを配置したりすることで、より複雑なデータの関係性や多面的な分析結果を効果的に表現できます。

 

 複数のグラフの組み合わせ

異なるタイプのグラフを組み合わせることで、データの異なる側面を同時に表現できます。

以下、コード例です。

import plotly.graph_objects as go
import numpy as np

# データの生成
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)

# 図の作成
fig = go.Figure()

# 折れ線グラフの追加
fig.add_trace(go.Scatter(x=x, y=y1, name="Sine", mode="lines"))

# 散布図の追加
fig.add_trace(go.Scatter(x=x, y=y2, name="Cosine", mode="markers"))

# 棒グラフの追加
fig.add_trace(go.Bar(x=[0, 1, 2, 3], y=[1, 3, 2, 4], name="Bar Data"))

fig.update_layout(title="Combined Graph: Line, Scatter, and Bar",
                  xaxis_title="X Axis",
                  yaxis_title="Y Axis")

fig.show()

 

このコードは、折れ線グラフ、散布図、棒グラフを一つの図に組み合わせています。

 

以下、実行結果です。

 

 サブプロットの作成

make_subplots 関数を使用して、複数のサブプロットを含む図を作成できます。

以下、コード例です。

import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np

# データの生成
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)

# サブプロットの作成
fig = make_subplots(
    rows=2, cols=2, 
    subplot_titles=("Sine", "Cosine", "Bar Chart", "Scatter Plot"))

# 各サブプロットにグラフを追加
fig.add_trace(
    go.Scatter(x=x, y=y1, mode="lines"), 
    row=1, col=1)
fig.add_trace(
    go.Scatter(x=x, y=y2, mode="lines"), 
    row=1, col=2)
fig.add_trace(
    go.Bar(x=[1, 2, 3, 4], y=[4, 5, 6, 7]), 
    row=2, col=1)
fig.add_trace(
    go.Scatter(x=[1, 2, 3, 4], y=[10, 11, 12, 13], mode="markers"), 
    row=2, col=2)

fig.update_layout(height=600, width=800, title_text="Multiple Subplots")
fig.show()

 

このコードは、4つの異なるグラフを2×2のグリッドレイアウトで表示しています。

 

以下、実行結果です。

 

 グラフのレイアウト調整

レイアウトを調整することで、グラフの見た目や配置を細かく制御できます。

以下、コード例です。

import plotly.graph_objects as go
from plotly.subplots import make_subplots

# サブプロットの作成(横に3つ、縦に1つ)
fig = make_subplots(
    rows=1, cols=3, 
    subplot_titles=("Left Plot", "Middle Plot", "Right Plot"),
    specs=[[{"type": "scatter"}, {"type": "surface"}, {"type": "bar"}]])

# 左側のプロット(散布図)
fig.add_trace(
    go.Scatter(x=[1, 2, 3], y=[4, 5, 6], mode="markers"),
    row=1, col=1
)

# 中央のプロット(3D surface)
fig.add_trace(
    go.Surface(z=[[1, 2, 3], [4, 5, 6], [7, 8, 9]]),
    row=1, col=2
)

# 右側のプロット(棒グラフ)
fig.add_trace(
    go.Bar(x=["A", "B", "C"], y=[10, 8, 12]),
    row=1, col=3
)

# レイアウトの調整
fig.update_layout(
    title_text="Customized Subplot Layout",
    height=500,
    width=1000,
    showlegend=False
)

# x軸とy軸のタイトルを更新
fig.update_xaxes(title_text="X Axis", row=1, col=1)
fig.update_yaxes(title_text="Y Axis", row=1, col=1)

fig.show()

 

このコードは、異なるタイプの3つのグラフを横に並べて表示し、レイアウトを細かく調整しています。

 

以下、実行結果です。

 

 インセットグラフ(グラフ内グラフ)

Plotlyでグラフ内にグラフ(いわゆる「インセット」グラフ)を作成することができます。

これは特に、データの一部を拡大して詳細を示したい場合や、補足的な情報を追加したい場合に非常に有用です。

以下、コード例です。

import plotly.graph_objects as go

# メインプロットのデータ
x_main = [1, 2, 3, 4, 5]
y_main = [6, 11, 7, 3, 5]

# インセットプロットのデータ
x_inset = [1, 2, 3, 4, 5]
y_inset = [2, 3, 5, 2, 4]

# メインプロットの作成
fig = go.Figure()

fig.add_trace(
    go.Bar(
        x=x_main, 
        y=y_main, 
        name='Main Plot', 
        marker_color='blue'))

# インセットプロットの追加
fig.add_trace(
    go.Scatter(
        x=x_inset, 
        y=y_inset, 
        mode='lines+markers', 
        name='Inset Plot', 
        xaxis='x2', 
        yaxis='y2'))

# インセットプロットのレイアウト設定
fig.update_layout(
    xaxis2=dict(
        domain=[0.6, 0.95],
        anchor='y2'
    ),
    yaxis2=dict(
        domain=[0.6, 0.95],
        anchor='x2'
    ),
    title="Main Plot with Inset",
    shapes=[
        dict(
            type="rect",
            xref="paper", yref="paper",
            x0=0.6, y0=0.6, x1=0.95, y1=0.95,
            line=dict(color="Black", width=2)
        )
    ]
)

fig.show()

 

以下、実行結果です。

 

まとめ

今回は、Plotlyを使用したやや高度なデータ可視化技術について紹介しました。

3D可視化から始まり、地理データの表現、そしてアニメーションと複合グラフの作成まで、データの多面的な側面を効果的に表現する方法についてお話ししました。

これらの高度な可視化技術を習得することで、データアナリストやデータサイエンティストは、より豊かで洞察に満ちたデータストーリーを語ることが可能になります。

単なる数値やテーブルでは伝えきれない複雑な関係性や傾向を、視覚的かつインタラクティブに表現することで、データの真の価値を引き出し、意思決定者や関係者により深い理解を提供することができます。