2022/9/28

iOSDC Japan 2022 コアスタッフとして入退室トラッキングのデータ整理・可視化を担当しました

iOSDC Japan 2022が今年も無事に開催されました。

私は2021に引き続きコアスタッフとして参加し、パンフレットの制作進行や当日のインフォメーション係、公式Twitter、iOSDCチャレンジ周りなどを担当していたのですが、今年の新たな試みとしてオフライン会場における「入退室トラッキング」に少し関わらせてもらいました。

トラッキングシステム(名札に仕込んだNFCタグを受信機にタッチすると入室・退室が記録できる部分)については、当日もNFCに関するトークをされていた@treastrainさんを中心に準備されていて、day1より稼働していました。

可視化については構想時点で話が出ていたのですが、実際に動き出したのはシステムの稼働が確認できたday1の夕方からです。最終的な成果についてはクロージングで(すごく縦長で見づらかったと思いますが)tomzohさんから紹介していただきました。

上の画像では、15:10からのLTに合わせて、Track AとBに人が移動した様子がわかります。一方、Track Dにも人が残っているように見えますが、Track Dではトークがありませんでしたので、実際の会場にはそれほど人がいなかったはずです。

この記事では、そのような不自然な可視化を極力減らすために行なったデータの補正や難しさなどについて、備忘録を兼ねてご紹介します。

カンファレンスの入退室の難しさ

入退室データとして保存されるデータは、以下のような入室イベントと退室イベントのログとなります。このようなログを参加人数分集めれば、ある時間帯にどの部屋に何人いたかが集計できます。

入室と退室が交互に記録されたログのイメージ
上記のテーブルを図にしたもの
入室から退室までの間、その部屋にいたことがわかる

職場や学校など、入館証等で入室や退室を確実に記録できる環境と違い、今回のカンファレンスの入退室は参考記録用として参加者の皆さんに任意でご協力いただいたもので、システムを使わなくても部屋の入退室が可能でした。また、積極的にご協力いただいた方でも、トークの入れ替えのタイミングなどで人の流れにそって動いた結果、NFCをタッチせずに移動した方もいらっしゃったと思います。

その結果、実世界と照らすと不自然なデータが一定数発生してしまいます。これらを愚直に計算すると、不自然に人数が多い部屋が生まれたり、逆に人数がマイナスになったりしかねません。何らかの方法でデータを補完し、整合性を保つ必要があります。以下でいくつかパターンをご紹介します。

退室記録がない場合

まずは退室記録がない場合を見てみましょう。

Track Aの退室記録がないログ

ユーザー2のログでは、Track Aの入室の後、時間を置いてTrack Bの入室が記録されており、本来あるべき「Track Aからの退室」がありません。この場合は、Track Bへの入室の直前までTrack Aにいたものとして扱いました。具体的には、Track B入室の直前にTrack Aの退室イベントを追加して処理しています。

入室記録がない場合

続いて、入室記録がない場合です。

Track Bの入室記録がないログ

ユーザー3のログでは、Track Aからの退室のあと、しばらくしてTrack Bの退室が記録されており、本来あるべき「Track Bへの入室」がありません。先ほどと同様、Track Aの退室と同時にTrack Bへ入室したことにしても良いのですが、退室後にスポンサーブースなどを回っている可能性も考えられるため、できるだけ妥当と考えられる時間にTrack Bへの入室イベントを補完したいです。

Trackへの入退室は基本的にトークを聞くために行なっていると考えられるため、今回は「退室イベントの直前のトークが開始した時間」を入室時間とすることにしました。上記のログの場合、Track Bからの退室のおよそ20分(または40分)前に入室したことにして集計をしています。

入室した部屋と退室した部屋が違う場合

上記を合わせた、入室と退室が違う部屋となっているようなケースも考えられます。

入室記録と退室記録の部屋が違うログ

ユーザー4のログは、Track Aの入室とTrack Bの退室が記録されていますが、間の入退室の記録が存在しません。この場合は、退室記録があるTrack Bの直前のトーク開始をTrack Bの入室として、その直前をTrack Aの退室として扱っています。なお、同一トークの時間内でこのような状況が発生した場合は、便宜的にTrack Aの入室直後にTrack Bに入室した(Track Aを退室した)ことにしています。

退室しないまま日程が終了した場合

入室後、退室ログがないまま日程(day1, day2)が終わってしまっている場合もあります。

最後に入室したTrackの退室記録がないログ

この場合は、無理に退室イベントを作らず、最後までその部屋にいたことにしました。冒頭でトークがないのに部屋に人がいるように見えたのは、このようなログが一定数あったためです。

