はじめに

データ分析において対象となるデータを可視化することは重要だとしばしば言われます。数値の羅列を見ているだけではデータ間の関係性を視覚的に発見できることがあるからです。
データ可視化の練習として、以前Tommyさんのblog記事をご紹介しました。今回は、Excelでできるデータドリブン・マーケティングという本の第3章をPythonで実装してみます。

データの読み込み

この本には演習用のデータとして、エクセルのファイルが付いています。そのファイルをcsv形式にしてダウンロードし、保存します。ファイル名はalcohol.csvとしました。

import pandas as pd

df = pd.read_csv('alcohol.csv')

Google Colaboratoryを利用したい方は弊社のブログ記事、Google ColaboratoryでDriveからデータを落とす方法を参考にすると良いかもしれません。

# Google Colaboratory用
from google.colab import drive

drive.mount('/content/drive')
Mounted at /content/drive
# Google Colaboratory用
import pandas as pd

df = pd.read_csv('/content/drive/My Drive/DDM_chap3/alcohol.csv')

作成したデータフレームdfを確認してみると、NAのみの余分な行が含まれていたのでそれらを削除しました。

df.dropna(axis=0, how='any', inplace=True)

次に列名を英語にしました。

columns_list = ['date', 'amount', 'TVCM', 'paper', 'OOH', 'WEB']
df.columns = columns_list
df.head()
date amount TVCM paper OOH WEB
0 2015/9/7 5,554,981 58.86750 20340000.0 5890000.0 23,080
1 2015/9/14 6,071,669 235.18125 15470000.0 5740000.0 29,979
2 2015/9/21 5,798,657 252.18375 8325000.0 0.0 23,784
3 2015/9/28 6,235,157 75.25500 0.0 0.0 26,732
4 2015/10/5 6,861,105 0.00000 0.0 0.0 28,823

列名の意味は

date:日付

amount:缶アルコール飲料の売り上げ本数

TVCM:TVCMの延べ視聴率

paper:紙媒体の出稿金額

OOH:屋外広告や交通広告の出稿金額

WEB:WEB広告のクリック数

です。

plotメソッドとbarメソッドで折れ線グラフと棒グラフを作成

本の中でExcelを用いて行われている操作をPythonでやっていきましょう。可視化の際に用いられるライブラリとして有名なものの一つにmatplotlibがあります。苦手意識のある方はこちらの記事もあわせて読むと良いでしょう。barメソッドを用いて棒グラフを描く方法を紹介しています。

3-1. 折れ線グラフで各変数の形をチェック

まずは横軸が日付('date')、縦軸が缶アルコール飲料の売り上げ本数('amount')であるような折れ線グラフを描いてみます。ここでの注意点は、'amount'列の数値がカンマを含む文字列型になっていることです。

df.amount
0      5,554,981 
1      6,071,669 
2      5,798,657 
3      6,235,157 
4      6,861,105 
          ...    
100    9,382,929 
101    6,424,589 
102    5,308,052 
103    5,637,103 
104    6,250,997 
Name: amount, Length: 105, dtype: object

文字列型の列を数値にしたいときはastypeメソッドを使うと良いのですが、カンマが含まれているとうまくいきません。例えば今回、df.amount.astype('int')とするとValueError: could not convert string to float: '5,554,981 'と出力されてしまいます。そこで、まずapplyメソッドを用いて、カンマを空白に置き換えます。以下を参考にしました。

Pythonで数字の文字列strを数値int, floatに変換

df.amount = df.amount.apply(lambda x: x.replace(',','')).astype('int')  # カンマを空白に置き換え、str型からint型に
import matplotlib.pyplot as plt

plt.figure(figsize=(25,10))
plt.plot(df['date'], df['amount'] )

このままでは横軸の文字が小さくて見えないので、一部だけ表記することにしました。

xlist = df.date[::10]  # date列の日付を10個刻みで取り出す

plt.figure(figsize=(25,10))
plt.plot(df['date'], df['amount'])
plt.xticks(ticks=xlist, labels=xlist)  # 10週間隔で横軸の目盛りを表示

plt.show()

次に、横軸が日付('date')、縦軸がTVCMの延べ視聴率('TVCM')であるような棒グラフを描きます。

plt.figure(figsize=(25,10))
plt.bar(df['date'], df['TVCM'], color='orange') 
plt.xticks(ticks=xlist, labels=xlist)  

