【Python】位置情報解析ライブラリscikit-mobilityについて② ~前処理編・前編~

# 1. scikit-mobilityとは？
scikit-mobilityは位置情報データを使用して人の動きを解析したり、可視化することができるpythonライブラリです。

公式ドキュメント：https://scikit-mobility.github.io/scikit-mobility/index.html
GitHub：https://github.com/scikit-mobility/scikit-mobility
公式ドキュメントは英語しかありませんが結構充実していて、
GitHubにはチュートリアル等も載っているので試してみるのがおすすめです。

scikit-mobilityの主な機能と、チュートリアルについて解説しているQiitaもありますのでこちらも是非参考にしてください。
* [【Python】位置情報解析ライブラリscikit-mobilityについて① ~scikit-mobilityとは?~](https://qiita.com/Nightley_dev/items/02b8fbad2db8236a92d1)

# 2. 今回紹介する関数について
## カテゴリ：preprocessing(前処理)
preprocessing(前処理)に分類される関数には、以下の二種類の関数があります。
    1. 生の緯度経度データを解析、加工がしやすいように不要なデータを削除したり、データ量を減らす処理を行うことができる関数
   2. ある程度同じ条件下にあるデータをクラス分けすることができる関数

今回はpreprocessing(前処理)で用意された４つの関数のうち、
　```1. 生の緯度経度データを解析、加工がしやすいように不要なデータを削除したり、データ量を減らす処理を行うことができる関数```
に該当する２つの関数をコードを交えて紹介します。

## 紹介する関数
* [filtering.filter](https://scikit-mobility.github.io/scikit-mobility/reference/preprocessing.html#skmob.preprocessing.filtering.filter)：ノイズ、または外れ値とみなされるデータポイントを削除する関数
* [compression.compress](https://scikit-mobility.github.io/scikit-mobility/reference/preprocessing.html#skmob.preprocessing.compression.compress)：近くにあるデータポイントを一つの点にまとめてデータポイント数を減らす関数

 **※ 今回この記事で使用するデータは社内検証用のデータになります。** 
# 3. 前提処理
## ライブラリのインストール
``` 
$ pip install scikit-mobility
```
詳しい環境構築は[こちら](https://github.com/scikit-mobility/scikit-mobility#installation)を参考にしてください

## TrajDataFrameデータの作成
* 以下の項目を含むデータを用意します。
> latitude(type: float);　緯度(必須)
> longitude (type: float);　経度(必須)
> datetime (type: date-time); 日時(必須)
> uid (type: string)；(オプション)
> tid (type: string); (オプション)

特に使いたいデータがない場合はscikit-mobilityのチュートリアルを参考に[こちらのデータ](https://raw.githubusercontent.com/scikit-mobility/scikit-mobility/master/examples/geolife_sample.txt.gz)をダウンロードするといいと思います。
※自動でデータがダウンロードされるので気をつけてください

```python
# ファイルのダウンロード(google colab等で実行する場合はこうすると楽です。)
import urllib.request
url='https://raw.githubusercontent.com/scikit-mobility/scikit-mobility/master/examples/geolife_sample.txt.gz'
save_name='geolife_sample.txt.gz'
urllib.request.urlretrieve(url, save_name)
```
* 用意したデータをTrajDataFrameに変換します。

```python
import skmob
# リストをTrajDataFrameに変換する場合
tdf = skmob.TrajDataFrame(data_list, latitude=1, longitude=2, datetime=3)
# pandas.DataFrameをTrajDataFrameに変換する場合
tdf = skmob.TrajDataFrame(data_df, latitude='latitude', datetime='hour', user_id='user')
```

# 4. preprocessing(前処理)について
参考URL：https://scikit-mobility.github.io/scikit-mobility/reference/preprocessing.html
### 概要
データ解析をしやすくするための前処理を行うことができる関数

## filtering.filter(フィルタ処理)関数
### 概要
ノイズ、または外れ値とみなされるデータポイントを削除する関数です。
外れ値とみなされるデータの閾値はデータポイント間の移動速度で決めることができます。

### inputに必要なデータとパラメータ、outputされるデータ
* inputデータ
    * tdf(TrajDataFrame) ：緯度経度データ
* パラメータ
    * max_speed_kmh(default: 500km/h)：前のポイントからの速度(km/h)がこの値よりも高い場合は、外れ値としてポイントを削除
    * include_loops(default:False)：Trueの場合、短くて早いループに属するデータポイントが削除される
    * speed(default:5km/h)：include_loops処理に使う
    * max_loop(default:6)：include_loops処理に使う
    * ratio_max(default:0.25)：include_loops処理に使う

* inputデータ例：
inputデータはTrajDataFrameを使うことができます。
みてみるとこんな感じになります。
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981403/a9aafbb1-88e7-9e7d-968c-56be4e79f969.png)

* outputデータ例
    フィルタ処理で除外されたデータポイントのないTrajDataFrameが出力されます。
    カラム的には特に変化はないですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981403/25f7a81e-4fcd-0d5e-35ca-773ecfd3b2ac.png)

### コード例
```python
from skmob.preprocessing import filtering
filtered_tdf = filtering.filter(tdf, max_speed_kmh=1000)
```
前データポイントからの速さ(km/h)が```max_speed_kmh```の引数で指定した値よりも高い場合は、外れ値としてポイントを削除する処理を行ってくれます。
上記のコードの場合は、**1000km/h以上の速さで移動していた場合のデータは削除される**
という感じになります。
何も指定しなかった場合はデフォルトの500km/hを基準に処理されます。

### 処理前と処理後のデータポイントを比較してみる
  * インプットデータ：
      * 特定のユーザーの緯度経度データ(以降元データとする)
  * アウトプットデータ：
      * 元データに対し、max_speed_kmh=500(デフォルト)でfiltering.filter処理を行なった後のtdfデータ
  * 元データをマッピングし、一部拡大した図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981403/4c291dc7-e961-c67b-1fc1-99d4c1fecbe4.png)


  * アウトプットデータをプロットし、一部を拡大した図
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981403/17ae5681-87be-92f7-0483-f7a417ecc6ee.png)

* 少し外れた位置にあるデータが削除されているのが確認できます。


### パラメータの変化による出力結果の違いを見てみる。
1. inputデータとして全体のデータを入れた場合

| max_speed_kmh(km/h) | 全体のデータ数 | 処理後のデータ数 | 削減されたデータポイント量(%) |
|--|--|--|--|  
| 100 | 15185 | 8246 | 45.7% |
| 200 |15185 |8440 |44.4% |
| 500(default) | 15185 | 8634 | 43.1% |
| 1000 | 15185 | 8673 | 42.9% |

* デフォルトで処理をしても40%程度のデータ削減を行うことができるようです。

2. inputデータとして特定ユーザーのデータを入れた場合

| max_speed_kmh(km/h) | 全体のデータ数 | 処理後のデータ数 | 削減されたデータポイント量(%) |
|--|--|--|--| 
| 100 | 722 | 432 | 40.2% |
| 200 | 722 | 432 | 40.2% |
| 500(default) | 722 | 432 | 40.2% |
| 1000 | 722 | 432 | 40.2% |

→データ数が少ないせいか、max_speed_kmh をデフォルトから変更してもパラメータごとにデータ数は変化しませんでした。

## compression.compress(軌道圧縮処理) 
### 概要
データポイント数を減らすための関数(軌道圧縮)。
与えられた初期点から指定した半径(km)内の全ての点を全ての点の中央座標と初期点の時刻を持つ単一の点に圧縮することができる関数。

### inputに必要なデータとパラメータ、outputされるデータ
* inputデータ
    * tdf(TrajDataFrame): 緯度経度データ
* パラメータ
    * Spatial_radius_km(default:0.2km): 圧縮された軌道の連続するポイントかんの最小距離(km単位)
* inputデータ例：
inputデータはTrajDataFrameを使うことができます。
みてみるとこんな感じになります。    
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981403/a9aafbb1-88e7-9e7d-968c-56be4e79f969.png)

