見出し画像

ねぇ、「時刻」の平均を計算してよ。

もう3年は前かな、生徒さんからこんなメールをもらった。
「お客さんの平均来客時刻を計算しろって、上司から言われて泣きたい。」

問題 : これまでに10:49, 12:14, 14:05, 15:59, 16:21, 17:56に来店したお客さんAの平均来客時刻を求めてください。

「それ、sinθ, cosθの出番だよ。」と返信したのは良い思い出。

Intro. 時刻の平均の計算方法

結論から言うと、以下のステップを踏めばよい。

【計算の流れ】
step 1. 時刻を単位円上の座標ベクトルで表現する。
step 2. 平均ベクトルを求める。
step 3. 平均ベクトルの向きが、それぞれ時刻の平均値に対応する。

単位円上の座標ベクトルに対応付けられるようなデータを「角度データ」という。角度データの平均値を計算する方法はすでに確立されているので、時刻にもこの計算方法を応用してみよう!

1. 時刻は角度データ

1日は24時間なので、円の一周に0から24まで目盛りをつけて、角度で表現できる。絵を描くと分かりやすい気がする。

特に一周は2πなので、0時から1時間経つごとにπ/12だけ角度が進む。例えば、3:00なら3時間進んでいるので、角度ではπ/4に対応している。あとは、この円を原点を中心とする半径1の円と思いなおすことで、3:00は(cosπ/4, sinπ/4)の座標ベクトルと対応づけできる。

2. 角度データにおける平均値

上の要領で、時刻データをすべて座標ベクトルに変換しておきます。するとデータポイントi (i = 1,...,n)に対して、(cosθi, sinθi)という座標ベクトルが得られる。あとは、以下の要領で計算すれば平均ベクトルの出来上がり。

平均ベクトルの向いている角度をθと書くとき、座標ベクトル(cosθ, sinθ)を「角度データの平均値」と言います。今回、時刻の平均はこの角度データの平均値に対応する時刻なわけです!

3. Rで計算できるよという話 / そして問題の答え

角度データの計算に役に立つcircularパッケージを用いて、時刻の平均を計算しましょう。以下の流れで解説します。

3.1 circularパッケージのインストール
3.2 時刻の表示を変換する関数を作っておく
3.3 時刻データの準備
3.4 時刻の平均値の計算

3.1 circularパッケージのインストール : 事前にcircularパッケージをロードしておきましょう。新しいパッケージをインストールするときは、install.packages関数を用いるのでした。

# circularパッケージのロード
install.packages("circular")

3.2 時刻の表示を変換する関数を作っておく : “hour:minute”の文字列をhour単位の数値に、hour単位の数値をhourとminuteの表示に書き換える関数が必要になるので、事前に作っておきましょう。

# “hour:minute”をhour単位に変換する関数
hm_to_hour <- function(clock){
    return(clock$hour + clock$minute / 60)
}
# hour単位を、"hour:minute"に書き換える関数
hour_to_hm <- function(hour){
    hour <- as.numeric(hour)
    h <- floor(hour); m <- (hour - h) * 60
    return(c(h, m))
}

3.3 時刻データの準備 : 上述した「これまでに10:49, 12:14, 14:05, 15:59, 16:21, 17:56に来店したお客さんAの平均来客時刻を求めてください」という問題を解いてみよう。lubridateパッケージを応用して、時刻をhour単位に変換しておきます。

# とりあえず各時刻を角度に変換する。
library(lubridate)
clock <- hm(c("10:49", "12:14", "14:05", "15:59", "16:21", "17:56"))
hour_clock <- hm_to_hour(clock)

さらに、hour単位のオブジェクトhour_clockをcircularクラスに変換しておいて、後で平均値を計算できるようにしておきます。circularクラスに変換することで、時刻を角度データとして扱えるようになります。

# circularクラス
library(circular)
hour_clock_circular <- circular(hour_clock, 
                                units = "hours",    # 準備したデータの単位
                                template = "clock24",    # データの種類
                                modulo = "2pi")     # 0-24時の単位で返す。
plot(hour_clock_circular)    # データを確認しておく。(時計が出てくるw)

3.4 時刻の平均値の計算 : 時刻の平均値を計算しましょう。circularクラスのオブジェクトにmean関数を適応すると、角度データの平均値を計算することが出来ます。

# 時刻の平均値の計算
mean_hour <- mean(hour_clock_circular)    # 平均値の計算
plot(mean_hour)    # 平均値の確認
hour_to_hm(mean_hour)    # 時刻の確認(時と分で出てくる。)
#> [1] 14.00000 36.51191    # 時刻の平均値は14:36だとわかる。

結果、時刻の平均はおよそ14時36分と計算することが出来ました!

4. おわりに

今回は、時刻の平均の計算を例に、角度データの計算を紹介しました。またR言語のcircularパッケージを用いた角度データの計算について紹介しました。実は、書き残したことが2つあるので、後日書くためにここに残しておきます。

1点目は、角度データの分散・標準偏差・相関係数の話です。要するにお客さんの来客時刻の散らばり具合や、お客さんどうしの来客時間帯の類似度を見ることもできるわけです。

2点目は、もっと簡単に「0:00からの経過時間を平均することで時刻の平均を求められないのか?」という疑問を持つ人もいるだろう点です。実はこの計算は適切とは限りません。具体例として0:00, 6:00, 12:00, 18:00の平均値の計算を考えると、今回の計算となにが異なるのか見えてくるかもしれません。

サポートをいただいた場合、新たに記事を書く際に勉強する書籍や筆記用具などを買うお金に使おうと思いますm(_ _)m