plt.show()

折れ線グラフと棒グラフをそれぞれ描くことができました。次に二つのグラフを一つにまとめます。

#xlist = df.date[::10]  # 既出

plt.figure(figsize=(25,10))
plt.plot(df['date'], df['amount'])
plt.bar(df['date'], df['TVCM'], color='orange') 
plt.xticks(ticks=xlist, labels=xlist)  

plt.show()

これでは棒グラフが見えなくなってしまいます。そこで左右で異なる縦軸を用意し、グラフを描画します。以下を参考にしました。

Matplotlib-2軸グラフの書き方
Pythonで棒グラフと折れ線グラフを重ねて2軸グラフを作成

ax とは何だ、という方は以下もご覧になると良いかもしれません。

早く知っておきたかったmatplotlibの基礎知識、あるいは見た目の調整が捗るArtistの話

fig, ax1 = plt.subplots(figsize=(25,10))  # Figureオブジェクトとそれに属する一つのAxesオブジェクト

ax2 = ax1.twinx()  # twinx関数で二つのAxesオブジェクトを関連付け

# グラフの描画
ax1.plot(df['date'], df['amount'], label='amount')
ax2.bar(df['date'], df['TVCM'], color='orange', width=0.1, label='TVCM') 

# ラベル付けや凡例
ax1.set_xticks(xlist)  # グラフのラベル付け
ax1.set_xticklabels(xlist)  # x軸のラベル付け
#fig.legend(['amount', 'TVCM'], loc='best')  # 凡例
ax1.legend(loc=2)  # 左上に凡例
ax2.legend(loc=1)  # 右上に凡例
ax1.set_ylim([0, df['amount'].max()])  # 縦軸の範囲を調整
(0.0, 9382929.0)

3-2 基本統計量とヒストグラムを使ってデータの形をチェック

基本統計量

先ほどのデータの基本統計量を確認します。本ではamount列のみに注目していますが、ここではすべての列について求めてみましょう。describeメソッドを用いて得られる基本統計量を確認してみると次のようになります。

df.describe()
amount TVCM paper OOH
count 1.050000e+02 105.000000 1.050000e+02 1.050000e+02
mean 5.937047e+06 78.964643 8.101143e+06 3.136000e+06
std 9.661499e+05 101.784009 1.255423e+07 5.773868e+06
min 4.335326e+06 0.000000 0.000000e+00 0.000000e+00
25% 5.309995e+06 0.000000 0.000000e+00 0.000000e+00
50% 5.685093e+06 44.422500 7.175000e+06 0.000000e+00
75% 6.320607e+06 105.630000 1.327000e+07 5.740000e+06
max 9.382929e+06 419.028750 1.085300e+08 2.785000e+07

WEB列がないのは、数値ではなく文字列型になっているからです。amount列と同様に変換してから再度describeメソッドを使ってみましょう。

df.WEB = df.WEB.apply(lambda x: x.replace(',','')).astype('int') 
df.describe()
amount TVCM paper OOH WEB
count 1.050000e+02 105.000000 1.050000e+02 1.050000e+02 105.000000
mean 5.937047e+06 78.964643 8.101143e+06 3.136000e+06 37823.323810
std 9.661499e+05 101.784009 1.255423e+07 5.773868e+06 16465.047521
min 4.335326e+06 0.000000 0.000000e+00 0.000000e+00 17721.000000
25% 5.309995e+06 0.000000 0.000000e+00 0.000000e+00 28823.000000
50% 5.685093e+06 44.422500 7.175000e+06 0.000000e+00 33106.000000
75% 6.320607e+06 105.630000 1.327000e+07 5.740000e+06 39689.000000
max 9.382929e+06 419.028750 1.085300e+08 2.785000e+07 117831.000000

ここで得られた結果と本を比較してみると、不足しているのは標準誤差、最頻値、分散、尖度、歪度、範囲、合計です。これらも求めてみます。

import math
import pandas as pd

df1 = df.describe()

# 標準誤差
df_tmp = df1[2:3] / math.sqrt(len(df))
df_tmp.index = ['SE']
df1 = pd.concat([df1, df_tmp], axis=0)

