openpyxlで複数のプロットデータを重ねた散布図をつくる


1.目的

pythonで作成した散布図をエクセルに出力するためにopenpyxlを使った。
そのとき、各プロットデータのまとまりごとでx値、y値、サンプルデータ数が異なっていて苦労したので検討したをやり方まとめる。

2.作成する散布図

ここでは例としてsklearnのirisのデータセットを散布図をエクセルに出力することを考える。

from sklearn import datasets
import pandas as pd
import matplotlib.pyplot as plt


iris = datasets.load_iris()

df = pd.DataFrame(iris.data,columns=iris.feature_names)
df['target'] = iris.target
df.loc[df['target'] == 0, 'target'] = "setosa"
df.loc[df['target'] == 1, 'target'] = "versicolor"
df.loc[df['target'] == 2, 'target'] = "virginica"

feature1 = 'sepal length (cm)'
feature2 = 'sepal width (cm)'

for t in df.target.unique():
    x = df[df.target==t].loc[:,feature1]
    y = df[df.target==t].loc[:,feature2]

plt.title('test graph')
plt.xlabel(feature1)
plt.ylabel(feature2)
plt.legend()
plt.show()

3.エクセルで作成

上の散布図をエクセルで作成するために以下のコードを実行。

import openpyxl
from openpyxl import Workbook
from openpyxl.chart import (
    ScatterChart,
    Reference,
    Series,
)

wb = Workbook()
ws = wb.active

feature1 = 'sepal length (cm)' #グラフでx軸になる特徴量
feature2 = 'sepal width (cm)'  #グラフでy軸になる特徴量

chart = ScatterChart() #散布図をかく
chart.title = 'test graph'    #グラフタイトル
chart.style = 10   #グラフのデザイン
chart.x_axis.title = feature1 #x軸ラベル
chart.y_axis.title = feature2 #y軸ラベル

r_end = 0 #

for t in  df.target.unique(): # setosa, versicolor,versicolorについてforループ処理を行う
    data_t = pd.DataFrame()
    data_t[''] =df[df.target==t].loc[:,feature1] # x値を抽出
    data_t[t] = y = df[df.target==t].loc[:,feature2] # y値を抽出
    input_data = data_t.T.reset_index().T.values.tolist()

    for row in input_data : #抽出した情報をエクセルシートに貼り付け
        ws.append(row)

    r_start = r_end+1 #  エクセルの「グラフの選択」の系列のスタート行を設定するためのパラメータ
    r_end = r_start+len(input_data)-1 #  エクセルの「グラフの選択」の系列のエンド行を設定するためのパラメータ

    xvalues = Reference(ws, min_col=1, min_row=r_start+1, max_row=r_end ) # 系列xの値
    values = Reference(ws, min_col=2, min_row=r_start, max_row=r_end )  # 系列yの値
    series = Series(values, xvalues, title_from_data=True)

    series.marker = openpyxl.chart.marker.Marker('circle') #マーカはサークルに設定
    series.graphicalProperties.line.noFill = True #プロットのみのため線はかかない設定にする
    chart.series.append(series)

ws.add_chart(chart, "E2") #グラフ貼り付けの位置(グラフの左上端)
wb.save("sample.xlsx")

これを実行すると、sample.xlsxというファイルが作成され、開くと以下のようなグラフができている。

※データの並び
ws.append(row)でデータをシートに書き込むと、新しいデータは下の行に追加される。
 ⇒列方向にデータを追加することをあきらめて、縦にデータを並べることにした。

4.参考資料

以下、参考にして勉強しました。
OpenPyXLのサイト:https://openpyxl.readthedocs.io/en/stable/index.html#
- Scatter Charts:上のコードはこのページのものを改造
- Line Charts:プロットの設定等を参照

以上