実装

ここまでご紹介した補正処理をかけるための実装が以下の通りです。クラス定義等は省いている&書き殴りの実装なので雰囲気で見てもらえればと思いますが、ログのアクション(入室、退室)によって処理を分岐しています。

ちなみに $SESSION_START_TIMES という変数には各トークの開始時間が格納されており、降順(day2の時間が遅いトーク→day1の最初のトーク、の順)に並んでいます。この処理をユーザーごと・日付ごとに行なっています。

$calculated = collect();
foreach($records as $record) {
    switch ($record->action) {
        case 'in':
            // 最初のレコード
            if ($calculated->isEmpty()) break;
            // 一つ前のレコードがoutである
            if ($calculated->last()->action == 'out') break;
            // 一つ前のレコードもinの場合(outの入力し忘れ)
            // このレコードと同じ時間でoutを追加
            $calculated->push(new Record(
                $record->time,
                $calculated->last()->track,
                'out',
                0,
                $identifier,
                true
            ));
            break;

        case 'out':
            // 一つ前が同じトラックのin
            $last = $calculated->last();
            if ($last && $last->action == 'in' && $last->track == $record->track) break;
            // 一つ前がout、または存在しない(inの入力忘れ)
            // このレコードに一番近いセッション開始時間、または一つ前の時間のうち遅い方をinとして記録
            if (!$last || $last->action == 'out') {
                $inTime = $record->time;
                foreach($SESSION_START_TIMES as $sessionStart) {
                    if ($sessionStart < $inTime) {
                        $inTime = $sessionStart;
                        break;
                    }
                }
                if ($last && $inTime < $last->time) {
                    $inTime = $last->time;
                }
                $calculated->push(new Record(
                    $inTime,
                    $record->track,
                    'in',
                    0,
                    $identifier,
                    true
                ));
                break;
            }

            // 一つ前が別トラックのin (別トラックのoutと、このトラックのinがない)
            // このレコードに一番近いセッション開始時間、または一つ前の時間のうち遅い方をinとして記録
            // 同時刻を前の部屋からのoutとして記録
            if ($last && $last->action == 'in') {
                $inTime = $record->time;
                foreach($SESSION_START_TIMES as $sessionStart) {
                    if ($sessionStart < $inTime) {
                        $inTime = $sessionStart;
                        break;
                    }
                }
                if ($last && $inTime < $last->time) {
                    $inTime = $last->time;
                }
                $calculated->push(new Record(
                    $inTime,
                    $last->track,
                    'out',
                    0,
                    $identifier,
                    true
                ));
                $calculated->push(new Record(
                    $inTime,
                    $record->track,
                    'in',
                    0,
                    $identifier,
                    true
                ));
                break;
            }

            break;
        default:
            throw new Exception("invalid action", 1);
    }
    $calculated->push($record);
}
return $calculated;

コメントがそこそこ書いてありますが、これはコメントを書かないと混乱してしまったからです。

集計処理と可視化の実装

2020でニコニコ生放送のコメント数を時間軸で可視化した時には、PHPでフロントエンドまで出力+Chart.jsで可視化という手法をとったのですが、今回はデータのみをPHPで返却し、Vue.jsで読み込み・可視化を行うという形で実装を行いました。

ここ1年ほどでVite+Vueの環境にそこそこ慣れてきたおかげで、ホットリロードの恩恵を受けながら、可視化部分についてはかなり試行錯誤しながら実装を進めることができました。実は時間ごとにアニメーションする実装なんかも作っていたりしており、新しいチャレンジができたかなと思います。

ちなみに2020のブログでは「来年以降に残すものでもないので」と思っていたコメント数可視化ツールなのですが、ほかのスタッフ(主に@blue_goheimochiさん)によって改良が加えられながら、2022でも元気に動いていました。読みづらいコードですみません……。

さいごに

iOSDC Japan 2022では、新たな試みとして「入退室データの可視化」にチャレンジしてみました。参加者の皆さんへのご紹介はクロージングのみに留まりましたが、iOSDCの新しいチャレンジとして、また個人的にも技術スタックの変化など新たな挑戦ができたのもよかったと思います。

入退室の取り組みについてはまた今後スタッフでも振り返りを行いますが、リアルタイムで見えるようにしたり、より積極的に参加してもらうような仕掛け作りなどもできると、より面白い取り組みに取り組みになりそうだなと思っています。

ご協力いただいた参加者の皆さま、そして当日まで全力で調整されていたスタッフの皆さま、ありがとうございました。

Share with Hatena Bookmark