# 分散
df_tmp = df1[2:3].apply(lambda x: x*x)
#df_tmp = pd.DataFrame(df.var(ddof=1)).T
df_tmp.index = ['var']
df1 = pd.concat([df1, df_tmp], axis=0)

# 尖度
df_tmp = pd.DataFrame(df.kurtosis()).T
df_tmp.index = ['kurtosis']
df1 = pd.concat([df1, df_tmp], axis=0)

# 歪度
df_tmp = pd.DataFrame(df.skew()).T
df_tmp.index = ['skew']
df1 = pd.concat([df1, df_tmp], axis=0)

# 範囲
df_tmp = pd.DataFrame(df1.apply(lambda x: x[7] - x[3])).T
df_tmp.index = ['range']
df1 = pd.concat([df1, df_tmp], axis=0)

# 合計
#columns_list = ['date', 'amount', 'TVCM', 'paper', 'OOH', 'WEB']  # 既出
df_tmp = pd.DataFrame(df[columns_list[1:]].sum()).T
df_tmp.index = ['sum']
df1 = pd.concat([df1, df_tmp], axis=0)
df1
amount TVCM paper OOH WEB
count 1.050000e+02 105.000000 1.050000e+02 1.050000e+02 1.050000e+02
mean 5.937047e+06 78.964643 8.101143e+06 3.136000e+06 3.782332e+04
std 9.661499e+05 101.784009 1.255423e+07 5.773868e+06 1.646505e+04
min 4.335326e+06 0.000000 0.000000e+00 0.000000e+00 1.772100e+04
25% 5.309995e+06 0.000000 0.000000e+00 0.000000e+00 2.882300e+04
50% 5.685093e+06 44.422500 7.175000e+06 0.000000e+00 3.310600e+04
75% 6.320607e+06 105.630000 1.327000e+07 5.740000e+06 3.968900e+04
max 9.382929e+06 419.028750 1.085300e+08 2.785000e+07 1.178310e+05
SE 9.428657e+04 9.933102 1.225168e+06 5.634718e+05 1.606824e+03
var 9.334456e+11 10359.984557 1.576088e+14 3.333755e+13 2.710978e+08
kurtosis 2.175782e+00 1.959914 3.911519e+01 6.875505e+00 5.557317e+00
skew 1.392248e+00 1.609215 5.135276e+00 2.436260e+00 2.107262e+00
range 5.047603e+06 419.028750 1.085300e+08 2.785000e+07 1.001100e+05
sum 6.233899e+08 8291.287500 8.506200e+08 3.292800e+08 3.971449e+06
# ユニーク件数
print('ユニーク件数(amount):' + str(len(df['amount'].unique())))
print('ユニーク件数(TVCM):' + str(len(df['TVCM'].unique())))
print('ユニーク件数(paper):' + str(len(df['paper'].unique())))
print('ユニーク件数(WEB):' + str(len(df['WEB'].unique())))

# 最頻値 
df_tmp = df[['TVCM', 'paper']].mode()
df_tmp
ユニーク件数(amount):105
ユニーク件数(TVCM):65
ユニーク件数(paper):60
ユニーク件数(WEB):105
TVCM paper
0 0.0 0.0

最頻値は不要でしょうが、一応付け加えておきました。

ヒストグラム

次に、amount列のヒストグラムを描きます。データ区間は本と少し異なりますが、区間が11個である点は合わせました。

import pandas as pd
import matplotlib.pyplot as plt

plt.figure(figsize=(25,10))

frequency, bin, histogram = plt.hist(df['amount'], bins=11)

plt.xticks(ticks=bin, labels=bin)
plt.xlabel('period')  
plt.ylabel('count')  
plt.title('histogram')  
plt.legend(['count'], loc='best')  # 凡例

print('データ区間の上限 : ' + str(bin[1:]))
print('頻度:' + str(frequency))
plt.show()
データ区間の上限 : [4794199. 5253072. 5711945. 6170818. 6629691. 7088564. 7547437. 8006310.
 8465183. 8924056. 9382929.]
頻度:[ 4. 21. 30. 19. 10.  9.  3.  5.  1.  1.  2.]

3-3 データの形のチェック(まとめ)

ここには問題の掲載はなく、3-1, 3-2 のまとめのみ書いてありました。

まとめ

前編はここまでとします。後編では相関係数や季節性をテーマにします。

(著:Y.Nozaki

関連記事