本記事では、Excelを「Python」で操作する機能を提供する「openpyxl」ライブラリの解説をしていきます。
Excelには多くの機能が搭載されておりますので、ひと記事で全てを網羅的に解説することはできません。大項目(機能)ごとに分けた連載記事【Python×Excel】としてまとめています。
前回の記事(連載7回)では、openpyxlによる「グラフの作成手順の概要」と「グラフを構成する要素のクラスやオブジェクト」について紹介しました。
今回からは【実践編】としてサンプルコードを交えて、より具体的にグラフ作成の手順を解説していきます。openpyxlを使うことで、驚くほど簡単に実用的なグラフを描画できることを実感してみてください。
この記事を読むことで、次のようなことが「できる・わかる」ようになりますので最後までお付き合いください。
なお、本記事ではPythonプログラムでグラフを作成する際のキーポイントや特殊属性(プロパティ)について解説していきます。グラフそのももの説明は各種Excelの専門書を参考にして下さい。
本サイトでの紹介例は一例です。また、関数などの省略可能なオプション引数などについては割愛していますので、詳細や不明点などは必要に応じて公式サイトなどを参照してください。
【公式サイト】<https://openpyxl.readthedocs.io/en/stable/index.html>
1.「散布図」の作成手順について
oenpyxlでグラフ(散布図・バブルチャート)作成をする手順について解説します。
「散布図」や「バブルチャート」などのように個々の系列によって異なる項目(X軸の値)を設定できるタイプのグラフについては複数の系列をまとめて一括参照することはできません。
図2にグラフ作成の手順を体系化して示します。
各ブロックについて補足します。
(なお、➀➁…などは図2の番号に対応しています。 )
➀.【Chartオブジェクトを生成する】
例えば「散布図」であれば「ScatterChartクラス」という専用のクラスが用意されていますので、インポートしてオブジェクトを生成します。このChartオブジェクトがグラフ本体(フレームワーク)となります。
➁.【Chartオブジェクトにグラフの要素を設定する】
散布図にかぎらずグラフは、タイトル、凡例、軸のタイトルなどの(共通)要素により構成されます。これらをChartオブジェクトに専用の属性(プロパティ)で追加・設定していきます。
➂.【系列データごとにデータ(XY軸)の参照情報を定義し設定する】
系列ごとに異なる項目(X軸)をとり得るので、系列ごとにX軸のデータとY軸のデータ1組とするために、それぞれの参照情報を Referenceオブジェクト として定義します。(図2➂-1, ➂-2)
個々のX/Yの参照情報は、Seriesクラス の引数に設定して Seriesオブジェクト としてまとめます。この 「Seriesオブジェクト」が系列となります。(➂-3)
「Seriesオブジェクト定義」のイメージを図示すると次のようになります。
(図3は、Excelのデータの編集から、系列の編集ウィンドウで系列を定義している様子です。)
そして、Chartオブジェクトの series.append()メソッドで系列データを1つずつグラフ本体に追加していきます。(➂-4)➂.は系列ごとに繰り返す必要があります。系列が複数ある場合は、For文やWhile文などで処理を繰り返します。
(すべての参照情報を一括設定するための add_data()メソッドは不要となります。また、Seriesクラスの引数に系列名を指定してオブジェクトを取得した場合は、set_categories() メソッドも不要となります。)
➃.【系列データごとに装飾効果を適用する】
➂で追加したSeriesオブジェクトに対し、必要であれば塗りつぶし、枠線などの効果を適用させます。
➂でオブジェクト定義と同じタイミングで設定しても構いません。
➄.【Worksheetにグラフを挿入する】
最後にWorksheetオブジェクトの add_chart()メソッド で➀~➃で定義した「Chartオブジェクト」をワークシートに挿入します。
以上が、「散布図・バブルチャート」のグラフ作成手順となります。次節からは、具体的なコード例を交えながら詳細解説をしていきます。
2. openpyxlによる「散布図/バブルチャート」について
「散布図・バブルチャート」は、X軸(項目名)とY軸(値)の対となるデータの分布(ばらつき具合)または推移を表現するのに都合のよいグラフになります。
同じようなグラフのタイプに「折れ線グラフ(LineChart)」がありますが、各系列ごとにXの値(カテゴリ)共有する必要がない点に違いがあります。よって「散布図・バブルチャート」は図2の手順に示したように、系列ごとに参照情報を Seriesオブジェクト として定義する必要があります。
散布図を提供するクラスとして ScatterChartクラス が用意されています。
散布図の形態には、Excelであれば、マーカーだけプロットしたもの、ラインを描画したものあるいは、その両方など5種類の中から選ぶことができます。(図4 左)
上記クラスの仕様上では 引数:scatterStyle にオプション指定することでそれらを設定できるとありますが、以下「公式ドキュメント」の注意書き(引用)によると、このオプション指定をしても(今のところ)機能しないようです。
よって、マーカーや線の設定などは系列ごとに「Seriesオブジェクト」から個別に設定ことになります。
The specification says that there are the following types of scatter charts: ‘line’, ‘lineMarker’, ‘marker’, ‘smooth’, ‘smoothMarker’.
However, at least in Microsoft Excel, this is just a shortcut for other settings that otherwise have no effect. For consistency with line charts, the style for each series should be set manually.
openpyxl公式ドキュメント<散布図(scatterStyle)についてのコメント>
次に「バブルチャート」について解説します。
openpyxlではバブルチャートの機能を提供する、BubbleChartクラス が以下の書式で用意されています。
バブルチャートは、先程の散布図からZ軸方向に3次元要素を追加し、3次データの大きさに応じたプロット (バブル状)を描画できるグラフになります。そのため、BubbleChartクラス には3次表現に関係する引数が多数用意されています。ここではそのうち3つを紹介します。
引数:bubbleScale は、バブルの大きさを指定します。通常、バブルチャートでは「Seriesクラス」の第3 引数:zvalues にバブル径の大きさを指定しますが、この引数bubbleScaleによって、径の相対的な比率はそのままにバブルの拡大・縮小を0%~300%の間で指定できます。
また、引数:sizeRepresents によってバブル径サイズへの反映方法を‘area’(面積)または、’w’(幅)から選択できます。省略した場合は”面積”で表現されます。
そして、1点以下のような注意点もあります。
BubbleChartクラスをの引数に 引数:bubble3D があります。公式ドキュメントにはBool型(“True”/”False”)で立体(3D)表現できる旨の表記がありますが、実際に設定するとエラーが表示され反映できませんでした。(少なくともExcel環境ではエラーが発生します。デフォルト設定である“None”(2D表現)であれば発生しません。)
バブルチャートを立体表現するには「Seriesクラス(オブジェクト)」の引数または属性(bubble3D)で系列ごとに設定してやる方法があります。このあとのサンプルコードで示します。
ここまでが、散布図とバブルチャートの概要説明になります。次節より具体的なサンプルコードを交えて手順を解説していきます。
3. 散布図(ScatterChart)の実装
それでは、次のような仕様をもつ「散布図」の実装例をステップごとに順番に解説していきます。
また、元データとなるテーブルは以下のようになります。
系列が「都市名」、項目(X軸)が「気温」(左側のテーブル)、データ(Y軸)が「降水量」(右側のテーブル)となる散布図を作ります。
3.1 「散布図」の概要を定義する【Step.1】
はじめに、必要なクラスのインポートから、グラフ本体となる ScatterChartオブジェクト、それからタイトルや凡例などのグラフエリアを構成する要素を次の<List1>で定義します。
このプログラムで使用するブック(.xlsx)ファイルは以下からダウンロードできます。
# モジュール・クラス群のインポート-------------------------------------------
from openpyxl import load_workbook
# 散布図(グラフ本体、データ参照情報の定義)に必要となるクラス
from openpyxl.chart import ScatterChart, Reference, Series
# パターン(模様)塗り潰しに必要なクラス
from openpyxl.drawing.fill import PatternFillProperties, ColorChoice
# データラベル情報の定義に必要なクラス
from openpyxl.chart.label import DataLabel, DataLabelList
# ファイル(シート)読込--------------------------------------------------------
wb = load_workbook('Graph_DataSource.xlsx') # Excelファイル(元データ)の読込み
ws = wb.worksheets[0] # Worksheetオブジェクトの取得
# [A] グラフ本体と構成要素の準備 ----------------------------------------------
# Chartオブジェクト(散布図の本体)を取得
c1 = ScatterChart()
# グラフの大きさを調整する
c1.width = 18 # デフォルト(15cm)
c1.height = 10 # デフォルト(7cm)
# グラフのタイトルを設定(メイン、軸)
c1.title = "Scatter Chart" # メインタイトル
c1.x_axis.title = 'Temperature' # X軸のタイトル
c1.y_axis.title = 'Precipitation' # Y軸のタイトル
# グラフの凡例
c1.legend.position = 'b' # 凡例の配置位置
#---------------------------------------------
# <List2>へ続く
それでは、ポイントを解説します。
<List1>だけを実行しても図5のようにグラフエリアの枠以外は何も表示されませんが、次の<List2>でプロットエリアを定義した時点で、軸や凡例が反映されるようになります。
3.2 系列の参照情報を定義する【Step.2】
<List1>に続き<List2>を追加して下さい。<List2>では「Chartオブジェクト」にデータの参照情報を系列ごとに定義していきます。
# [B] Chartオブジェクトに系列データを設定する ------------------------------------------------
# 系列ごとに参照情報を取得する
for i in range(3, 9):
# 系列のY軸の値の参照する(系列名となる行も含めて参照する)
# "i"列, 4行目-10行目
values = Reference(ws, min_col=i, max_col=i, min_row=4, max_row=10)
# 系列のX軸の値の参照する
# "i"+8列, 5行目-10行目
xvalues = Reference(ws, min_col=i+8, max_col=i+8, min_row=5, max_row=10)
# 系列のデータをSeriesオブジェクトとして定義する
series = Series(values, xvalues, title_from_data=True)
# Seriesコレクションに個々のSeriesオブジェクトを追加する
c1.series.append(series)
#---------------------------------------------
# <List3>へ続く
系列ごとに Seriesオブジェクト を定義する必要があるので、For文によって系列数分繰り返します。今回は図6の右側のテーブルをYの値とし、右のテーブルをXの値とする系列を定義します。
※ 引数:titles_form_data で系列名を指定できるのはあくまで先頭行の要素となります。列方向では指定できませんので、テーブルは図6のような配置しておく必要があります。
ここまでのコードを実行した結果は図7のようになります。デフォルトではプロット同士がラインで結ばれた形態になっています。散布図はプロットの散布分布を把握することが多いですから、ラインで結線するよりもマーカーで強調表現した方良い場合があります。今回の場合も、見えずらくなるのでラインは不要となるケースです。
3.3 系列の書式を設定する【Step.3】
<List2>に続き<List3>を追加して下さい。<List3>では、系列の形態や装飾など見栄えを整えていきます。
# [C] 系列ごとにマーカーを設定する -----------------------------
# 各系列のプロットを表現するマーカーのシンボルの指定を辞書形式で用意
marker_symbol = {1:"triangle", 2:"diamond", 3:"square", 4:"star", 5:"circle", 6:"plus"}
# マーカーを塗り潰す色を同様に辞書形式で用意
# 色の指定はRGBのHex指定とColorChoiceオブジェクト指定による2タイプで指定
marker_solidFill = {1:"FF00FF", 2:"FFFF00", 3:"FFF0F",
4:ColorChoice(prstClr="midnightBlue"),
5:ColorChoice(prstClr="yellowGreen"),
6:ColorChoice(prstClr="cornflowerBlue")}
# データラベルを定義する(全系列の項目2番目にのみラベルを適用する)
lb = DataLabel(idx=1, showVal=True)
lbl = DataLabelList(dLbl=[lb])
# 個々のSeriesオブジェクトへ属性を設定します
# enumerateでオブジェクトとインデックスを同時に展開します
for i, obj in enumerate(c1.series, 1):
# ラインを無効
obj.graphicalProperties.line.noFill = True
# 以下マーカーの設定
obj.marker.symbol = marker_symbol[i] # シンボルを指定
obj.marker.size = 10 # サイズを指定
obj.marker.graphicalProperties.solidFill = marker_solidFill[i] # 塗りつぶし色
obj.marker.graphicalProperties.line.solidFill = "000000" # 枠線を黒に設定
# データラベルを設定
obj.labels=lbl
# Chartオブジェクトをシートに追加して保存 -----------------------------------------
ws.add_chart(c1, "B13") # B13セルを左上にグラフを貼り付ける
wb.save('Scatter_example1_with_label.xlsx')
先の図7のように、今回のデータ分布であればラインで結線するよりも各プロットをマーカーで強調表現した方が都合がよさそうです。そこで、<List3>では各系列の「Seriesオブジェクト」から配下の属性を使って、ラインを無効にしてマーカーを設定をしていきます。
21行目からは、For文 と enumerate関数 によって、コレクションから「Seriesオブジェクト」とインデックス(1はじまり)を展開して、各属性によって設定を行います。
最後に labels属性 で先の「DatalabelListオブジェクト」を設定した後に、ワークシートにChartオブジェクトを貼り付けてからブックを保存します。
サンプルコードの解説は以上となります。<List1>~<List3>をすべて連結、実行した結果(以下からダウンロード可能)は図8のようになりました。
プロットがマーカー表記となり、シンボルの形状や色、それから2番目の項目に対してデータラベルが付加されました。図7の比較して分かりやすくなりましたね。
4. バブルチャート(BubbleChart) の実装
次にバブルチャートの実装例を紹介していきます。次のような仕様をもつバブルチャートついてステップごとに解説していきます。
また、元データとなるテーブルは以下のようになります。
系列が「ID1…」、項目(X軸)が(左のテーブル)、データ(Y軸)が「中央のテーブル」、そしてプロットの大きさ (Z軸)を「右のテーブル」をそれぞれ参照することにします。
4.1 「バブルチャート」の概要を定義する【Step.1】
はじめに、必要なクラスのインポートからグラフ本体となる「BubbleChartオブジェクト」、それからタイトルや凡例などのグラフエリアを構成する要素を次の<List1>で定義します。
このプログラムで使用するブック(.xlsx)ファイルは以下からダウンロードできます。
# モジュール・クラス群のインポート-------------------------------------------
from openpyxl import Workbook
# バブルチャート(グラフ本体、データ参照情報の定義)に必要となるクラス
from openpyxl.chart import BubbleChart, Series, Reference
# グラデーション塗り潰しに必要なクラス➀
from openpyxl.drawing.fill import GradientFillProperties, GradientStop
# グラデーション塗り潰しに必要なクラス➁
from openpyxl.drawing.fill import ColorChoice, LinearShadeProperties
# 元ファイル(シート)読込--------------------------------------------------------
wb = load_workbook('Graph_DataSource_Bubble.xlsx') # Excelファイル(元データ)の読込み
ws = wb.worksheets[0] # Worksheetオブジェクトの取得
# [A] グラフ本体とグラフ概要の設定 ---------------------------------------------------------
# Chartオブジェクト(散布図の本体)を取得
c1 = BubbleChart()
# グラフの大きさを調整する
c1.width = 18 # デフォルト(15cm)
c1.height = 10 # デフォルト(7cm)
# グラフの凡例
c1.legend.position = 'b' # 凡例の配置位置
# スタイル設定
c1.style = 18
#---------------------------------------------
# <List2>へ続く
ちなみに、32行目の style属性 を用いることで、既定のスタイル(色調や影・立体効果など)を簡単に設定することができます。スタイルの設定例については、このあと紹介します。
4.2 「バブルチャート」の系列の参照情報を定義する【Step.2】
<List1>に続き<List2>を追加して下さい。<List2>では「BubbleChartオブジェクト」にデータの参照情報を系列ごとに定義していきます。
# [B] Chartオブジェクトに系列データを設定する -----------------------------------------------
# 系列ごとに参照情報を取得する
for i in range(2, 5):
# "i"列, 5行目-8行目
xvalues = Reference(ws, min_col=i, max_col=i, min_row=5, max_row=8) # X軸のデータ
# "i+4"列, 4行目-8行目
yvalues = Reference(ws, min_col=i+4, max_col=i+4, min_row=4, max_row=8) # Y軸のデータ
# "i+8"列, 4行目-8行目
zvalues = Reference(ws, min_col=i+8, max_col=i+8, min_row=5, max_row=8) # バブルの大きさ
# 個々の系列データをSeriesオブジェクト(3次元)として定義する
series = Series(values=yvalues, xvalues=xvalues, zvalues=zvalues, title_from_data=True)
# Seriesコレクションに系列データを追加していく
c1.series.append(series)
#---------------------------------------------
# <List3>へ続く
系列ごとに Seriesオブジェクト を定義する必要があるので、For文によって系列数ぶん繰り返します。今回は3つの系列を定義するので3回、以降の処理を行います。
そして、series属性 でSeriesコレクションを取得し、 append()メソッド で系列を追加します。
<List2>までの実行結果を図10に示します。先の<List1>で指定した style属性 だけでも綺麗なバブルチャートの描画ができました。
style属性 の補足として、スタイルの適用例を紹介します。
バブルの「色合い」は次の8種類の中から選択できます。(図11)
また、「その他の効果」については、次の6種類の中から選択できます。(図12)
もちろん、style属性で設定する方法以外にも、ユーザー独自スタイルを定義・適用することもできます。次の<4.3項>では、系列ごとに書式を設定する例を紹介します。
4.3 系列の書式(塗り潰し)を設定する【Step.3】
<List2>に続き<List3>を追加して下さい。<List3>では系列に書式を設定しています。ここでは、バブルに「立体(3D)効果」や「塗りつぶし(グラデーション)」を施す例を紹介します。
# [C] 系列ごとに設定する ------------------------------------------------
# 系列2に対する設定
ser2 = c1.series[1]
ser2.bubble3D = True # 3D表記にする
# 系列3に対する設定
ser3 = c1.series[2]
ser3.bubble3D = True # 3D表記にする
# グラデーション効果を適用する
# 色の階調をGradientStopオブジェクトとして定義する(色の指定としきい値)
gs1 = GradientStop(pos=20000, prstClr="medVioletRed")
gs2 = GradientStop(pos=60000, prstClr="aquamarine")
gs3 = GradientStop(pos=90000, prstClr="cornflowerBlue")
# グラデーション効果をGradientFillPropertiesオブジェクトとして定義する
gfProp = GradientFillProperties() # オブジェクトを取得
gfProp.stop_list = [gs1, gs2, gs3] # stop_list属性で色階調の定義情報を渡す
gfProp.linear = LinearShadeProperties(90) # グラデーションを線形変化
# 以下のようにクラスの引数に直接渡たすこともできる
#gfProperty = GradientFillProperties(gsLst=[gs1, gs2, gs3], lin=LinearShadeProperties(90))
# gradFill属性で、GradientFillPropertiesオブジェクトを設定する(グラデーションの適用)
series.graphicalProperties.gradFill = gfProp
# Chartオブジェクトをシートに追加して保存 -----------------------------------------
ws.add_chart(c1, "D12")
wb.save("Bubble_Sample.xlsx")
「GradientStopクラス」と「GradientFillPropertiesクラス」の書式については下記を参照下さい。
そして、最後に28行目の gradFill属性 で定義したグラデーション情報を適用させます。
サンプルコードの解説は以上となります。<List1>~<List3>を連結させて、実行した結果は図13のようになりました。系列2,3のバブルが立体表現となり、かつグラデーションが適用されています。
5. まとめ
いかがでしたでしょうか?
Excelを操作する外部ライブラリ「openpyxl」を使用して、散布図・バブルチャートの作成手順を実例を交えて紹介しました。
前回のクラスやオブジェクトの説明だけでは分かりずらい点もあったかもしれませんが、実際のコードを読むことで理解が深まったと感じて頂けたのではないでしょうか。
また、グラフ作成のコードにはある程度パターンがあるので紹介したサンプルコードを参考にアレンジしてみて下さい。
ここまでの内容をまとめておきましょう。
➀. 散布図・バブルチャートといったような系列ごとに異なる項目(カテゴリ)を設定できるタイプのグラフは、系列ごとに Seriesオブジェクト を定義してChartオブジェクトに追加する。
- 散布図は「ScatterChartクラス」からChartオブジェクトを取得
- バブルチャートは「BubbleChartクラス」からChartオブジェクトを取得
➁. 散布図は、クラスの 引数:scatterStyle ではグラフの形態をすることができないため、系列「Seriesオブジェクト」ごとに手動で設定する必要がある。
➂. バブルチャートに3D効果を適用する場合は、クラスの 引数:bubble3D では反映できないため、「Seriesオブジェクト」の同名の引数で設定する。
Excelのグラフには、このほかにもさまざまなタイプがあります。
たとえば、広く一般的に使われる「折れ線グラフ」や「棒グラフ」などがあります。次の関連記事も参考にして下さい。
またの機会に、その他タイプの紹介もしていけたらと思いますので、どうぞお楽しみに!
最後までお付き合い頂き、ありがとうございました。