Pythonのmatplotlibパッケージを使って棒グラフを書くのは割と簡単にできますが、データ系列が複数になったときの棒グラフってどうやって描くんだろうと思ったことはありませんか?
棒グラフのデータ系列が複数になると、1つの横軸目盛の上に複数のバーを描かなければならなくて、このときバーの横方向の位置計算を自分でするのが面倒ですよね。
この記事では、そんな初心者の方向けに、Pythonのmatplotlibパッケージを使って複数系列の棒グラフを描く方法を最新の情報をもとに丁寧に解説していきます。紹介する方法では、データ系列の数が増減してもコードの修正が不要になっています。
ワンステップずつコードを解説しているのでどなたでもPythonで複数系列の棒グラフが描けるようになります!
是非、この機会にmatplotlibパッケージを使った棒グラフにトライしてみましょう。
準備
Pythonの実行環境の構築やmatplotlibパッケージのインストールがまだの方は以下の記事を参考にしてPythonの実行環境の構築とmatplotlibのインストールを済ませてください。
複数系列の棒グラフの描画
複数系列の棒グラフの描き方を解説していきます。
全体のコード
上記グラフを作成するためのコードは以下になります。
ここでは、スマホやパソコンなどの物品の所有率を各層ごとに表した棒グラフを作成しています。データはすべて架空のものです。次節以降で、以下のコードをワンステップずつ解説していきます。
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib import rcParams as rcp
# フォントを設定する。
rcp['font.family'] = 'sans-serif'
rcp['font.sans-serif'] = ['Hiragino Maru Gothic Pro', 'Yu Gothic', 'Meirio', 'Takao', 'IPAexGothic', 'IPAPGothic', 'VL PGothic', 'Noto Sans CJK JP']
# カラーマップを用意する。
cmap = plt.get_cmap("tab10")
# データを用意する。
labels_series = ["M1層", "M2層", "F1層", "F2層"]
labels_data = ["パソコン", "スマホ", "時計", "車"]
data = [
[54, 85, 63, 32],
[58, 90, 75, 40],
[45, 87, 84, 25],
[48, 94, 88, 29]
]
# Figureを作成する。
fig = plt.figure()
# Axesを作成する。
ax = fig.add_subplot(111)
# Figureの解像度と色を設定する。
fig.set_dpi(150)
fig.set_facecolor("whitesmoke")
# Axesのタイトルと色を設定する。
ax.set_title("物品の所有率")
ax.set_facecolor("whitesmoke")
# x軸とy軸のラベルを設定する。
ax.set_xlabel("物品")
ax.set_ylabel("所有率")
# x軸の目盛のラベルの位置を変数xで保持する。
x = np.arange(len(labels_data))
# x軸の目盛の位置を設定する。
ax.xaxis.set_major_locator(mpl.ticker.FixedLocator(x))
# x軸の目盛のラベルを設定する。
ax.xaxis.set_major_formatter(mpl.ticker.FixedFormatter(labels_data))
# y軸の範囲を設定する。
ax.set_ylim(0, 100)
# y軸の目盛の位置を設定する。
ax.yaxis.set_major_locator(mpl.ticker.MultipleLocator(10))
# y軸の目盛のラベルを設定する。
ax.yaxis.set_major_formatter(mpl.ticker.PercentFormatter())
# 棒の幅を変数widthで保持する。
width = (1 - .2) / len(data)
# 棒グラフのオブジェクトのリストを変数barsで保持する。
bars = []
# データを描画する。
for i in range(len(data)):
bars.append(ax.bar(x + (i - .5 * (len(data) - 1)) * width, data[i], width, label = labels_series[i], color=cmap(i)))
# データ値を表示する。
for bar in bars:
ax.bar_label(bar)
# グリッドを表示する。
ax.set_axisbelow(True)
ax.grid(True, "major", "y", linestyle="--")
# 凡例を表示する。
ax.legend(loc="upper left")
# グラフを表示する。
plt.show()
各部のコード
パッケージのインポート
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib import rcParams as rcp
必要なパッケージとモジュールをインポートしています。ここでは、数値計算のためのnumpyパッケージ、グラフ描画のためのmatplotlibパッケージとpyplotモジュール、日本語を表示するためのrcParamsモジュールをインポートしています。
日本語フォントの設定
# フォントを設定する。
rcp['font.family'] = 'sans-serif'
rcp['font.sans-serif'] = ['Hiragino Maru Gothic Pro', 'Yu Gothic', 'Meirio', 'Takao', 'IPAexGothic', 'IPAPGothic', 'VL PGothic', 'Noto Sans CJK JP']
日本語を表示するためのフォントを設定しています。以下のyniji様の記事を参考にさせていただきました。上記コードを挿入するだけでグラフの中に日本語を表示することができるようになります。
カラーマップの用意
# カラーマップを用意する。
cmap = plt.get_cmap("tab10")
「tab10」という名前のカラーマップを用意しています。このカラーマップを棒グラフの各データ系列の表示に使用します。以下のskotaro様の記事を参考にさせていただきました。
データの用意
# データを用意する。
labels_series = ["M1層", "M2層", "F1層", "F2層"]
labels_data = ["パソコン", "スマホ", "時計", "車"]
data = [
[54, 85, 63, 32],
[58, 90, 75, 40],
[45, 87, 84, 25],
[48, 94, 88, 29]
]
グラフに描画するデータを用意しています。物品「パソコン」「スマホ」「時計」「車」の所有率を層「M1層」「M2層」「F1層」「F2層」ごとに棒グラフにします。変数labels_seriesが各層を表すラベルのリストを保持し、変数labels_dataが各物品を表すラベルのリストを保持しています。また、変数dataが各層ごとの各物品の所有率を保持しています。このデータを自身で用意したデータに置き換えることで自身のグラフを作成することができます。
FigureとAxesの作成
# Figureを作成する。
fig = plt.figure()
# Axesを作成する。
ax = fig.add_subplot(111)
グラフを描画するためには、最初にFigureとAxesを作成する必要があります。24行目でFigureを作成し、26行目でAxesを作成しています。Figureはグラフを貼るための台紙で、Axesはグラフ本体といったイメージです。
Figureの解像度と色
# Figureの解像度と色を設定する。
fig.set_dpi(150)
fig.set_facecolor("whitesmoke")
Figure(台紙です)の解像度(DPI)と色を設定してます。ここでは、Figure(台紙です)の解像度(DPI)に「150」を設定し、色に「whitesmoke」(Whitesmoke)に指定しています。
グラフを構成する各部位の色の選択肢は以下のページに掲載されています。
Axesのタイトルと色
# Axesのタイトルと色を設定する。
ax.set_title("物品の所有率")
ax.set_facecolor("whitesmoke")
Axes(グラフ本体です)のタイトルと色を設定しています。ここでは、Axes(グラフ本体です)のタイトルに「物品の所有率」を設定し、色に「whitesmoke」(Whitesmoke)を指定しています。Axes(グラフ本体です)の色は、Figureの色を設定したときと同じようにいろいろな色を選択することができます。
x軸とy軸のラベル
# x軸とy軸のラベルを設定する。
ax.set_xlabel("物品")
ax.set_ylabel("所有率")
\(x\)軸と\(y\)軸のラベルを設定しています。ここでは、\(x\)軸のラベルに「物品」を、\(y\)軸のラベルに「所有率」を設定しています。
x軸の目盛の位置とラベル
# x軸の目盛のラベルの位置を変数xで保持する。
x = np.arange(len(labels_data))
\(x\)軸の目盛の位置を変数xで保持します。中身は、リスト[0, 1, 2, 3]です。棒グラフでは、\(x\)軸の目盛の位置は左から0, 1, 2, 3,・・・と指定できるようです。
# x軸の目盛の位置を設定する。
ax.xaxis.set_major_locator(mpl.ticker.FixedLocator(x))
# x軸の目盛のラベルを設定する。
ax.xaxis.set_major_formatter(mpl.ticker.FixedFormatter(labels_data))
\(x\)軸の目盛の位置とラベルを設定しています。ここでは、\(x\)軸の目盛の位置をFixedLocatorというロケータを使って設定しています。FixedLocatorロケータは、目盛の位置をリストで指定することができます。また、\(x\)軸の目盛のラベルをFixedFormatterというフォーマッタを使って設定しています。FixedFormatterフォーマッタは、目盛のラベルをリストで指定することができます。
y軸の範囲と目盛の位置とラベル
# y軸の範囲を設定する。
ax.set_ylim(0, 100)
# y軸の目盛の位置を設定する。
ax.yaxis.set_major_locator(mpl.ticker.MultipleLocator(10))
# y軸の目盛のラベルを設定する。
ax.yaxis.set_major_formatter(mpl.ticker.PercentFormatter())
\(y\)軸の目盛の範囲と位置とラベルを設定しています。ここでは、目盛の範囲に「0から100」を、位置をMultipleLocatorというフォーマッタを使って「10」ごとに、ラベルをPercentFormatterというフォーマッタを使ってパーセント表記に設定しています。
データの描画
# 棒の幅を変数widthで保持する。
width = (1 - .2) / len(data)
# 棒グラフのオブジェクトのリストを変数barsで保持する。
bars = []
# データを描画する。
for i in range(len(data)):
bars.append(ax.bar(x + (i - .5 * (len(data) - 1)) * width, data[i], width, label = labels_series[i], color=cmap(i)))
データを描画しています。ここのコードが当記事のポイントになります。56行目の「.2」の値を0から1の間で調節することで棒グラフのバーの太さを変更することができます。1に近づければバーは細くなり0に近づければバーは太くなります。また、61行目では棒グラフのバーをAxesクラスのbarメソッドで描画しています。この部分は、棒グラフのデータ系列の増減に対応するコードになっています。Axesクラスのbarメソッドの呼び出しではキーワード引数labelでデータ系列のラベルを設定しキーワード引数colorでデータ系列のカラーマップを指定しています。キーワード引数labelで渡したデータ系列のラベルは凡例の表示で使われます。なお、Axesクラスのbarメソッドの返り値を変数barsへ格納しておき、この次の項でデータ値を描画する際に使用します。
データ値
# データ値を表示する。
for bar in bars:
ax.bar_label(bar)
各データの値を、Axesクラスのbar_labelメソッドに変数barsの各要素を与えて棒グラフのバーの上に描画しています。
グリッド
# グリッドを表示する。
ax.set_axisbelow(True)
ax.grid(True, "major", "y", linestyle="--")
\(y\)軸の目盛に対応したグリッド線を設定しています。ここでは、グリッド線として「--」(破線)を指定しています。他の選択肢は、以下のページに掲載されています。破線の他に実線や点線や一点鎖線を選択することができます。
また、68行目ではグリッド線を棒グラフのバーの手前ではなく後ろに引くよう設定しています。
凡例
# 凡例を表示する。
ax.legend(loc="upper left")
凡例を設定しています。ここでは、凡例の位置として「upper left」(左上)を指定しています。凡例の位置は、「upper left」(左上)以外にもいろいろ選択できます。他の選択肢は、以下のnkay様の記事が参考になります。
グラフの表示
# グラフを表示する。
plt.show()
最後に、グラフを表示しています。
データ系列の追加
# データを用意する。
labels_series = ["T層", "M1層", "M2層", "F1層", "F2層"]
labels_data = ["パソコン", "スマホ", "時計", "車"]
data = [
[25, 75, 58, 12],
[54, 85, 63, 32],
[58, 90, 75, 40],
[45, 87, 84, 25],
[48, 94, 88, 29]
]
ここでは、上記のようにグラフのデータ系列に「T層」を追加してみます。
前章で紹介したコードは、用意するデータを変更するだけでデータ系列を追加することができます。以下のように\(x\)軸の各目盛の上にT層のバーが追加されその分各バーの幅が細くなっているのがわかると思います。
pandasパッケージを使った棒グラフの描画
pandasパッケージを使っても棒グラフを描画することができます。pandasパッケージは、データ分析を行うためのライブラリです。pandasパッケージを使うとバーの横方向の位置を計算するコードを自前で用意する必要がなくなります。
pandasパッケージのインストール
pandasパッケージを使うには、以下のコマンドをシェル上(Windowsならコマンドプロンプト)で実行してpandasパッケージをインストールする必要があります。
pip install pandas
全体のコード
以下がpandasパッケージを使ったときのコードになります。
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib import rcParams as rcp
# フォントを設定する。
rcp['font.family'] = 'sans-serif'
rcp['font.sans-serif'] = ['Hiragino Maru Gothic Pro', 'Yu Gothic', 'Meirio', 'Takao', 'IPAexGothic', 'IPAPGothic', 'VL PGothic', 'Noto Sans CJK JP']
# カラーマップを用意する。
cmap = plt.get_cmap("tab10")
# データを用意する。
data = {
"物品": ["パソコン", "スマホ", "時計", "車"],
"T層": [25, 75, 58, 12],
"M1層": [54, 85, 63, 32],
"M2層": [58, 90, 75, 40],
"F1層": [45, 87, 84, 25],
"F2層": [48, 94, 88, 29]
}
# Figureを作成する。
fig = plt.figure()
# Axesを作成する。
ax = fig.add_subplot(111)
# Figureの解像度と色を設定する。
fig.set_dpi(150)
fig.set_facecolor("whitesmoke")
# Axesのタイトルと色を設定する。
ax.set_title("物品の所有率")
ax.set_facecolor("whitesmoke")
# データを描画する。
df = pd.DataFrame(data)
df.plot(ax=ax, kind="bar", rot=0)
#df.plot(ax=ax, kind="bar", rot=0, colormap=cmap)
# データ値を表示する。
for c in ax.containers:
ax.bar_label(c)
# x軸とy軸のラベルを設定する。
ax.set_xlabel("物品")
ax.set_ylabel("所有率")
# x軸の目盛の位置を設定する。
ax.xaxis.set_major_locator(mpl.ticker.FixedLocator(np.arange(len(data["物品"]))))
# x軸の目盛のラベルを設定する。
ax.xaxis.set_major_formatter(mpl.ticker.FixedFormatter(data["物品"]))
# y軸の範囲を設定する。
ax.set_ylim(0, 100)
# y軸の目盛の位置を設定する。
ax.yaxis.set_major_locator(mpl.ticker.MultipleLocator(10))
# y軸の目盛のラベルを設定する。
ax.yaxis.set_major_formatter(mpl.ticker.PercentFormatter())
# グリッドを表示する。
ax.set_axisbelow(True)
ax.grid(True, "major", "y", linestyle="--")
# 凡例を表示する。
ax.legend(loc="upper left")
# グラフを表示する。
plt.show()
変更したコード
前章から変更したコードは5箇所あります。
パッケージのインポート
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib import rcParams as rcp
pandasパッケージを追加でインポートします。
データの用意
# データを用意する。
data = {
"物品": ["パソコン", "スマホ", "時計", "車"],
"T層": [25, 75, 58, 12],
"M1層": [54, 85, 63, 32],
"M2層": [58, 90, 75, 40],
"F1層": [45, 87, 84, 25],
"F2層": [48, 94, 88, 29]
}
データを用意するコードです。\(x\)軸と\(y\)軸のラベルとデータをまとめて定義することが可能です。
データの描画
# データを描画する。
df = pd.DataFrame(data)
df.plot(ax=ax, kind="bar", rot=0)
#df.plot(ax=ax, kind="bar", rot=0, colormap=cmap)
データを描画するコードです。前章ではバーの横方向の位置を計算するコードが必要でしたが、pandasパッケージのDataFrameクラスを使う場合はそういった計算をすべてこのクラスに任せることが可能です。
ただ、現時点ではカラーマップを指定する方法がわかりませんでした。正確に言うと、DataFrameクラスのplotメソッドのキーワード引数colormapでカラーマップの指定ができるのですが、実際に試したところなぜか期待した結果にはなりませんでした。40行目のようにカラーマップを指定すると、カラーマップ内の色が連続して使われずに以下のように1個おきに使われてしまうようです。
<2024/03/31追記>
当記事をご覧になられた方からご連絡があり再度調べなおしたところ、以下のことがわかりました。
- DataFrameクラスのplotメソッドは、グラフに使うカラーマップをキーワード引数colormapで指定されると、そのカラーマップに含まれている色の数がグラフで使用する色の数より多い場合、カラーマップから「均等」に「飛び飛びで」取り出した色を使ってグラフを描画している。そのため、色合いの悪いグラフが出来上がってしまっていた。
- DataFrameクラスのplotメソッドを呼び出す前に以下のコードを挿入して39行目をコメントアウトし40行目のコメントを外してキーワード引数colormapにはsliced_cmapを与えるとことで期待通りのグラフが表示できるようになる。
sliced = []
for i in range(len(data) - 1):
sliced.append(cmap(i))
sliced_cmap = mpl.colors.ListedColormap(sliced)
データ値
# データ値を表示する。
for c in ax.containers:
ax.bar_label(c)
各データの値を、Axesクラスのbar_labelメソッドにAxesクラスのインスタンス変数containersの各要素を与えて棒グラフの各バーの上に描画しています。
x軸の目盛の位置とラベル
# x軸の目盛の位置を設定する。
ax.xaxis.set_major_locator(mpl.ticker.FixedLocator(np.arange(len(data["物品"]))))
# x軸の目盛のラベルを設定する。
ax.xaxis.set_major_formatter(mpl.ticker.FixedFormatter(data["物品"]))
\(x\)軸のラベルのリストが変数labels_dataから変数dataに移動したので\(x\)軸の目盛の位置とラベルを設定するコードも変更になります。
作成したグラフの保存
作成したグラフは、表示された画面の左下に並んでいるボタンのうち保存ボタンをクリックすることで画像ファイルとして保存することができます。
以上、「Python初心者でも簡単!matplotlibを使った複数系列の棒グラフの描き方を徹底解説」でした。
コメント