OKIYUKI99 Blog

データ分析や日常に関するブログ

WSDM 2019 : How A/B Tests Could Go Wrong : Automatic Diagnosis of Invalid Online Experiments を読んだ

dl.acm.org

LinkedinでInvalidなA/B テスト検知するための方法論を紹介している論文を読みました

A/Bテストにおけるメトリック解析の話から、丁寧にメトリックを分解し、検知ロジックを提案しています。LinkedinのA/B テストプラットフォームで実際に動いてるみたいです

Introduction

まずはじめに、invalidなA/BテストにはInternally または Externally なテストがあることを紹介しています

Internally invalid とは?

Treatment と Control間の差がTreatmentによる効果と結論づけれるのが本来のA/B テストの魅力の1つですが、その差がTreatmentによる効果ではない ことを言います

特にこの論文では、incomparable samples(つまり、比較不可なサンプル群)における Internally invalidを紹介しています。LinkedinでのA/Bテストのうち10%ほどが発生していたと報告されています

比較不可なサンプル群って何?と初見では思うかもですが、今年のKDD2019でも話があったSample Size Ratio Mismatchの話はこれの1つに該当します

gingi99.hatenablog.com

Externally invalid な A/B Test とは?

実験自体は正しく行えていても、実験の設定を超えて結果を一般化したときに妥当性が維持できない ことを言います

これも初見では何?となりましたが、具体的な例を述べますと、実験の結果が例えば時間依存 または サンプルの集団依存 していただけであって、100%リリースしたら思ったより有効な効果がでないような話がこれに該当します

これら2つのInvalidなA/Bテストを自動で検知するアルゴリズムを考えたのがこの論文の貢献です

このブログでは、Externally invalid が興味深かったので、それについて紹介します*1

Triggered Analysis

Triggered Analysisとは聞き慣れない言葉ですが、これを理解することがExternally invalidの実例を理解する上でとても重要になります

これは要するに、評価したいモノを実際に体験したユーザのみ を含んだ分析のことを言います。つまり、Treatment郡の集団に属した人でも、A/Bテストで評価したいモノ(例えば画面)を見てない人を含めて良いんだっけ?ということが動機です。例えば、Daily Active User(DAU) が2億人のウェブサイトがあったときに、A/Bテストで評価したあるページのDAUが200万人であるなら、本来Treatmentすべてのユーザを含めると、せっかくインパクトがある変更でもNoise(体験していないユーザが99%にいるため)になってしまいます

このTriggered analysisには session-triggeringuser-triggering の2種類があります*2

f:id:gingi99:20190317215938p:plain

みたとおり、session-triggeringとは、評価したいモノを体験したセッションのみ を解析する。user-triggeringとは、ユーザが一度体験してからのユーザのすべてのデータ を解析することを言います

この論文では、user-triggeringに絞ります*3

Cross-day vs Single-day Impact

どのユーザを使ってImpact(効果)を評価するかの考え方として、cross-day impactsingle-day impact の2つを述べています

実験期間k日あるとします

cross-day impact とは、1日目からt日目( 1 \le t \le k)までの どこかの日にトリガーしたすべてのユーザ を使って効果を求めるやり方です

single-day impact とは、t日にトリガーしたユーザ を使って効果を求めるやり方です

cross-day impactを評価するために、 n^{[1,t]}を1日目からt日目までのサンプルサイズ、  X^{[1,t]} をユーザiのメトリック、 \Delta^{[1,t]}\%をパーセンテージインパクト(リフト)とみなすと、 single-day impactはそのまま、 n^{[t,t]} X_{i}^{[t,t]} \Delta^{[t,t]}\%として評価すればOKです

cross-day impactはあくまで、一度でもtriggerすればそのユーザは評価対象に含まれます。つまり、ある日はtriggerしてなくてもそのときのそのユーザのメトリクス値もImpactを評価する際に使われるということです

Fully-covered vs Partially-covered Metrics

実験を評価するメトリックとして、Fully-coveredPartially-covered メトリクスの2つを述べています

fully-covered metrics とは ユーザがトリガーしないとメトリクスの計算ができない指標 のことです。サイトに訪れることがトリガーであるA/Bテストの場合、ページビューの合計はFully-covered metricsに該当します。つまり、ページビューの合計は、トリガーの影響を100%受けているからです。

partially-covered metrics とは ユーザがトリガーしなくても計算できる指標 のことです。あるページを見ることがトリガーであるA/Bテストの場合、ページビューの合計はpartially-covered metricsになります。つまり、ページビューの合計は、トリガーがなくても計算することができるからです。

In-trigger vs Off-trigger Impact

in-trigger impact は、ユーザがトリガーした日におけるtreatment impact のことです

off-trigger impact は逆で ユーザがトリガーしていない日におけるtreatment impact のことです

もちろん、fully-covered metricsのoff-trigger impactは常に0になります*4

External Invalidity

普通我々はA/Bテストの結果から、最も良いTreatmentを見つけて、100%のユーザに向けてテストした機能をリリースしていくかと思います。例えば、「この機能で○%良くなる結果を得たので、すべてのユーザに適用して、四半期経てば、△%の収益アップが狙えます」という感じですね

これは暗黙的に 別のサンプル集団時間 というものに対しても一般化して考えてる自然な思考だと思いますが、実はこれが External Invalidity を引き起こし得ることを論文では述べています

1つ目を Generalizing beyond the experiment population と言っており、A/Bテストの実験集団のみでの結果はあくまである機能に対する Local Impact であると言っています。 これをユーザ全体の Global Impact にするには、別の集団でもA/Bテストを複数行い、Site wide Impact を求め、Global Impactに転換していくことを述べています。Site wide impactとは、いわゆるサイト全体への影響を評価することを言います。詳しい内容は、以下の論文で紹介されているようです

dl.acm.org

2つ目の Generalizing over time が本論文で扱うExternal Invalidityになります。文字通り、メトリクスとは時間に依存してしまうがゆえにTreatment Effectを見過うことです

ここでさらに、この時間依存型のtreatment effectを novelty effecttrigger-day effect に分類しています

Novelty Effect

これは、ユーザの振る舞いの変化 に起因するEffectのことを言います。具体的には、最初にトリガーを体験したときと、何度もトリガーを体験したときでは、ユーザの反応は異なってきます。例えば、最初は通知に対してとても反応していたのが、だんたんと無視するようになるような行動の変化のことを言います