* Outputデータ：
  * データポイントが圧縮されたTrajDataFrameが出力されます。
  * Outputデータ例は以下のような感じです。カラムはinputデータと特に変わらないです。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981403/25f7a81e-4fcd-0d5e-35ca-773ecfd3b2ac.png)


### コード例
```python
from skmob.preprocessing import compression
# spatial_radius_kmがdefault(0.2km)の場合
ctdf = compression.compress(tdf)
# spatial_radius_km(0.5km)を指定する場合
ctdf = compression.compress(tdf, spatial_radius_km=0.5)
```

### 軌道圧縮前と軌道圧縮後のデータポイントを可視化して比較
* インプットデータ：特定のユーザーの緯度経度データ(以降元データとする)
* アウトプットデータ：元データに対し、パラメータspatial_radius_km=1.0(km)でcompression.compress関数を使って軌道圧縮処理を行なった後のtdfデータ
  * 元データをマッピングし、一部拡大した図
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981403/49f01203-9603-d29c-d529-16d2b565df47.png)

  * アウトプットデータをプロットし、一部を拡大した図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981403/3e749e5b-d04f-7262-5dc0-fe62a4688765.png)

* 同じ時間付近に圧縮範囲内で得られたデータを中心の一箇所に圧縮してデータを削減しています。
* 元データと比べると似たような場所にあるデータが削減されているのがわかります。


