データの向こうに見える世界

戦略コンサルとデータマイニング

入退会データ分析(1) 入退会者数の時系列推移①

みなさま、こんにちは。

前回記事に続き、今回は予備校の入退会者数の時系列推移を見ていきたいと思います。

基本的に、データ分析はビジネスの中で発生している何らかの課題に対して、仮説を検証していきながら進めるものですが、実際の現場でいきなり仮説検証のための分析に取り掛かる事はあまりありません。

これは、「そもそものデータに何らかの偏り(バイアス)がないか」「データ内部に欠損値・エラー値など分析に悪影響を及ぼすような欠陥がないか」等を確認するためであると共に、単純な集計を幾つか回す中でそのデータに対する理解を深める目的もあります。

そのため、最初は特に指向性を持つ事なく、様々な簡易集計やグラフ化を行ったりします。(感覚としてはデータと“遊ぶ”イメージ)

今回は、それに近い感覚で元データを整形して単純な集計を行います。

また、元データが18,460行程度しかないため、これくらいであれば全てエクセルで集計できてしまうのですが、より大規模なデータを扱うことも想定し今回はR*1を使ってデータ整形していきます。

僕自身、実際のコンサルのプロジェクトで、[①Rでデータを分析しやすい形に整形]→[②エクセルでグラフ化して資料作成]という手法をよく使います。*2

データ読み込み

今回は元データをエクセルファイルで用意したので、エクセルからRに読み込みます。

setwd("~/Desktop")
library(readxl)
dataset <- read_excel("~/Desktop/データ分析/yobiko.xlsx")

 

整形後データのイメージ

データ整形に入る前に、どのような形のデータを整形していくか考えておきます。

今回は、

  • 入会者数の時系列推移
  • 退会者数の時系列推移
  • 生徒在籍数の時系列推移

をグラフにしたいので、下記のようなデータ形式に整形していきたいと思います。

f:id:mochi1441:20170630210457p:plain

データ整形

(1) 素材データの生成

データ整形に必要な集計を行います。コードは下記の通り。

# 分析(1): 入退会者数の時系列推移
## 1-1: データ整形(1) 素材データの生成
Date <- sort(unique(dataset$適用年月))
In_flow_count <- table(dataset$適用年月[dataset$申込種別 == "In"]) #「入会者数」を適用年月毎に集計
Out_flow_count <- table(dataset$適用年月[dataset$申込種別 == "Out"]) #「退会者数」を適用年月毎に集計
Change_flow_count <- table(dataset$適用年月[dataset$申込種別 == "Change"]) #「コース変更者数」を適用年月毎に集計

 元データは申込ベースでのログが1件ずつ蓄積されている状態であるため、まずは「適用年月」毎にフローベースの入会者数・退会者数をtable関数で集計しています。

ここでは4つのベクトルを生成しています。

  • Date: 「適用年月」列の重複を除き、小さい順(日付が若い順)に並べる
  • In_flow_count: 「申込種別」が"In"(入会)となっている行を「適用年月」毎に集計
  • Out_flow_count: 「申込種別」が"Out"(退会)となっている行を「適用年月」毎に集計
  • Change_flow_count: 「申込種別」が"Change"(コース変更)となっている行を「適用年月」毎に集計

(コース変更に関しては特に要らないのですが、何となく作ってます。)

出来たベクトルは下記のような形になっています。

f:id:mochi1441:20170630213550p:plain

 ここで、注意しなければいけない事が。4つのベクトルの長さ(要素数)を見ると下記のようにずれています。

> length(Date)
[1] 79
> length(In_flow_count)
[1] 79
> length(Out_flow_count)
[1] 76
> length(Change_flow_count)
[1] 57

Date の長さが79であることから、元データに含まれている「適用年月」は合計で79ヶ月分である事がわかるのですが、Out_flow_countChange_flow_countがそれより少なくなっています。

これは、退会とコース変更に関して、申込みが存在しなかった月が存在しているためです。

つまり、Out_flow_countChange_flow_countに関しては、適用年月に歯抜けが存在している状態になっているわけです。(実際に先ほどのスクショでも抜けがある事がわかります。)

これに注意した上でさらにデータ整形を進めていきます。

(2) 適用年月に合わせて整形

フローベースでデータ整形

先ほどのベクトルを使って、適用年月に歯抜けがない状態の整形データを作成します。

## 1-2: データ整形(2) 適用年月に合わせて整形
### 1-2-1: フローベースでデータ整形
flow.mat <- matrix(0, nrow = length(Date), ncol = 3) #縦長=集計年月数、横長=3で全てに"0"が入った行列を作成
rownames(flow.mat) <- Date #flow.matの行名に日付を代入
colnames(flow.mat) <- c("In", "Out", "Change") #flow.matの列名を代入

flow.mat[match(rownames(In_flow_count), rownames(flow.mat)), "In"] <- In_flow_count
flow.mat[match(rownames(Out_flow_count), rownames(flow.mat)), "Out"] <- Out_flow_count
flow.mat[match(rownames(Change_flow_count), rownames(flow.mat)), "Change"] <- Change_flow_count

フローベースでの整形データは、flow.matという変数に格納していきます。

最初にマトリクスの形だけ作ってあげて、該当する列に適用年月ごとに入会者数・退会者数・コース変更者数を入れていきます。

先ほどの歯抜け問題は、match関数を使う事で解決しています。(○○_flow_countの行名=歯抜けの適用年月が、フルの適用年月の何番目に当たるかを検索して、該当する箇所にデータを入れています。)

これにより、下記のような整形データを作れました。

f:id:mochi1441:20170630215156p:plain

ストックベースでデータ整形

先ほど作成したフローベースの整形データを元に、ストックベースの整形データも作成します。

### 1-2-2: ストックベースでデータ整形
stock.mat <- apply(flow.mat[,1:2], 2, cumsum) #frow.matを列毎に累積和にする事でストック化
enrollment <- stock.mat[,1] - stock.mat[,2] #各時点での在籍者数
stock.mat <- cbind(stock.mat, enrollment)
colnames(stock.mat)[3] <- "Enrollment"

ここではapply関数にcumsum関数を噛ませて集計しています。

cumsum関数はベクトルから累積和ベクトルを作成する関数で、apply関数を用いる事で行列の各行or列に対して同じ処理を施す事が出来ます。

また、ここではコース変更の列は取っ払ってしまって、代わりに在籍者数の列を追加しています。(累積入会者数−累積退会者数=在籍者数)

これにより、ストックベースでのデータ整形も行う事が出来ました。

f:id:mochi1441:20170630215852p:plain

 

少し長くなってしまったので、グラフ化は次の記事で!

 

以上

*1:オープンソースフリーソフトウェアの統計解析向けのプログラミング言語。無料で使える上に先人達が様々なパッケージを公開してくれているため、お手軽に統計分析や機械学習を実装できて最高な言語です。大体何でもできます!

*2:エクセルでvlookupとか使いまくるとめちゃくちゃ重くなるのでRでの整形の方が速い。ただ、ビジュアル化に関してはやはりエクセルの方が綺麗だし楽です。