今日のテーマは統計数学です♡
すまない、ガチ理系以外は帰ってくれないか!
サンプル コード: C#で統計処理(Code Recipe)
元データ
具体的には、データの傾向分析をします♠
ところで、このグラフを見てくれ…こいつをどう思う?
これは、とあるサイトの1日辺りのページ ビュー(PV)を、約6年分並べたものです。
なんとなく、毎年毎年、季節ごとに同じ傾向が出ているのはわかるでしょうか。盆正月、ゴールデン ウィークの時期にはPVが減ります。
もう1つ、同じデータで、期間を絞ってみましょう。
8週ほど抜き出したものです。みごとなまでに、土日のPVが少ないです。
ということで、
- 季節傾向
- 週間傾向
を抜き出してみましょう。
週間傾向
まずは、週単位でデータを区切ります♣
private static DailyData[][] GroupByWeek(DailyData[] data)
{
var weeks = data.Length / 7;
var weekly = Enumerable.Range(0, weeks)
.Select(i => data.Segment(i * 7, 7)).ToArray();
return weekly;
}
主成分分析
データ系列の似た部分を抜き出す方法として、主成分分析というものがあります。データ系列から作った相関行列を固有値分解するだけの簡単な統計解析手法です。
例えばこの場合、日曜を0として、月火水…を1, 2, 3, …と表すものとして、
X={xij} (第i週のj曜日のPV)
RX=XT X (Xの相関行列)
RX=VT Λ V (相関行列を固有値分解)
として、固有値のうち、絶対値が最大のものが、Λ の k 行目に現れるものとします。
すると、
Y=VT X (Xを相関行列の固有ベクトルを使って分解)
の 行目に、主成分(データ系列をもっともよく表す成分)が現れます。
—–
注意: 右肩にTは転置を表す記号です。相関行列は対象行列なので、固有ベクトルが作る行列を直行行列にでき、転置=逆行列になります。
C#で主成分分析
さて、要は、固有値分解が必要です。今回は、以下のライブラリを使います。
Shoは、Microsoft Researchが提供する、数値解析環境です。IronPythonベースのコマンド ラインや、可視化ツールを提供します。数値計算用のライブラリ自体は.NET製なので、C#からも参照できます。
Sho付属のライブラリを使って主成分分析を行うコードは以下の通りです。
private static double[] GetPrimaryComponent(DoubleArray matrix)
{
var correlation = matrix * matrix.Transpose(); // 相関行列
var eigenAnalysis = new EigenSym(correlation); // 固有値分解
var order = EigenValueOrder(eigenAnalysis); // 固有値の絶対値の順序
var v = eigenAnalysis.V.GetCol(order[0]); // 最大固有値に対応する列で
var primary = v.Transpose() * matrix; // 元の行列を分解
return primary.ToArray();
}
private static int[] EigenValueOrder(EigenSym eigen)
{
var indeces = eigen.D
.Select((x, i) => new { x, i })
.OrderByDescending(p => Math.Abs(p.x))
.Select(p => p.i)
.ToArray();
return indeces;
}
結果
以下のグラフは、計算で得た主成分を、和が1になるように正規化したものです。
土日は平日の3分の1程度のPVだということがわかります。また、月曜日と金曜日は本調子ではないようですね、微減です。
水曜日も少し少ないですね。いわゆる定時退社デーを水曜日にしている会社がおおいんでしょうか。
季節傾向
続いて、季節傾向にいきましょう♢
まず、前節で得た週間傾向で割って、影響を除外します。
var data2 = data.Select(x => new DailyData
{
Date = x.Date,
PageView = x.PageView / weeklyFactor[(int)x.Date.DayOfWeek]
});
そして、年度でグループ化します。うるう年の扱いがめんどくさいので、3月1日を先頭にして切り出します。
private static DailyData[][] GroupByAccountYear(IEnumerable<DailyData> data)
{
var anual = data
.GroupBy(x => x.Date.AccountYear())
.OrderBy(x => x.Key)
.Select(x => x.OrderBy(y => y.Date).ToArray())
.Where(x => x.Length >= 365)
.ToArray();
return anual;
}
public static int AccountYear(this DateTime t)
{
t = t.AddMonths(-2);
return t.Year;
}
あとは週間傾向と同じです。主成分分析にかけるだけ。
結果
同様に、季節傾向を正規化した結果を以下に示します。
後述する通り、最終結果を見るに、おそらく、季節傾向以外に、右肩上がりのトレンドを少し拾ってしまっています。なので、季節傾向としては、年度末ほど少し下げて考える(2月と3月が滑らかにつながるように補正する)必要があります。このグラフでは1・2月辺りがピークに見えますが、実際のピークはゴールデン ウィーク明けになります。
PVが減る場所がはっきり表れています。基本的には、お休みの日ほど減ります。
-
盆正月、ゴールデン/シルバー ウィーク
- 特に、お正月の減りが半端ないです
- 逆に、お盆はみなさん結構お仕事ですね
-
夏休み・春休み
- 大学の試験が終わると思われる2月ごろから急激に減ります
-
ハッピー マンデーを除く国民の休日
- 2/11(建国記念日)、11/3(文化の日)、11/23(勤労感謝の日)辺りが顕著です
- ハッピー マンデーでも、海の日と体育の日近辺は皆様、連続したお休み取られるようで
最終結果
週間傾向・季節傾向を除外した結果は以下の通りです。上半分が元、下半分が結果です。
一応は、右肩上がりなPVだといえるでしょう。
結果のグラフに微妙に周期性が残っているということは、「割り算すれば影響を除外できるだろう」という仮定がいまいちだったということなんですが、まあ、及第点なグラフでしょう。
毎年3月1日(年の区切りにした日)で急にグラフが跳ね上がっているので、季節傾向以外に、少し右肩上がりトレンドを拾ってしまったんだと思います。
あと、ハッピー マンデーな休日のせいで変なピークができている可能性もあります。
そして、2か所ほど、異常な落ち込みがあります。おそらくなんですが、
-
2007年7月頃:
- ページの言語設定がおかしかったらしく、日本語のページとして認識されず、検索結果が落ちていました
-
2011年3月中旬:
- ええ、東北地方太平洋沖地震の後の3日ほどです
だと思います。
まとめ
- 主成分分析
- Sho
- 休みの日は通常の3分の1
- みんな、月曜日はやる気微減
コメントを残す