【データ分析編】RaspberryPi + 物体検出 で室内の人数を slack へ可視化


概要

【構築編】RaspberryPi + 物体検出 で室内の人数を slack へ可視化 の続きです.

Slack のチャンネルに,室内の人数が流れるようになりました.

スクリーンショット 2019-07-08 00.41.54.png

今回は,このチャンネルのログから過去の人数データを取得し,そのデータの分析を試みます.


手順


チャンネルのログを取得し,csv形式で保存する

事前に Slack API でデータ取得用の App を作成し,トークンを控えます.


  • 与える権限


    • channels:history

    • channels:read



また,チャンネルIDを調べておきます.

参考:SlackのチャンネルIDを確認する方法

必要な情報を取り出して csv 形式で保存するPythonスクリプトを作りました.


logall.py

import requests

import re
import time
import json
import pandas as pd

url = "https://slack.com/api/channels.history"
token = "(あなたのトークン)"
channel_id = "(あなたのチャンネルID)"
prog = re.compile("現在ゼミ室には [0-9]+名います.")

# 他の発言を拾わないように,正規表現でマッチングする
def is_match(text: str) -> bool:
return prog.fullmatch(text) is not None

# 人数抽出
def text2people_num(text: str) -> int:
return int(text.split('現在ゼミ室には ')[1].split('名います.')[0])

def main():
payload = {
"token": token,
"channel": channel_id,
"count": 1000,
}
response = requests.get(url, params=payload)

df = pd.DataFrame(columns=["timestamp", "people_num",])

json_data = response.json()
messages = json_data["messages"]
for message in messages[::-1]:
if is_match(message['text']): # 欲しいデータであるとき
ts = time.ctime(int(message['ts'].split('.')[0]))
people_n = text2people_num(message["text"])
print(ts, people_n)
df = df.append(pd.Series([ts, people_n], index=df.columns), ignore_index=True)

df.to_csv("room_log.csv")

if __name__ == '__main__':
main()


csvファイルの中身はだいたいこんな感じになります(Jupyter Notebook で開いたためidが振られている点に注意).実際にはもっと行があります.

スクリーンショット 2019-07-08 01.06.03.png


csv形式のデータを Jupyter Notebook で開き分析する


[1]

import numpy as np

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline


[2]

df = pd.read_csv('room_log.csv', index_col=0)

df

データを確認したら,前処理を行います.


タイムスタンプをそのまま使うのは難しいので,日付と時刻のカラムを追加します.

また,あとで先月の記録を可視化するので,月のカラムも追加します.

今回,元のタイムスタンプはここで捨てました.


[3]

from calendar import month_abbr

def timestamp2disc_time(ts: str) -> str:
elms = ts.split()[-2]
elms = elms.split(':')
correct_minute = int(elms[1]) // 15 * 15
return elms[0] + ':' + "%02d" % correct_minute

def timestamp2date(ts: str) -> str:
elms = ts.split()
day = str("%02d" % int(elms[2]))
elms = [elms[4], elms[1], day, '(' + elms[0] + ')']
return " ".join(elms)

month2int = dict()
for i, key in enumerate(month_abbr):
month2int[key] = i

df['month'] = df['timestamp'].apply(lambda elm: month2int[elm.split()[1]])
df['time'] = df['timestamp'].apply(timestamp2disc_time)
df['date'] = df['timestamp'].apply(timestamp2date)
df = df.drop('timestamp', axis=1)
df


この時点でのデータフレームは次のようになります.

スクリーンショット 2019-07-08 01.14.39.png

seaborn のヒートマップを用いて,このデータをわかりやすく可視化します.

ヒートマップの作成にあたり,seabornに与えるデータフレームを作成します.

参考:Python でデータ可視化 - カッコいいヒートマップを描こう


[4]

df_pivot = pd.pivot_table(data=df[df['month'] == 6], values='people_num', columns='date', index='time', aggfunc=np.mean)

df_pivot

ヒートマップを描画します.縦に長くなるので figsize を変更して見やすくします.


[5]

plt.figure(figsize=(9, 20)) 

sns.heatmap(df_pivot, annot=True, fmt='2g', cmap='Reds')

201906.png

ヒートマップですので,室内の人数が多かった時間帯ほど色が濃く表示されます,


まとめ

データを取得して処理し,可視化する手順を示しました.

今後は曜日ごとの利用状況を見たり,機械学習によって利用傾向のモデル化を行なうとおもしろいかもしれないと考えています.