リテールテックのアドベントカレンダー2021(笑)にのせているというところからお分かりいただけるように、私は小売業に勤務しています。
現在、経営企画本部にて会社全体の数値管理やデータ分析を主体とした業務をしているのですが、より高いレベルでの経営に向けて、現在機械学習を学んでいます。その過程でちょっとしたアウトプットをしてみたので記事にしてみます。
あくまで 小売、特に経営企画的な視点で機械学習を考えるとこうなる という記事なので、技術的詳細は既に世の中に出回っている記事を参照いただくことをお勧めします。
営業利益黒字を達成するための決定木
字が小さいですね。
読めてしまってはここに載せられないので、あえて小さいです。
なぜ決定木なのか
解釈性が高く経営判断として活用しやすい ためです。
需要・客数予測のように実務に近い領域では正確性が重要ですが、会社の意思決定として活用するためには、なぜそのような結果となるかを理解することが重要となります。
例えば、
- 営業総利益率が30%を下回る店舗はすべて赤字となっている。
- 営業総利益率が30%以上の店舗でも、人件費率が8%を超える店舗は8割が赤字となっている
- 人件費率8%を超える赤字店舗の内、販促費が10%を下回る店舗は赤字
となれば、まずは商品の利益率と店舗の管理レベルを上げることが優先で、人件費の削減は必要な利益が取れるようになってからという経営判断ができます。
また、人件費削減に合わせて販促費まで削ると、トップラインが下がり赤字になっているのではないかという可能性も考えられます。
あくまでも一例です。今回の決定木がこのような結果となったわけではないです。
決定木の結果は・・・すみません。企業秘密です。
従来このような判断は、エクセルとにらめっこしたり、ひどいときは感覚に任されたりといった状況でしたが、機械学習という一つの基準を取り入れることで、判断のブレを少なくすることに繋がるとかんがえました。
今回目指したこと
店の営業利益黒字要因を機械学習の視点で分析することで新たな示唆を得る
パラメーターとしては2021年の各店舗における売上や人件費を入れていきます(後述)。
実行環境
Python 3.9.7
IPython 7.28.0
jupyterlab 3.1.14
ライブラリ
scikit-learn 1.0.1
matplotlib 3.4.3
pandas 1.3.4
入力したパラメーター
目的変数
- OP: 営業利益(円)
説明変数
- population: 5km 内人口(人)
- household: 5km圏内 世帯数
- sales: 21年度直営売上高(円)
- gross_OP: 営業総利益(円)
- labor_cost: 人件費(円)
- SGA: 販売費一般管理費(円)
- area: 直営売場面積(m^2)
- age: 店舗年齢(開店から何年たったか。10年刻み)
- in_out: 流入・流出(お盆・年末年始で客数が増えるか減るか)
- cluster: 立地(アーバン、準アーバン、ローカル)
age, in_out, cluster はカテゴリー変数としてダミー化
店別P/L数値から、機械学習により黒字化するための全体のあるべき売上・経費構造を明確化していきます。
コード(あんまり参考になりませんが)
対話型のjupyterlabで動かしているので、コピペでは動かないです。
何をしたのかを見ていただければよろしいかと思います。
import os
import io
import pandas as pd
import graphviz
import pydotplus
import matplotlib.pyplot as plt
import japanize_matplotlib
df = pd.read_excel('../data/df.xlsx')
# 実行しているipynbファイルをsrcディレクトリ、データをdataディレクトリに入れてます。
# df.xlsxは事前にpandasで整理したデータをexcelにおとしておいたもの
df.columns
# Index(['Unnamed: 0', 'code', 'store_name', 'population', 'household', 'format', 'type', 'corp', 'in_out', 'cluster', 'age', 'sales', 'gross_OP', 'labor_cost', 'SGA', 'OP', 'area'], dtype='object')
df_tree = df[['population', 'household', 'in_out', 'cluster', 'age', 'sales', 'gross_OP', 'labor_cost', 'SGA', 'area', 'OP']]
# 学習データとして使う変数のみ抜粋
df_tree['age'] = pd.cut(df_tree['age'], [0,10,20,30,50], labels = ['0_10', '11_20' ,'21_30', '30_over'])
# 数量データとなっている店舗年齢をカテゴリー化
df_tree['in_out'] = df_tree['in_out'].replace({'流入':'in', '流入・流出':'both', '流出':'out'})
df_tree['cluster'] = df_tree['cluster'].replace({'準Urban':'Semi_urban'})
# 日本語が嫌なので修正
df_tree = pd.get_dummies(df_tree, drop = True)
# ダミー変数化
x = df_tree.drop(columns = 'OP')
y = df_tree['OP']
# 説明変数、目的変数を設定
from sklearn import tree
model = tree.DecisionTreeClassifier(max_depth=3, random_state=0)
clf = model.fit(x,y)
# scikit-learnで決定木を作成
plt.figure(figsize=(10,10)) # set plot size (denoted in inches)
tree.plot_tree(clf)
plt.show()
# matplotlibで決定木を描画
今後の方針
見ていただければわかるように初心者です。
引き続き、勉強しながら頑張ります。
今回は売上、人件費、・・・等実数値でのモデル作成を試みました。
経営判断に用いる、P/L構造を見る、という目的であれば売上を説明変数から除外し、人件費売比(人件費 / 売上高)等各細目の売比でモデル化した方が良いかもしれません。
また、今回は企業で展開する店舗フォーマットのうちの一つについて作成したので、その他のフォーマットについてもモデル化を進めていきます。
本来であればモデル評価が必要ですが、フォーマットごとにわけたことで必要なデータ量が取れない可能性を考えて、学習データとテストデータを分けていません。