はじめに
製造業でよく使われるQC七つ道具の一つであるパレート図と呼ばれるものを作成してみようと思います。
パレート図とは、値が降順にプロットされた棒グラフとその累積構成比を表す折れ線グラフを組み合わせた複合グラフのことです。
品質管理において、欠陥の最も一般的な原因、最も起こりやすい欠陥の種類などを見つけ出すために使われます。
Excelでも一発でパレート図を作成できますが、なんか見栄えが悪かったり、見栄えを整えようとすると意外と大変だったりするので、pythonで作成してみます
関数化して一瞬でグラフが作れるようにしたいと思います。
データの準備
プロット用のデータを準備しておきます。
以下の表のデータをサンプルとして使用しますので、data.csvとして保存しておきます。
ラベル | カウント |
---|---|
APPLE | 20 |
BANANA | 40 |
CACAO | 80 |
DURIAN | 5 |
EGG PLANT | 15 |
データの加工
データの読み込みを行い、パレート図プロット用に、データを少々加工します
行うのは下記の2点。
- カウントで降順に並べ替え
- カウントの累積割合を算出
import numpy as np import pandas as pd data = pd.read_csv('data.csv', encoding='shift-jis') data = data.sort_values(by='カウント', ascending=False) sum_data = data['カウント'].sum() data['accum'] = np.cumsum(data['カウント']) # 累積を算出 data['accum_ratio'] = data['accum'] / sum_data * 100 # 累積割合を算出
加工後のデータは以下のようになります。
ラベル | カウント | accum | accum_ratio | |
---|---|---|---|---|
2 | CACAO | 80 | 80 | 50.000 |
1 | BANANA | 40 | 120 | 75.000 |
0 | APPLE | 20 | 140 | 87.500 |
4 | EGG PLANT | 15 | 155 | 96.875 |
3 | DURIAN | 5 | 160 | 100.000 |
パレート図のプロット
まずは何も考えずそのままプロットしてみます。
左右に2つの軸のあるグラフを作る際はax.twinx()を使用します。
fig, ax = plt.subplots()
で作成したaxに対し、twin_ax = ax.twinx()
とすることで、x軸共通でy軸のみ異なるグラフを作成できます。
import matplotlib.pyplot as plt import seaborn as sns sns.set() plt.rcParams["font.family"] = "IPAexGothic" labels = len(data) fig, ax = plt.subplots() ax.bar(data['ラベル'], data['カウント'], label='カウント') ax.set_xlabel("ラベル") ax.set_ylabel("カウント") ax.set_ylim([0, sum_data]) ax.grid(axis='x') # yのグリッドは邪魔なので消しておく twin_ax=ax.twinx() twin_ax.plot(range(labels), data['accum_ratio'], marker='o', color='orange', label='累計比率') twin_ax.set_ylabel('累計比率') twin_ax.set_ylim([0, 100]) twin_ax.grid(False) # 棒グラフの上からグリッドができてしまうので第2Y軸の方のグリッドは消しておく fig.legend(loc="center right", bbox_to_anchor=(1,0.5), bbox_transform=ax.transAxes) plt.tight_layout() plt.show()
結果はこちら。
見栄えを整える
もう少し見やすくしたいと思います。
折れ線グラフと棒グラフの位置関係の整理
以下2点変更します
- 折れ線グラフは0%始まりにする
- 棒グラフの右上の角と折れ線グラフのマーカーが交差するように変更する
accum_data = [0] + data['accum_ratio'].tolist() # 累計比率のデータの先頭に0追加 fig, ax = plt.subplots() bar = ax.bar(range(labels), data['カウント'], align="edge", width=1, label='カウント') ax.set_ylim([0, sum_data]) ax.set_xlim([0, labels]) ax.set_xticks([0.5 + i for i in range(labels)]) # 棒グラフの真ん中にX軸ラベルが来るように変更 ax.set_xticklabels(data['ラベル'].tolist(),rotation = -90) # 棒グラフの真ん中にX軸ラベルが来るように変更 ax.grid(axis='x') ax.set_xlabel("ラベル") ax.set_ylabel("カウント") twin_ax=ax.twinx() twin_ax.plot(range(labels + 1), accum_data, marker='o', color='orange', label='累計比率', linewidth=2.0) twin_ax.set_ylabel('累計比率') twin_ax.set_ylim([0, 100]) twin_ax.grid(False) fig.legend(loc="center right", bbox_to_anchor=(1,0.5), bbox_transform=ax.transAxes) plt.tight_layout() plt.show()
結果はこちら。
棒グラフの右上の角と折れ線グラフがきれいに交差するようになりましたね。
左右の軸のグリッドを合わせる
どちらも10等分して目盛り線を合わせます。
percent_labels = [str(i) + "%" for i in np.arange(0, 100+1, 10)] # 累計比率表示用リスト accum_data = [0] + data['accum_ratio'].tolist() fig, ax = plt.subplots() bar = ax.bar(range(labels), data['カウント'], align="edge", width=1, label='カウント') ax.set_ylim([0, sum_data]) ax.set_xlim([0, labels]) ax.set_xticks([0.5 + i for i in range(labels)]) ax.set_xticklabels(data['ラベル'].tolist(),rotation = -90) ax.set_yticks(np.arange(0, sum_data+1, sum_data / 10)) # 第1Y軸を10等分して表示 ax.grid(axis='x') ax.set_xlabel("ラベル") ax.set_ylabel("カウント") twin_ax=ax.twinx() twin_ax.plot(range(labels + 1), accum_data, marker='o', color='orange', label='累計比率', linewidth=2.0) twin_ax.set_ylabel('累計比率') twin_ax.set_ylim([0, 100]) twin_ax.set_yticks(np.arange(0, 100+1, 10)) # 第2Y軸を10等分して表示 twin_ax.set_yticklabels(percent_labels) twin_ax.grid(False) fig.legend(loc="center right", bbox_to_anchor=(1,0.5), bbox_transform=ax.transAxes) plt.tight_layout() plt.show()
棒グラフの左右に補助線追加
折れ線との関係がわかりやすいように棒グラフの左右にグリッド追加します。
minorのgridを使用して補助線を入れます。
percent_labels = [str(i) + "%" for i in np.arange(0, 100+1, 10)] accum_data = [0] + data['accum_ratio'].tolist() fig, ax = plt.subplots() bar = ax.bar(range(labels), data['カウント'], align="edge", width=1, label='カウント') ax.set_ylim([0, sum_data]) ax.set_xlim([0, labels]) ax.set_xticks([0.5 + i for i in range(labels)]) ax.set_xticks([1 + i for i in range(labels)], minor=True) #副目盛り ax.set_xticklabels(data['ラベル'].tolist(),rotation = -90) ax.set_yticks(np.arange(0, sum_data+1, sum_data / 10)) ax.grid(axis='x') ax.grid(axis='x', ls=':', which='minor') #副目盛り ax.set_xlabel("ラベル") ax.set_ylabel("カウント") twin_ax=ax.twinx() twin_ax.plot(range(labels + 1), accum_data, marker='o', color='orange', label='累計比率', linewidth=2.0) twin_ax.set_ylabel('累計比率') twin_ax.set_ylim([0, 100]) twin_ax.set_yticks(np.arange(0, 100+1, 10)) twin_ax.set_yticklabels(percent_labels) twin_ax.grid(False) fig.legend(loc="center right", bbox_to_anchor=(1,0.5), bbox_transform=ax.transAxes) plt.tight_layout() plt.show()
関数化
一発でグラフが書けるように最後に関数化しておきます。
引数にはプロット用データのデータフレーム、ラベルの列名、カウントの列名を指定してグラフ表示まで行います。
import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns def pareto(df, label_name, cnt_name): ''' plot pareto chart input df: data for plotting (DataFrame) label_name: row name for label (str) cnt_name: row name for count (str) ''' sns.set() plt.rcParams["font.family"] = "IPAexGothic" label = df[label_name].tolist() cnt = df[cnt_name].tolist() data = pd.DataFrame(label, columns=['ラベル']) data['カウント'] = cnt data = data.sort_values(by='カウント', ascending=False) sum_data = data['カウント'].sum() data['accum'] = np.cumsum(data['カウント']) data['accum_ratio'] = data['accum'] / sum_data * 100 labels = len(data) percent_labels = [str(i) + "%" for i in np.arange(0, 100+1, 10)] accum_data = [0] + data['accum_ratio'].tolist() fig, ax = plt.subplots() bar = ax.bar(range(labels), data['カウント'], align="edge", width=1, label=cnt_name) ax.set_ylim([0, sum_data]) ax.set_xlim([0, labels]) ax.set_xticks([0.5 + i for i in range(labels)]) ax.set_xticks([1 + i for i in range(labels)], minor=True) #副目盛り ax.set_xticklabels(data['ラベル'].tolist(),rotation = -90) ax.set_yticks(np.arange(0, sum_data+1, sum_data / 10)) ax.grid(axis='x') ax.grid(axis='x', ls=':', which='minor') #副目盛り ax.set_xlabel(label_name) ax.set_ylabel(cnt_name) twin_ax=ax.twinx() twin_ax.plot(range(labels + 1), accum_data, marker='o', color='orange', label='累計比率', linewidth=2.0) twin_ax.set_ylabel('累計比率') twin_ax.set_ylim([0, 100]) twin_ax.set_yticks(np.arange(0, 100+1, 10)) twin_ax.set_yticklabels(percent_labels) twin_ax.grid(False) fig.legend(loc="center right", bbox_to_anchor=(1,0.5), bbox_transform=ax.transAxes) plt.tight_layout() plt.show() data = pd.read_csv('data.csv', encoding='shift-jis') pareto(data, 'ラベル', 'カウント')
おわりに
パレート図が一発で書けるようになりました。
毎回エクセルのグラフの設定に悩まなくて済みそうです。