Pythonでオープンデータを活用する(2)〜盗難推測編〜
この記事は、 JSL (日本システム技研) Advent Calendar 2019 - Qiita 12/17の記事です。
今回は↓で数値化した自転車盗難情報を使って任意に指定された自転車利用情報がどのくらい盗まれやすい特徴であるかを示す指標を作成する。 kawakeee.hatenablog.com
過去の自転車盗難情報の特徴に合わせ、以下項目を任意の自転車利用情報の項目として選別した。
- occurTime(発生時)
- age(年齢)
- occupation(職業 )
- isRock(施錠関係)
- occurMonth(発生月)
目標とする指標
目標とする指標(以下、安全スコア)のざっくりとした定義
【安全スコア】
・スコアが大きい → 盗難被害の特徴に似ていない(安全)
・スコアが小さい → 盗難被害の特徴に似ている(危険)
安全スコアの算出
安全スコアの算出には、マハラノビス距離を利用する。
【マハラノビス距離】 任意の点 が データ群 からどれだけ離れているかを示す数値
このマハラノビス距離を利用して任意の自転車利用情報が過去の自転車盗難被害情報からどれだけ離れているか(盗まれやすい特徴であるか)を数値化する。
詳細については、ここがわかりやすい。
自転車盗難データは5次元の行ベクトルの集合であるため、各行を (1482は推測に使う被害情報件数)とすると
番目の行ベクトルは以下のように示せる。
(スペースの都合で列ベクトルで表現)
このとき平均ベクトルを、分散共分散行列をとすると、
となる。
上記で算出した数値、任意の行ベクトル(利用者情報)をとすると
マハラノビス距離は以下の式から算出できる。
以降↓の手順でマハラノビス距離(= 安全スコア)をPythonで算出する。
1. 使用データの準備
2. 平均ベクトルの生成
3. 分散共分散行列の生成
4. マハラノビス距離の算出
1. 使用データの準備
使用する以下のデータを準備する
・過去の自転車盗難情報
・任意の自転車利用情報
こちらで数値化したデータを過去の自転車盗難情報として使用する。Unnamed: 0
は不要な列なので最初に削除しておく。
import pandas as pd import numpy as np import scipy as sc from scipy import linalg # 過去の自転車盗難情報 trans_data = pd.read_csv("./CSV/output/transform_bohan_data.csv") # 不要な列が存在するので削除 trans_data = trans_data.drop(columns='Unnamed: 0')
次に以下の値で、任意の自転車利用情報userInfo
を生成し、trans_data
の末尾に追加する。
# 任意の自転車利用情報を生成 userInfo = pd.Series([15, 30, 5, 1, 1], index=trans_data.columns) # trans_dataの末尾に追加 trans_data = trans_data.append( userInfo, ignore_index=True )
2. 平均ベクトルの生成
次にtrans_data
の各列(特徴量ごと)の平均値を算出していく。trans_data
の行数、列数を定数ROW
、COLUMN
に指定する。
# 定数の準備(trans_dataの行/列の数) ROW = 1482 COLUMN = 5 # column:列の初期化 column = [] # ave:平均ベクトルの初期化 ave = [0.0 for i in range(COLUMN)]
trans_data
の列ごとのリストを持つcolumn
から平均値を算出しave
に格納する。
# columnにtrans_dataの列要素(5件分)を代入 for i in range(COLUMN): column.append(list(trans_data.iloc[:, i])) # 平均ベクトルの計算(各列の平均値を算出) for i in range(COLUMN): ave[i] = np.average(column[i]) print(ave)
出力結果 [12.950742240215924, 19.87854251012146, 2.8157894736842106, 0.27800269905533065, 6.922402159244265]
3. 分散共分散行列の生成
diff
にuserInfo
とave
の差分を格納し、上記公式通りに分散共分散行列を算出する。
# diff: userInfoとaveの差分を格納する diff = userInfo - ave diff = np.array([diff]) # diffの転地行列 diff_T = np.swapaxes(diff, 0, 1) # vcm:分散共分散行列の計算 vcm = (diff * diff_T) / ROW
4. マハラノビス距離の算出
diff
とvcm
を用いて最後にマハラノビス距離を算出する。
# 算出のときはvcmの逆行列をもとめる vcm_r = sc.linalg.pinv(vcm) vcm_r = vcm_r.transpose() vcm_r = np.identity(COLUMN) # diffとvcm_rで行列の積(内積)をtempに代入 temp = np.dot(diff,vcm_r) # マハラノビス距離の算出 mahal = np.dot(temp[0],diff_T) mahal = np.sqrt(mahal[0]) print(mahal)
出力結果 12.12477884941061
マハラノビス距離の考察
(1)年齢を60歳代にして同様にマハラノビス距離を算出する
マハラノビス距離の出力結果 40.65309054438262
(2)年齢を20歳代にして同様にマハラノビス距離を算出する
マハラノビス距離の出力結果 6.677035044283844
今回、安全スコアの指標としてマハラノビス距離を算出した。
(1)、(2)より年齢が若いほど
マハラノビス距離の値は小さくなる
=> 安全スコアが小さくなるという結果になった。
【安全スコアのイメージ(再掲)】
目標は、安全スコアが小さくなる特徴は、盗難被害件数が多い特徴となっている事なので 実際に年齢が若いほど盗まれやすいのか、過去の盗難情報の年齢・職業別盗難発生件数から確認する。
年齢が若いほど発生件数が多くなる傾向がある事が確認できる。よって、安全スコアが小さくなる特徴は、盗難被害件数が多い特徴である事が確認できた。
他のグラフからも盗難件数が多くなる特徴が年齢以外にも見つかる。これらの特徴に、userInfo
の値を設定すれば安全スコア(マハラノビス距離)が小さくなっていく事も確認できた。
以上より、オープンデータを活用した盗難推測(任意に指定した特徴量が盗難被害の特徴に似ているかを判定)を実現した。