結果、ユーザがトリガーすればするほど、treatment effectは増加/減少します。これは以下の左図のとおり、single-day と cross-day impactの両方で強い一定の傾向をします

f:id:gingi99:20190824125424p:plain

まず左図を理解するために、まず右図を理解します。x軸が表しているのは、A/Bテストが開始してからの経過日数で、y軸は ユーザのトリガーした平均的な日数 です。これが増加するのは、A/Bテスト後に新たにトリガーするユーザが増えるためです。x = 1のときに、y = 1になるのは、トリガーした人のみを対象に数えるためですね。x = 2のときに、y = 1.7くらいでしょうか。2日経過して、2日ともにトリガーしたユーザと1日しかトリガーしていない人(1日目 or 2日目)がいるためですね。x = 3のときに、y = 2くらいなので、このA/Bテストでは、3日間のうちに1度以上トリガーした人は平均的に2日くらいはトリガーすると解釈すれば良いと思います

ここで左図を振り返ると、single-day impactのほうがA/Bテストが経過するほど、減少スピードが速いです。single-day impactは 各日でトリガーした人(初めてトリガーしたユーザ、もしくは何度かトリガーしているユーザも含まれる)のみインパクトを測ります。経過日数が立つほど、何度もトリガーしてる人が増えてくるため、後半の日ほどsingle-day impactは小さくなります。一方、cross-day impactはA/Bテスト開始日から、現時点までの どこかの日でトリガーしたユーザインパクトを測ります。Novelty Effectがある場合は、最初の体験時がImpactを大きくするため、そのImpact分が後半の日でも影響してくるため、このようにcross-day impactのほうがsingle-day impactより大きくなります

こういったNovelty Effectの特徴を使って、検知するアルゴリズム(以下のSTEP1とSTEP2)を提案しています

STEP 1

single-day impactを評価する回帰モデル  \Delta \%^{[t,t]} = \beta_0 + \beta_1 \frac{1}{t^{\alpha}} + \beta_2 \frac{1}{t^{\gamma}} を学習します。   \frac{1}{t^{\alpha}} がslow-decay 項、 \frac{1}{t^{\gamma}} が fast-decay項を表します。パラメータ \alpha \gammaはLinkedinのこれまでの何千ものA/B test結果チューニングした値(例として、 \alpha=0.35 \beta=2)を使っています。 slow-decayとfast-decayは以下のgradualとelbowタイプになると述べています

f:id:gingi99:20190824132245p:plain

STEP 2

Novelty effectが検知されたとフラグをつけるために、(a) STEP1で学習したモデルの決定係数が0.8より大きい(つまり十分single-day effectを捉えている)(b) 回帰直線が経過日数に対して単調増加/減少する (c) single-day impactが最も大きい日と最も小さい日で比較し、統計的に有意な差があるか。これらの3つの条件で判定します

最後に注意点として、あくまでSTEP1とSTEP2の評価により、single-day impactに強い傾向が見られることがわかるが、Novelty effectのせいでその傾向が起こってるかは保証できないことを述べていいます。当然ですが、曜日効果や季節効果 *5 により、このような強い傾向を観測することはあります。また、Novelty effectがどれくらいの程度であったかを評価するのも困難であることを述べています

ちなみに、LinkedinでよくNovelty effectが検知されるテストには、新しい機能に最初に触ったユーザは強い傾向を示すこと / 通知系のテスト / 推薦アルゴリズムなどでよく見られると述べています

Trigger-day effect

Trigger-day effectとNovelty effectの違いとして、cross-day impactに強い傾向があるのに対し、single-day impactがフラットになるといった特徴があります

f:id:gingi99:20190824134402p:plain

trigger-day effectがいつ起こるかというと、off-trigger impactとin-trigger impactに大きな違いがあるとき に発生します

例えば、トリガーが"通知を送る"、メトリックがTotal Page ViewであるA/Bテストを想像してみましょう。 トリガーを受けた日には2つのページを1度ずつ訪問し、それ以外の日はいつものページを1度訪問するだけである場合を考えると、ある週でのリフトは 8/7 - 1 ≒ 14.3%です。 つまり、 in-trigger impactは100%、off-trigger impactは0%、すべての日で見たcross-day impactは14.3% になります。 結果、single-day impactはin-trigger impactとほぼ同じとみなすことができるため一定のimpactが観測され、cross day impactはin-trigger と off-triggerの混合した結果になるので、テストが経過するにつれて増加/減少していきます

ここからtrigger-day effectを検知するアルゴリズムを提案しています