### パラメータの変化による出力結果(軌道圧縮処理)
* inputデータとして全体のデータを入れた場合

| spatial_radius_km | 全体(input)のデータ数 | 処理後(output)のデータ数 | 削減されたデータポイント量(%) |
|--|--|--|--|
| 0.1 | 15185 | 8240 | 45.7% |
| 0.2(default) | 15185 | 8130 | 46.5% |
| 1.0 | 15185 | 5695 | 62.5% | 
|5.0| 15185 |1846 | 87.8% |


* 特定のユーザーのデータを入れた場合

| spatial_radius_km | 全体(input)のデータ数 |  処理後(output)のデータ数 | 削減されたデータポイント量(%) |
|--|--|--|--|
| 0.1 | 722 | 396 | 45.2% |
| 0.2(default) | 722 | 385 | 46.7% |
| 1.0 | 722 | 208 | 71.2% |
| 5.0 | 722 | 108 | 85.0% |

* デフォルトがちょうど50%くらいデータを減らすことができて前処理にはちょうどいい距離設定になっています。


ユーザーデータを入れた場合のパラメータごとのデータポイントのマッピング結果を比較(軌道圧縮処理)
* spatial_radius_km=0.1(km)
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981403/0a983df5-302b-4e88-7f12-4e63fc2c2ec1.png)

* spatial_radius_km=0.2(km)(デフォルト値)
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981403/e45c759a-e50a-7f41-f737-060fd2e410bf.png)

* spatial_radius_km=1.0(km)
　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981403/96a6ddd5-251c-ffc1-5586-843a6d087b0a.png)


* spatial_radius_km=5.0(km)
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2981403/5be03e26-9a0d-ffad-f093-fb618778c527.png)

→ 指定する距離が広がるとより多くのポイントが一箇所に圧縮されてデータポイントを減らすことができます。
→ 結構細かく圧縮範囲の指定ができるので自分でカスタムして前処理の精度を決められるのがいいなと思いました。


# 5. シリーズ記事
scikit-mobilityについて、シリーズで記事を投稿しています。
シリーズの記事もぜひ読んでいただけると嬉しいです。

前回の記事はこちらです。
* [【Python】位置情報解析ライブラリscikit-mobilityについて① ~scikit-mobilityとは?~](https://qiita.com/Nightley_dev/items/02b8fbad2db8236a92d1)






# 最後に
私たちの会社、ナイトレイでは一緒に事業を盛り上げてくれるGISチームメンバーを募集しています！
現在活躍中のメンバーは開発部に所属しながらセールス部門と密に動いており、
慣れてくれば顧客とのフロントに立ち進行を任されるなど、顧客に近い分やりがいを感じやすい
ポジションです。

 このような方は是非Wantedlyからお気軽にご連絡ください（もしくは recruit@nightley.jp まで）

 ✔︎ GISの使用経験があり、観光・まちづくり・交通系などの分野でスキルを活かしてみたい
 ✔︎ ビッグデータの処理が好き！（達成感を感じられる）
 ✔︎ 社内メンバーだけではなく顧客とのやり取りも実はけっこう好き
 ✔︎ 地理や地図が好きで仕事中も眺めていたい 

一つでも当てはまる方は是非こちらの記事をご覧ください 。
二つ当てはまった方は是非エントリーお待ちしております(^ ^)

「位置情報×モビリティ.まちづくりetc事業領域拡大の為GISエンジニア募集」 

https://www.wantedly.com/projects/1031814

▼ナイトレイとは？

https://www.wantedly.com/companies/nightley