pythonを使った三角ダイアグラム(ternary plot)の描画

三角プロット(ternary diagram)とは

三角プロット(ternary diagram)は,3成分のデータを三角形のグラフにプロットしたもので、3成分の相対的な割合(組成データなど)をによって,正三角形の位置でグラフ化する。プロットされた点から各辺へおろした線の位置がそれぞれの成分の大きさを示す(参考

3元プロットでは、3つの変数a, b, cの値の和がある定数K(1や100など)にならなければならず自由度は2であるため、3つの変数の組み合わせを2次元でグラフ化することができる。
化学組成の描写にも三角プロットはよく用いられ、異なる相が存在する組成領域の輪郭をプロットすることで、相図を作成するのにも使用できる。

ライブラリのインストール

plotlyに加えて、scikit-imageをインストールしておく(plotly.pcreate_ternary_contourを使用するには必要らしい)

condaインストール

conda install -c plotly plotly
conda install -c anaconda scikit-image

pipインストール

pip install plotly
pip install scikit-image

実装コード

以下三つの方法で三つのタイプの三角図(ternary diagram)を書いてみる。

  • plotly.express:散布図
  • plotly.graph_objects:等高線図,散布図
  • ploty.figure_factory(おまけ):散布図

モジュールインストール〜データの準備まで

X成分、Y成分、Z成分の三つから構成される仮想の組成を作成し、それぞれの組成に応じて適当な式に基づいて物性値を算出する。

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

import japanize_matplotlib
from scipy.stats import norm

import plotly.graph_objects as go
import plotly.express as px
import plotly.figure_factory as ff

# 仮組成データを作成する(0.1刻み,最大1で各組成の成分を作成する)
df = pd.DataFrame(columns=["X_component", "Y_component","Z_component","property"])

component_pattern = [n for n in range(0,11)]
comb = np.array(list(itertools.combinations_with_replacement(component_pattern, 2)))

df["X_component"] = comb[:,0]/10
df["Y_component"] = (comb[:,1] - comb[:,0])/10
df["Z_component"] = 1 - (df["X_component"] + df["Y_component"])

df['label'] = ["sample_{}".format(i+1) for i in range(len(df))]

# 適当な関数を作って、その組成における物性の値を決める
df["property"] = (df["X_component"]*0.1 \
                + (df["Y_component"]-0.1)**2 +\
                + (df["Z_component"]-0.2)**3)*20

df.loc[df["property"]>df["property"].mean(),"type"] = 'high_samples'
df.loc[df["property"]<=df["property"].mean(),"type"] = 'low_samples'

plotly express を使った描画

物性に応じたカラーマップ付きの散布図

引数colorに,色に反映させたい情報のあるカラム名を指定する

# マーカーのサイズ(size)は、dfのカラム名 or 配列(配列の長さはdfのサンプル数と揃える)で指定する。

fig = px.scatter_ternary(df, 
                         a="X_component", 
                         b="Y_component", 
                         c="Z_component", 
                         color="property", 
                         size=[20 for i in range(len(df))],
                         hover_name="label", 
                         size_max=15,
                         title="ternary plot",
                         )

fig.write_html("ternaryPlot_property.html")

作成したグラフ(HTML)を開く

※ 以降はスクリーンショットのみ掲載

ラベル(カテゴリデータ)に応じた散布図

引数symbolに,マーカータイプに反映させたい情報(カテゴリカルデータ)のあるカラム名を指定する。
color_discrete_mapで、それぞれのラベルに対する色を指定することも可能。

fig = px.scatter_ternary(df, 
                         a="X_component", 
                         b="Y_component", 
                         c="Z_component", 
                         color="type", 
                         symbol = 'type',
                         color_discrete_map = {
                                               "high_samples": "blue",
                                               "low_samples": "red"
                                               },
                         size="property",
                         hover_name="label", 
                         size_max=20,
                         title="ternary plot",
                         )

fig.write_html("ternaryPlot_type.html")

テキストをカスタマイズする

fig.update_layoutでグラフの細かいカスタマイズができる。

fig = px.scatter_ternary(df, 
                         a="X_component", 
                         b="Y_component", 
                         c="Z_component", 
                         color="property", 
                         size="property",
                         hover_name="label", 
                         size_max=15,
                         title="ternary plot"
                         )
fig.write_html("ternaryPlot_property.html")

def makeAxis(title, tickangle):
    return {
      'title': title,
      'titlefont': { 'size': 20 },
      'tickangle': tickangle,
      'tickfont': { 'size': 15 },
      'tickcolor': 'rgba(0,0,0,0)',
      'ticklen': 5,
      'dtick':0.1,
      'showline': True,
      'showgrid': True,
      'gridcolor':'gray'
    }

fig.update_layout(
    font_family="Courier New",
    font_color="#545454",
    title_font_family="Courier",
    title_font_color="#344050",
    legend_title="Album",
    legend_title_font_color="#545454",
    legend_title_font_size=40,
    title_font_size=24,
    )

fig.update_layout({
    'ternary': {
                'aaxis': makeAxis('X_component', 45),
                'baxis': makeAxis('<br>Y_component', 0),
                'caxis': makeAxis('<br>Z_component', -45),
                },
    'annotations': [{
      'showarrow': False,
      'text': "Ternary Diagram",
        'x': 0.5,
        'y': 1.2,
        'font': { 'size': 25 }
    }],
})

fig.write_html("ternaryPlot_property_custom.html")

plotly graph_objects を使った描画

ternary contours(等高線)を描く

fig = ff.create_ternary_contour(df[["X_component","Y_component","Z_component"]].T.values,
                                df["property"].values,
                                pole_labels=["X_component","Y_component","X_component"],
                                interp_mode='cartesian',
                                ncontours=20, # 等高線の数
                                colorscale='Viridis',
                                showscale=True,
                                title='ternary contours',
                                )

fig.write_html("ternaryContors.html")

ternary contours(等高線)を描く

  • coloring='line'で線のみの描画
  • showmarkers=Trueでマーカーを描画
fig = ff.create_ternary_contour(df[["X_component","Y_component","Z_component"]].T.values,
                                df["property"].values,
                                pole_labels=["X_component","Y_component","X_component"],
                                interp_mode='cartesian',
                                ncontours=20, # 等高線の数
                                colorscale='Viridis',
                                showscale=True,
                                title='ternary contours',
                                coloring='lines',
                                showmarkers=True,
                                )

fig.write_html("ternaryContors_line_and_markers.html")

補完のタイプを変更

補完のタイプには、デカルト空間での補完(interp_mode='cartesian')と等比級数変換?(interp_mode='ilr')があるらしい。

Two modes are available in order to interpolate between data points: interpolation in Cartesian space (interp_mode='cartesian') or interpolation using the isometric log-ratio transformation (see also preprint), interp_mode='ilr'. The ilr transformation preserves metrics in the simplex but is not defined on its edges.

fig = ff.create_ternary_contour(df[["X_component","Y_component","Z_component"]].T.values,
                                df["property"].values,
                                pole_labels=["X_component","Y_component","X_component"],
                                interp_mode='cartesian',
                                ncontours=20, # 等高線の数
                                colorscale='Viridis',
                                showscale=True,
                                title='ternary contours',
                                )

fig.write_html("ternaryContors_ilr.html")

plotly graph_objects を使った描画

plotly expressを使う場合よりもグラフの設定を色々といじることができる。

fig = go.Figure(go.Scatterternary(
                                    {
                                    'mode': 'markers',
                                    'a': df["X_component"],
                                    'b': df["Y_component"],
                                    'c': df["Z_component"],
                                    'text': round(df["property"],3),
                                    'hoverinfo':'text',
                                    'marker': {
                                             'color': df["property"].values,
                                             'size': 20,
                                             'opacity':0.5,
                                             'showscale':True,
                                             }    
                                    }
                                )
                )

# 軸のラベルなどを設定する
def makeAxis(title, tickangle):
    return {
      'title': title,
      'titlefont': { 'size': 20 },
      'tickangle': tickangle,
      'tickfont': { 'size': 15 },
      'tickcolor': 'rgba(0,0,0,0)',
      'ticklen': 5,
      'dtick':0.1,
      'showline': True,
      'showgrid': True,
      'gridcolor':'gray'
    }

fig.update_layout({
    'ternary': {
                'aaxis': makeAxis('X_component', 45),
                'baxis': makeAxis('<br>Y_component', 0),
                'caxis': makeAxis('<br>Z_component', -45),
                },
    'annotations': [{
      'showarrow': False,
      'text': "Ternary Diagram",
        'x': 0.5,
        'y': 1.2,
        'font': { 'size': 25 }
    }]
})

fig.write_html("ternaryPlot_go.html")

参考

関連書籍






udemy講座