まずメトリックを分解していきます。 X_i がユーザ i のテスト期間中のあるメトリックの値の合計とします。それを、トリガーした日のメトリックの値の合計 I_iとトリガーしていない日のメトリックの値の合計 O_iに分解します(つまり、 X_i = I_i + O_i

k日間のそのメトリックのリフトを以下のように定式化します(はてなtex記法の挙動がよくわからなくなったので貼り付けます)

f:id:gingi99:20190824140515p:plain

f:id:gingi99:20190824142004p:plain

 w_k はin-trigger impactの重み係数として表現します

ここで、kを無限大に発散させたとき(つまり、経過日数をどんどん増やしていく)のことを考えると、ユーザがトリガーする確率が p ならk日後までにトリガーしたユーザ数は組み合わせの計算式で求めることができます

f:id:gingi99:20190824142744p:plain

f:id:gingi99:20190824142901p:plain

これで k \to \inf に発散させると、以下のように w_kを得ることができます

f:id:gingi99:20190824142830p:plain

実際のwを求めてシミュレーションしてみると一致することが確認できます

f:id:gingi99:20190824142948p:plain

この定式化を使って、以下の2つの条件を満たすかどうかでtrigger-day effectがあるかを判定します

  1. wが小さい(つまりテストがある程度経過している)、もしくはcross-day impactが圧倒的にsingle-day impactよりかつ一定の値を示している
  2. 2つのリフト  \Delta \%I \Delta \%O に有意な差がある

とくに、2つ目はDelta method*6を使って、 var(\Delta \%I) var(\Delta \%O)で求めれば検定のための情報が出揃うので、判定可能になります*7

さいごに

ヒューリスティック感はありますが、実際解釈しやすく意味のあるImpact指標になっており使い勝手がよさそうでした。しかし、完コピするというより、こういうオリジナル指標をデータやサービスの特性に合わせて自分たちも作る必要があるなーと感じました

参考

LinkedIn の A/Bテスト基盤と文化(KDD2015のFrom Infrastructure to Culture: A/B Testing Challenges in Large Scale Social Networks)の内容を紹介してくれています。Linkedinのように企業独自の1000を超えるメトリクスの管理は参考になりますね www.flywheel.jp

*1:というのも、Internally invalid の章も読んだのですが、Sample Size Ratio MismatchについてはKDD2019の論文が詳しく、他の事象については腑に落ちなかったので

*2:図はWSDM2015 Diluted Treatment Effect Estimation for Trigger Analysis in Online Controlled Experimentsより引用

*3:一度トリガーを経験すると、あとの行動にも影響はなんらかの影響が出る可能性はあるなーと想像は付くのですが、そのようにデータを引っこ抜くETLのほうが大変な印象です

*4:トリガーしないとfully-covered metricsは0になるので、Off-tiggerな日は0ですよね

*5:Linkedin では実用上見たことがないらしい

*6:A Note on the Delta Method - https://u.demog.berkeley.edu/~jrw/Biblio/Eprints/%20M-O/oehlert.1992_American.Statistician_delta.method.pdf

*7:いつかブログで使い方を書くかも

KDD 2019 : Diagnosing Sample Ratio Mismatch in Online Controlled Experiments: A Taxonomy and Rules of Thumb for Practitioners を読んだ

KDD2019な時期がきましたので、A/Bテスト系の論文あるかなと探してたときに気になった論文を読みました。

Booking.comのDirector of Experimentation のLukas Vermeer氏のツイートより

dl.acm.org

自分的要約としては、 複数の企業(Microsoft、Booking.com、Outreach.io)のこれまでの知見を集結させて、A/BテストにおけるSample Ratio Mismatch(SRM)がなぜ発生してしまうのかの根本原因を分類し、原因調査の勘所とそれらの防止策をまとめた論文 です。

はじめに

MicrosoftのA/Bテストの例として、以下のようなカルーセル UI でカードの数を12から16個に増やした画面を用意し、どちらの画面が良いかを比較するA/Bテストが紹介されています。

f:id:gingi99:20190807223832p:plain

最初の期待では、16個に増やしたほうがユーザがカルーセルに魅力を感じ、クリックする回数が増えると予想し実験を進めました。しかし、全く反対の結果(つまり、多くのカードを見たユーザほど、カルーセルのクリック回数が有意に減少した)が検出されてしまいました。

なぜこれが起こったかを調べたところ、実験開始後に、A/Bテストに割り当てられたTreatmentユーザ数がControlユーザ数と比べて有意に少なかったことが原因でした。それが起こってしまった原因を特定し(後に理由はでてきます)、正しく検証した結果、期待どおりのPositiveな差を検出することができました。

まさにこの現象のこと、Sample Ratio Mismatch(SRM と言います。つまり、A/Bテストのために割当したControl / Treatmentの2つの集団で、サンプルサイズが 期待どおりの比率で集まらなかった ことで、誤った結果を導いてしまう現象です。

期待どおりというと、例えば多くのA/Bテストでは、テスト対象ユーザを事前に見積もったサンプルサイズのとおり集め、2つの集団が 50 / 50 の比率でスプリットして、テスト対象ユーザをControl / Treatmentのどちらかに割り当てるかと思います。

しかし、実際にA/Bテストを開始しデータを集めると、予定通り50/50になるかと思いきや、集計時にA/Bテストの対象ユーザの比率が 50.2/49.8 (たとえば、821,588人 vs 815,482人) になることがあります。

なぜTreatmentのほうが少なくなってしまったのでしょうか?ただの偶然?そして、これが問題になるのか?とも思いますが、MicrosoftのTechnical FellowのRon Kohavi氏のTweetでもSRMによって本来Flatな結果だった実験も誤って有意差が検出されてしまう例を紹介しています。

また、この論文でも、この現象によりA/Bテストの結果の信憑性が損なわれるため、SRMが発生しているかどうかは検知する必要があると述べています。

この期待どおりになっていないを検出する方法として、統計的仮説検定の枠組みで考えると、カイ二乗検定を行えば検出できそうであることは想像がつくかと思います。

この論文の貢献は、カイ二乗検定などの検出方法についての議論をするのではく、 なぜこのSRMが起こるかを複数の企業のA/Bテストの経験値と調査した実績から分類し、体系立てた ところにあります。

つまり、 SRMが起こってしまう根本原因はこれかこれかこれか....のどれかだと分類し、分析者がSRMの調査のための指針書として役立ててもらう ことが狙いのようです。

MicrosoftとBooking.comとOutreach.ioの連名であることからも、複数の企業の協力によりこの論文が完成しています。

本題の内容に入る前に、スムーズに論文を理解するために、一般的なA/Bテストの4ステップをおさらいしておきます。このステップそれぞれにSRMが起こってしまうポイントがありそうだなーという気持ちで理解しておけばOKです。

(1) 実験の割当。ユーザを、2つの実験グループ(Control or Treatment)に割り当てます。

(2) 実験の実行。ユーザに実験のためのコンテンツが実際に配信され、ユーザが実験に参加できる状態になり、実験が行われます。

(3) ログの処理。ユーザの行動ログなどのデータが収集・処理され、分析できる形式に集約します。

(4) 分析。集約したデータを分析します(当たり前ですが)

SRMの理解

SRMを分類するためにどうしたかというと、この論文の著者の過去のケースを再度分析したり(10,000個にもおよぶ)、実際A/Bテストを行っている分析者のインタビューも含めた定量&定性調査により分類しています。

インタビューに使ったスクリプトも公開されていました:SRM Interview Guide 2019

MicrosoftではそもそもSRMはどれくらい発生してるのかを調べたところ、調べたテストのうち約6%でSRMが検出されました。年間10,000以上のA/Bテストが走ってるプロダクトでは、1日に1度は発生していた試算だそうです。

f:id:gingi99:20190808213120p:plain

実際の分析者のインタビューの内容を紹介されています。そこでは、SRMの発生はA/Bテストにおいて深刻な問題である。またそれがなぜ起こったを特定するのに、何ヶ月もかかることがあったなどが紹介されています。 しかし、SRMを防ぐことも可能であることと、逆にSRMの発生がTreatmentユーザにPostiveな結果であると解釈することもできることが紹介されていました(たとえば、コンテンツのLoad のSpeedがTreatmentで改善した場合、そのLoad Eventに関するログが増加する可能性があるなど)。

5種のSRM

ここからSRMを実際に分類していきます。

1つ目が、実験に割当を行うステップで起こってしまったSRMを紹介しています。

この例では、あるA/Bテストの前に、A/Aテストの時点でSRMが発生したケースがあったそうです。

原因は、実験の割当システムで特殊なUser IDを持ったユーザでのテストの配信トラブル(あまり詳しいことは書いていませんでした)で、50/50でテスト対象者を割当るはずが、49.9/50になっていたことがわかりました。

また、複数の実験を継続的に行っていると、ある実験に参加した人が次の実験にも参加することによるキャリーオーバー効果(ユーザの実験慣れのようなもの)が発生する場合に、SRMが起こってしまったことを紹介しています。

テストを大量に同時に行っているMicrosoftだからこそのSRMな気もしました。

2つ目がA/Bテスト"実行中"(ここでは、1セッションが解析対象になっているため、実行中と言ったときには何かしらのスパンがあるイメージ)に起こるSRMです。

これは、Skypeの改善に関するA/Bテストで、TreatmentにはMLモデルを使ってバッファリングパラメータをオンライン更新し、通話品質を高めることができるかのテストでした。Controlは、予め固定されたパラメータを使っています。

この実験では、当然のようにTreatmentの品質が改善するとMicrosoftは予想していました。しかし、いざ評価すると、全く反対の結果(音声の歪みや遅延が増加)が示され、さらにTreatmentのセッション数(コール数)がControlと比べて30%も少ない結果が得られてしまったそうです。

この原因は、セッションの途中で実験のための非同期に設定がリフレッシュされてしまい、Treatmentユーザの割当が変更されてしまったことが原因であったとわかりました(つらすぎる...)

これにより、Treatmentユーザの30%が、結果としてTreatmentのログとして扱われていないことがわかり、リフレッシュにより品質劣化まで引き起こしてしまったようです。なんとかログを調べ尽くし、リフレッシュが起こっていないセッションのみを使って評価することになりました。

これは、SRMの問題というより、開発側のQAミスなのでは?と思いましたが、この問題からSRMの一般化を行っています。それは、(1) このセッションが実験対象になるときの振る舞い (2) 実験中(つまりセッション中)の振る舞い (3) そのセッションで生成されるログの振る舞いの3つで構成されることを述べています。(3)はログが発生する前にユーザがExitしてセッションが壊れるケースがあるので想像はつきますが、(1)と(2)はかなり丁寧にログを見ないと見つけることも一筋縄ではいかないだろうなと想像できます。また、品質のA/Bテストだからこそ起こる問題かとも思います。

これを検知する仕組みとしてBooking.comでは、データ収集時のログを丁寧に取得し、Treatmentで警告ログ(割当の変更など)が増えてないかなどをモニタリングする仕組みをいれているようです。クライアント側のデータといえば、一般的にはかなりノイズの多いデータになることが想像できます。そこから検知できる仕組みがすでに動いていることに感心しました。

3つ目がA/Bテストのログ処理中におこるSRMを紹介しています。

これは、最初に紹介した12枚のカードを16枚のカードに変更したA/Bテストで起こったSRMです。この実験では、Treatmentユーザ(16枚のカードを見る対象)の数が少なくなっていました。

この原因は、エンゲージメントが高いユーザがなんとログの処理中にBotに分類されて(!)、Treatmentユーザから除外されてしまっていたことがわかりました。 エンゲージメントが異常に高い(大量にカードを見ていた)ことにより、Bot検知アルゴリズム閾値を超えてしまったそうです。

Bot検知を開発してる側も、12枚のカードがきっとこれまでデフォルトでパラメータチューニングされてデプロイされている中、16枚のカードになるなんて思ってもいなかったのだろうと勝手に解釈してしまいました)

この問題をログ処理中のSRMであると一般化しています。実験時の生ログを分析ができるフォーマットにETLするときに不適当な処理が入ってしまう問題であると述べてます。このSRMは防ぐのが難しいとも述べており、個人的には納得できます。

4つ目が分析時に発生するSRMです。

これは一番想像しやすいSRMかと思います。分析時にさまざまFilterを入れることで、不適当にTreatmentユーザが少なくなってしまう、逆に多くなってしまうことはあるかと思います。とくにA/Bテストなので、Treatmentにしか無い機能がある場合だと、Controlと収集ログの特徴が変化してしまうことは想像できます。

5つ目が実験とは関係のない別の要因が干渉してくることにより発生するSRMです。

Microsoft Store のHomepageのリデザイン(画面イメージは下記)のA/Bテストをしたときに、Controlユーザのほうが多かったことがあったことを報告しています。

f:id:gingi99:20190808230245p:plain

この原因は、検索エンジンのキャンペーンにより、ユーザが割当とは異なる誤ったURLに強制的にリダイレクトされてしまったことが原因だったそうです。

これはインターネット上でのA/Bテストだからこそ、別の仕組みと干渉してしまうことはあると思うので、防ぐのが難しい気もします。ユーザ側で見えてる画面までをモニタリングするしかないのでは?とも思います。 実際、Microsoftでは、割当ユーザが正しい画面(URLのパラメータなども見て)を見ているかをチェックしているそうです。

ここまでの5つのSRMを最後の図でまとめています。赤い*マークが複数の実験でインパクトを起こしうる原因です。読者はこの図を用いてSRMの根本原因がどこにあるかを探っていけばよいでしょう。

f:id:gingi99:20190808231150p:plain

10のSRM調査の指針

最後の章に、SRM発生時に調べるべき10の指針を示してくれています。最後の図と合わせて見ながら、これらを調べていけばよいでしょう。

  1. Examine scorecards : 分析時のフィルタがミスってないか調べよう

  2. Examine user segments: あるユーザセグメントだけに起こってないか調べよう

  3. Examine time segments: ある時間セグメントだけに起こってないか調べよう

  4. Analyze performance metrics: A/Bテスト時のログのパフォーマンスメトリクス(ページのロード時間などに差がないか)を調べよう

  5. Analyze engagement metrics: EngagementがTreatmentで高いなら、Engagementが低いユーザが原因で起こっていると考えられる。そのユーザを調べよう

  6. Count frequency of SRMs: SRMが複数のA/Bテストで発生してるなら、原因はA/Bテストシステムである可能性が高い

  7. Examine A/A experiment: A/Aテストで発生してるなら、原因はA/Bテストシステムである可能性が高い

  8. Examine severity: TreatmentとControlのサンプル比率が大きく偏っていたなら、ログ収集およびログ処理時にユーザが除外されている可能性が高い

  9. Examine downstream: データの収集から集約までのパイプラインを確認してみよう

  10. Examine across pipelines: パイプラインが複数ある場合、互いに比べてみよう

おわりに

SRMは結果の信頼性を損なうだけでなく、分析者の調査時間もかなり必要であることを最後まで強調しています。

理想的には、今回の論文でのベストプラクティス集を使うことなく(つまり、SRMに出くわすことなく)A/Bテストを回していきたいと思いますが、SRMに出くわしたときはこの論文にそって調査していければと思います。

Udemyの【徹底的に解説!】Djangoの基礎をマスターして、3つのアプリを作ろう!をやりました

フロントエンドの基礎周辺をなんとなく知っておきたいと思って、udemyで以下をやってました。

https://www.udemy.com/django-3app/

個人的にはかなり良かったです。やった感想をつらつらと、

  • Data ScientistとしてPythonのコードは書けるくらいのレベルでしたが、いまいちWebアプリのPythonコードってどんなの?HTMLやCSSどれくらい知識としているの?とか不明瞭だったのが、かなりクリアになった感じです。
  • djangoの勉強はこれまで何度かトライしようとしては、習得に時間かかりそうだなと思ってやめていましたが、このビデオで用語やプロジェクトの始め方を1から丁寧に説明してくれました。著者はdjangoチュートリアルがわかりにくという感想を持ってるようで、チュートリアルとは違う観点で説明しており、それが自分的にもかなり良かった気がします。
  • pythonやHTMLの説明がなく、ちょうどdjango自体を0から知りたい人向けになっており、自分的にもちょうどよかったです。

次はReactかVue.jsあたりに入門してみようかなと思います。

逐次的に検定を行っていると第一種過誤が増幅してしまう例(KDD 2017 : Peeking at A/B Tests より)

最近KDD 2017のPeeking at A/B Testsの論文を読んでいました。

www.kdd.org

その論文の問題設定に関わるこの図が少し気になっていました。

f:id:gingi99:20190703211931p:plain

これは何かというと、

このシミュレーションでは、サンプルサイズ10,000のA/B Test(仮にAが5,000、Bが5,000の対象が集まるまで続ける)を行うという設定で、データはAとBともに平均0、標準偏差1の正規分布からサンプリングしたデータを使います。 これは全く同じ分布から生成されるデータですので、平均が同じである帰無仮説を棄却してしまうことは、False Positive = 第一種過誤が生じていることを意味します。 そのような設定のもと、対象が集まる度に逐次的に検定を行い、X個のサンプルサイズ(横軸)が集まるまでに設定した有意水準(0.05とか)で有意差が 一度でも 検出される(= P値が有意水準を下回った = 第一種過誤が生じてしまった)確率をこの図の縦軸Yが表現しています。

一例を述べると、有意水準0.05の場合(緑の線)、サンプルが集まる度に検定をしていると、10,000サンプルサイズ集まるころには、約60%の確率で一度はFalse Positiveが発生してしまうことを意味しています。

有意水準0.05なので、そもそも5%の確率でFalse Positiveが発生するのですが、逐次的に検定を行っていると、それが何倍にも増幅してしまうことを示すとても良い一例だと思いました。

論文ではこのシミュレーションの細かい設定までは書いていなかったのですが、自分の理解の確認のため、Rで再現してみました。

パッケージの読み込み等は省略し、以下のシミュレーションを回してみます(結構時間がかかります…)。

sim_func <- function(simulations, sample_size, times, alpha){
  obs <- sapply(1:simulations, function(j){
    data_a <- c()
    data_b <- c()
    n <- sample_size / times
    for(i in 1:(times+1)){
      a <- rnorm(n / 2, mean = 0, sd = 1)
      b <- rnorm(n / 2, mean = 0, sd = 1)
      data_a <- c(data_a, a)
      data_b <- c(data_b, b)
      pval <- t.test(data_a, data_b, alternative = "two.sided", paired = F, var.equal = F)$p.value
      if(pval < alpha){
        break
      }
    }
    return(length(data_a) * 2)
  })
  
  data.frame(alpha = as.character(alpha), obs = obs, stringsAsFactors = F) %>%
    dplyr::group_by(alpha, obs) %>%
    dplyr::count() %>%
    ungroup() %>%
    mutate(false_positive_prob = cumsum(n) / simulations) -> df
  df
}

simulations <- 10000
sample_size <- 10000
times <- 1000
df_010 <- sim_func(simulations, sample_size, times, alpha = 0.1)
df_005 <- sim_func(simulations, sample_size, times, alpha = 0.05)
df_001 <- sim_func(simulations, sample_size, times, alpha = 0.01)

df <- bind_rows(df_010, df_005, df_001) 

1回のシミュレーションの中で、10個のサンプルが集まる(すでに集まってるサンプルに新しいサンプルが追加されるイメージ)度にt検定を行い、設定した有意水準のもと有意差が検出されるかをチェックします。 検出されたときのサンプルサイズを返す(スクリプトの中の変数obs)ことで、10,000個のサンプルが集まるまでに初めて検出されてしまうのはどれくらいサンプルが集まったときなのか?についてのデータを集めます。 これを3つの有意水準でそれぞれ10,000回ずつシミュレーションします。論文と同じ図を描くために累積数を計算して、可視化します。可視化のコードは以下です。

df %>%
  filter(obs <= sample_size) %>%
  mutate(alpha = format(as.numeric(alpha), nsmall = 2)) %>%
  ggplot(aes(x = obs, y = false_positive_prob, group = alpha, color = alpha, linetype = alpha)) + 
    geom_line(size = 1) + 
    scale_y_continuous(breaks = seq(0.0, 0.8, by=0.2), limits = c(0,0.8)) +
    theme_bw() + 
    labs(x = "# of observations", y = "False positive probability")

結果は以下のようになり、だいたい同じような図が描けました。

f:id:gingi99:20190703215943p:plain

論文よりも、False positiveの確率が低くなっているのは、私のシミュレーションでは計算時間を減らす&簡略化するために10個のサンプルが集まる度に検定を行っているためだと思います。 これを1個ずつにすれば、検出される確率は大きくなるため、論文ではそのようにしているのかと思います。

手を動かすことで、論文の問題設定が少しクリアに理解できた気がします(先は長い)

CHI 2019 : When Do People Trust Their Social Groups ? を読んだ

Facebook のグループ機能に関するデータ解析の論文を最近読みました

自分的要約

  • Facebookのモチベーションとしては、ユーザが安心して使うための信頼できるグループを増やしたい。じゃあ信頼できるグループってどんなのか?から入る
  • グループを使用している10,000人のFacebookユーザをサンプリングし、アンケートを行う。妥当な回答を得た6,383人を対象に分析を行う
  • アンケートとは、ユーザ自体の他人への態度(他人を信頼することができる気質かどうかなど)に関するアンケートと使っているグループでの態度に関するアンケートを実施し、グループの信頼スコアを定め、定量的に分析する
  • これらのアンケートにより得られたデータとFacebook保有する観測データから構成される特徴量をもとに、信頼スコアを予測する回帰モデルを作り、何が信頼に効くかを評価する
  • 結果的には、(1)個人の態度は重要、(2)グループのサイズが小さくて、昔から使われてて、均質的なグループが重要 (3)グループ内の人のネットワーク構造は重要であることを示した
  • 得られた結果を使って、Facebookプラットフォーマーの立場として、信頼できるグループ作りに役立てる

以下は、論文の流れにそってつらつらと説明する。

背景知識

  • グループの中での信頼関係というのは、メンバーの満足度もあがるし、メンバーのパフォーマンスも上がるし、対立も減らせるし、良いことづくめ(当たり前なんだがきちんと参考論文を引用しているのがすごい)
  • Facebookでは、14億人がGroupを毎月使っている
  • 関連研究では、個人の一般的な他人を信頼するかの傾向が、特定のグループにおける信頼に影響すると言っている。グループといっても構成される人によるという話。
  • 一般的には、ダンバー数150を超えると、認知限界がきて維持が難しくなる(らしい)

過去の研究サーベイがかなり充実していたので、また興味があるときに読むと良さそうです。

データとサーベイ

まずユーザは以下のようにサンプリングしている

  • グループをアクティブに使っているUSの10,000人をサンプリング
  • グループは5人以上のグループでかつ、28日間に1つ以上の何らかのやり取りが観測されたものを使っている
    • 多少のサンプリングバイアス(性別や年齢)が入ってしまったらしい

これらのユーザにサーベイ(アンケート)を広告経由で招待して実施。大項目としては、(1) 個人の他人への態度 と (2) 所属しているグループがどれだけ信頼できるかを質問している。 質問内容は下記の表のとおり。ざっくりいうと、人を信頼する派か?リスクを取るタイプか?など。どうやら、World Value Surveyという何か標準化されているものを使って質問を作ったらしい。こういうのいつか参考になるかもしれない。5段階のリッカート尺度で回答してデータを集める。

f:id:gingi99:20190608213428p:plain

(ちなみに、個人に関する情報やグループ内のポストはすべて匿名化されており、このサーベイを実施ことも厳しく監査されてる様子)

別途、グループはどういうタイプのものが多いかもアンケートを取っている。カテゴリは以前Facebookが調査したものを使っている。結果としては、89%が以下の5つのグループに該当するらしい

• Close friends and family (e.g., extended family) -> 20%

• Education/Work/Professional (e.g., college, job)

• Interest-based (e.g., hobby, book club, sports) -> 34%

• Lifestyle/Identity-based (e.g., health, faith, parenting)

• Location-based (e.g., neighborhood/local organization)

アプローチ

  • アンケートデータと行動ログ(like数や使用時間など)を特徴量として使う
  • 10,000人からアンケートが妥当でかつ、行動ログと合わない人などを除いて、6,383人までデータをサンプルを落としている
  • 手法は、オーソドックスに重回帰分析とランダムフォレストとロジスティク回帰を使っている。特徴量の追加を繰り返すことで、決定係数の改善幅を見ていくスタイル

結果

Resultsの章はかなり事細かく記載している。CHI論文らしい感じ。

アンケート項目の基本統計量やアンケート間の相関値を確認する(Table 2)と、グループに対するアンケート(下表)は高い相関を示していることがわかる。

f:id:gingi99:20190608224708p:plain

その結果を踏まえて、目的変数となるグループの信頼スコアは、グループに関する4つのアンケート内容のスコアの平均値として使う(といっても、あとから別々にも評価している)

まず、「個人の他人への態度」についてのアンケートの特徴量だけで予測できるかをやってみる。Table 3が結果。

f:id:gingi99:20190608225005p:plain

結果をみると、「個人の他人への態度」についてのアンケートの特徴量全部入りで決定係数0.14まで上昇している。そもそも性格的に人を信じるタイプかどうかということだろうか。

ここから、Facebookお手製の特徴量をどんどん入れて決定係数を改善していく。それらの特徴量をまとめているのが、Table 4。

f:id:gingi99:20190608225556p:plain

まず1つ目のグループに関する基本的な特徴量を入れると、決定係数が0.08増加したと報告している。

最も効いた特徴量はグループサイズ。それと、グループがPublicかPrivate(FacebookでいうSecretとClose Group)であること。グループサイズは多きすぎる知らない人増えそうだし、PrivateグループはPublicよりは見られないからある程度の安心はあるよねと納得できる。

これらの特徴量で信頼スコアがどう変わるか図示したのが下の図(Figure 1)。

f:id:gingi99:20190608225736p:plain

disposition to trust (信頼への気質)によって、信頼スコアの低下の傾きの大きさが違う結果を示していた。この値が低い人ほど、大きく降下していくことを示している。この結果がでても、個人の気質はコントロールできないし、どうして制御するのかは難しい問題である

2つ目にグループのカテゴリに関する特徴量を入れる。

グループの5つのカテゴリの各信頼スコアをみてみる(Figure 2)と「Close friends and family」が高い結果が出ていて、直感的にも正しい。このようなグループカテゴリを特徴量としていれると、決定係数が0.05増加したと報告している。

f:id:gingi99:20190609105617p:plain

3つ目にグループのアクティブ度合い。

サーベイに使った28日間のグループ内でのアクティビティ(滞在時間、投稿数、Likeの数、コメント数など)の平均値を特徴として入れると、決定係数が0.04増加したらと報告している。グループ内での交流が盛んなほど信頼できるグループだよねという直感的にも正しい。

4つ目にHomogeneityとhomophily の特徴量を入れる。

Homogeneity(均一性)は、グループ内に所属する人が互いにどれだけ似ているかを測る。論文では、性別のエントロピーや年齢の標準偏差を計算している。homophily (同類性)は個々の人がグループ内の他人にどれだけ似ているかを測る。論文では、性別や年齢をビン化した距離のアプローチを使っている。

これらを特徴としていれたが、あまり改善はなかったと報告している。性別や年齢がバラけていても、信頼スコアには寄与しないのは、家族グループとかは完全にそうだしなーという感想なのでそう言われたらそうな気もします。

最後の5つ目の特徴量として、Network structureの特徴量を入れる。

これはいろいろと工夫した5種類ほどの特徴量があり、Network density(グループ内のメンバー間で友達となっている数の合計を全組み合わせ数で割った数)やpaticular degree centrality(メンバーが持っている友達の数をグループサイズで正規化したもの)など。このNetwork特徴が最も決定係数を改善していて、決定係数を0.10も改善したと報告している。だれもお互いに知らない素なグループよりは、密にあるほうが良いというのは直感的であるが、一番効くというのは驚いた。

最終的にすべての特徴量を入れて、決定係数が0.26、MSEが0.53まで精度をあげている。さらに追加実験で、観測データのみ(アンケートデータは使わずに)でモデルを組んだところ、決定係数が0.15、MSEが0.59になったと報告している。アンケートデータは将来に渡って使えるとは限らないので、こういうふうにロバスト性をチェックするやり方はとても良い。

Figure 4に各特徴量の重要度をまとめている。重要度は計算したい特徴量を抜いて、MSEがどれだけ悪くなってしまうかの比率で定義している。Network Structureの特徴を抜くと、22%も悪くなってしまうことがわかる。こういう可視化わかりやすいと思うので、機会があったらやってみたい。

f:id:gingi99:20190609160118p:plain

アンケートをとってから28日後までに、グループのメンバーサイズやつながりの数が変化があったかをグループの信頼スコア別に評価している。 面白いのは、信頼スコアの高いグループほど、メンバーサイズが増加していないことがわかる。(Figure 5の左) これは、メンバーサイズの増加率が停滞してくることが、信頼スコアにおいては良いグループになってくることを示唆している。こういうのが定量的に現れるのは面白い。

f:id:gingi99:20190609162317p:plain

最終的に、得られた結果をFacebookがどう活かすが最後に述べられている。

  • プラットフォーマーとしての立場として、Group Adminに信頼スコアを提供するなどを考えている。
  • グループの推薦ロジックの改善に役立てる
    • グループサイズに応じたり、個人の信頼への気質でロジックを変更できるだろうと考えている
  • グループ内の知り合いを探しやすくして、友達のつながりを増やすことなど

感想

個人的には人間の潜在的な心理状態は、サービス提供社が保有するログだけからじゃわからないけど、アンケートも加えると、そのログにラベルが付いた状態で解析できる分、掛け算効果でログの情報量が増える印象。何より分析が面白くなる。このような理想的なストーリーはいつも頭に入れておこうと思う。

2019GWにやったこと

ただのGW記です。令和になったGWは10連休ということでしたが、お互いの実家に帰省。息子と遊んだりでのんびり過ごしました。

GWは普段とは異なるインプットをしようと思い、気になってた本を読んだり、Webサーフィンして調べものをしていました。全体的にキャリアや組織論に関することを調べていました。

以下は読んだ本や参考になった記事。

CAREER SKILLS ソフトウェア開発者の完全キャリアガイド

CAREER SKILLS ソフトウェア開発者の完全キャリアガイド

CAREER SKILLS ソフトウェア開発者の完全キャリアガイド

これまでのキャリアを振り返ると、データ分析周りでエンジニア、リサーチャー、データサイエンティストというロールで仕事をしてきましたが、個の競合優位性を高めるために、次に何をプラスしようかと考えていました。 この本は下のSOFT SKILLSの著者ジョン・ソンメズの2018年に出た本です。

SOFT SKILLS ソフトウェア開発者の人生マニュアル

SOFT SKILLS ソフトウェア開発者の人生マニュアル

20万語を超える本なので、全部読むのではなく、気になった部・章だけ読めるように設計されています。自分は5章のキャリアの進め方を中心に読みました。

スペシャリストとゼネラリスト論できっぱり言い切ってくれている箇所があり、気に入っています。

ソフトウェア開発者の多くは、すべてのスペシャリストがゼネラリストでもあるのに対し、ゼネラリストは決してスペシャリストではない

自分の分野全体についての一般的な知識の広い裾野ができてなければ、優れたスペシャリストになることは非常に難しい

また、スペシャリストを目指すときの選択肢としても、スモールスタートで特定の言語のフレームワークやライブラリの専門家を目指すようなイメージを勧めています。

大きすぎる専門分野を選ぶくらいなら、小さすぎて特殊すぎるものを選ぶようにしよう

サイドプロジェクトをしようという話もおもしろかった。

ソフトウェア開発者としてのキャリアを伸ばす最良の方法は間違いなくサイドプロジェクトだ。

まず第一にどんなサイドプロジェクトを手がける場合でも、そこから大きくなくても収益を上げる方法を考えることをお勧めする

これまでサイドビジネスとして、趣味でもあるボードゲームの制作を行い、サークルとしてピクテルを制作したり、海外版Imagineを流通させてきましたが、そろそろ別のサイドプロジェクトを持つことを考えるきっかけになりました。

bodoge.hoobby.net

エンジニアリング組織論への招待 ~不確実性に向き合う思考と組織のリファクタリング

前々から評判のよかった本を読めました。たしかに前評判どおりの内容の濃さでした。

特に社会学者の話で、「社会人はコミュ力大事!」という理由もなく通説になっていることを3つの不確実性(他者理解の不確実性・伝達の不確実性・成果の不確実性)に分解しているのを見たときに、「あーこれでコミュ力なんで大事かがやっと人にきれいに説明できる」と思いました。

というふうに気づきの多い本でした。何度か読んで、脳に刷り込んでいきたいです。

岩波データサイエンス Vol.3

岩波データサイエンス Vol.3

岩波データサイエンス Vol.3

もはやX周回遅れ感がありますが、今のチームメンバーの分析思考のなかに、因果推論の基本事項がデフォルトインストールされている感じがしていたので、そろそろ議論の中でその視点から価値出せるコメントができるようにと思い、精読しました。 傾向スコアの強力さは複雑な共変量を1次元に落とし込むシンプルさと説明力だと解釈できたり、差の差法やABテストの操作変数からの見方がやっと腑に落ちたので、がんがん使っていきたいと思います。

ワーク・ルールズ!

ワーク・ルールズ!―君の生き方とリーダーシップを変える

ワーク・ルールズ!―君の生き方とリーダーシップを変える

これも周回遅れ感があるが、Hontoライブラリに眠っていたのを読んだ。が、特に今となっては何か新しい視座を得ることはそこまでなかったので、流し読みしました。また組織論をキャッチアップしたいときにいずれ戻ることはあると思います。

プロダクトマネージャーとか

キャリアの話の続きで、データ分析周りしてて、ゆくゆくはプロダクトマネージャーのキャリアもあり得るだろうなと考えていて、以下の記事がわかりやすかった。中のリンクも読んで、自分に足りてなそうな視点を拾っていきました。

findy-code.io

プロダクトマネージャーは特にビジョナリーが大事なのは強調されているが、UXデザインの知識も必要であることが気になりました。大学時代に認知心理学とかを学んで以来、知識がストップしてるので、リフレッシュしたいなーとは思っていますが、なかなかどう習得したらいいのかわからない。実践経験?

つづく

INSPIREDとジョブ理論の評価が高そうなので、また引き続き読書しようと思います。

1st Shiny Contest に参加しました

Shiny Contest とは?

Shinyとは、さまざまなinput形式に基づき動的にグラフを作成し、Webアプリやダッシュボードとして簡単に公開できる人気の高いRライブラリです。

Rが好きな理由の一つに、Shinyのことを言うRユーザは多いのではないでしょうか(私の希望的観測)。

本題のShiny Contestとは、1月7日にRStudio社の公式ブログで発表された第1回目のShinyアプリのコンテストです。

blog.rstudio.com

参加資格としては、

  • RStudio Cloudでソースコードを公開すること
  • shinyapps.io にデプロイすること
  • コードやデータは自由に公開できること

を満たすアプリであればだれでも参加できます。

参加するために、RStudio CommunityにBlogを投稿する必要がありますが、アプリの紹介とデプロイしたアプリのURLを書いて投稿完了となります。

締め切りは3/8まででした。実は私がこのコンテスト知ったのは、2月末ごろにRStudio社のツイートがRetweetされていたのをたまたま見て知りました。

ABテスト用のサンプルサイズ計算アプリを投稿してみた

このコンテストを知る前から、自身の統計の勉強も兼ねて、ABテストのシミュレーションに使用するためのサンプルサイズ計算Shinyアプリを作っていました。

ちょうどコンテストのことを知ったときくらいに、アプリはできていたので、いろいろと機能を加えて投稿しようと考えました。

ABテスト用のサンプルサイズ計算アプリはすでに世の中にたくさんあります。しかし、このとき自分がなぜいまさらShinyアプリを作ろうと思ったのは、既存のアプリでは満足できなかったからです。例えば、

  • Type Ⅰ / Type Ⅱエラーのサマリーした表が欲しかった。有意水準と検出力を人に理解してもらうためでもあります。検出力が小さいとこの箇所のパーセントが小さくなりますと人に説明することを想定しています。
  • サンプルサイズ計算までの各パラメータをまとめてhtmlのテーブルに出力したかった。htmlテーブルだと、社内のConfluenceに秒でコピーペーストでき、各パラメータを動かしたときの必要なサンプルサイズの結果をすぐにお伝えすることができるからです。
  • 有意水準と検出力を可視化したプロットが欲しかった。この効果量だと、A群とB群の分布がこうなるんですよと人に見せることをを想定しています。

上記のように、人に説明することを想定して、自分の理解も深めようと考えアプリを作成しました。

実装したGithubはこちらにあります。

github.com

また、RStudio Communityに投稿したポストはこちらです。

community.rstudio.com

実は自分が投稿する数日前に、下記のブログの作者もサンプルサイズ計算のためのShinyアプリを投稿していました。

www.searchdiscovery.com

このアプリのコードを読んで、アイデアをいくつかもらい、自身のShinyアプリにも追加で実装しました。 この記事でも、結果を誤解されないように丁寧に項目を精査している旨が書かれており、非常に参考になりました。

結果発表

4/5にRStudioから結果が発表されました。

blog.rstudio.com

当初の予定では、3/22までに発表すると予定されていましたが、予想以上に投稿数があったたため、発表日を延長したみたいです。

上記の公式ブログにも各種統計情報が掲載されていますが、136個の投稿があり、4人のWinnerが選ばれました!

選ばれたアプリをぱっと眺めているだけでも、Shinyでどうやって動いてるんだ?と思うものがあり、見てるだけで非常に楽しいです。一度は目を通すと勉強になるかと思います。

気になったShiny App

69 Love Songs: A Lyrical Analysis

Best Design ということでWinnerに選ばれたアプリです。開くとわかりますが、フォントといい、可視化の色使いといい、クールな見た目のアプリです。

ぱっと見て複雑な処理はそこまで多くなさそうなので、どうやって実装しているかをあとでチェックしたいと思います。

committedtotape.shinyapps.io

Pet records

こちらもWinnerに選ばれたアプリです。自身のペットの医療やワクチンのトラッキングアプリのようです。アイデアがユニークなのと、見た目のビジュアルも美しく、見ていて楽しくなるアプリです。

jennadallen.shinyapps.io

R Package Explorer

開くとわかりますが、もはやShinyアプリの見た目ではありません笑。

HTML Templateを駆使しているとのことですが、RのcomponentをどのようにHTMLの中に入れてコードを書いているのか気になりますので、こちらも要チェックです。

nz-stefan.shinyapps.io

ubeRideR

自身のuberの走行データを用いて可視化しているアプリです。こちらもHTML Templateを使用しているとのことですが、Shinyアプリの一線を超えた見た目に驚きました。

vivekkatial.shinyapps.io

おわりに

Shiny Contestによって、世界中からさまざまなアイデアを持ったShiny Appアプリが集まってきました。 提出する前にすでに投稿済みのアプリを眺めていましたが、非常にユニークなアプリが多く、見ているだけで楽しかったです。

投稿されたものについては、ほぼすべての実装がRStudio Cloud上でソースコードが公開され、すぐに実行することができるようになっています。 これを機に、気になったShinyアプリを見つけ、ソースコードを追うなどして深くShinyを勉強できるかと思います。

また、一からShinyを学びたい人は、今は非常に日本語の資料も増えてきています。

特に、@Np_Ur_ さんの100本ノックやShiny Advent CalendarやShiny本からはじめると非常にスムーズに学べるかと思います。

www.randpy.tokyo

qiita.com

次回のShinyContestでもなにか出せるようにネタを作っておこうと思います。そして、日本からもどんどん提出していきましょう。

参考

私が実装するにあたり、既存のABテストに関わるShinyアプリをいくつか参考にしました。