機略戦記

Maneuver warfare

エンジニアからデータ分析になって3ヶ月が経った

これは【その1】ドリコム Advent Calendar 2015の21日目の記事です。 前日は、奈良阪さんの研修で新規ゲームアプリをモデルとビューに分けて作ったり色々したら捗った話です。【その2】ドリコム Advent Calendar 2015もよろしくお願いします。


私は、2015年10月から、あるネイティブゲームでデータ分析を担当しています。
2015年10月までは、同じネイティブゲームでエンジニアを担当していました。

本記事は、私がエンジニアからデータ分析へと役割を変えた事によって得た経験を元に、データ分析が何を目的としてどんな事をする役割なのか自分なりに考えた事を説明するものです。

現在エンジニアをしていて「データ分析をやってみたい」と思っている人や、「データ分析って何をやる仕事なの」と思っている人にとって、一つの事例として参考にしてもらえれば幸いです。

なお今回の記事では、弊社内での具体的な分析の事例は触れておりません。 また、ここに書いた内容は、分析チームに所属してまだまだ日が浅い私個人の考えであり、組織の考え方を代表したものでは全くありません。

データ分析はどのような形でチームに貢献できるか?

最初に、データ分析はどのような形でチームに貢献できるのか? という事について私が考えた事を説明したいと思います。

前提

私たちの商品は何か?

ネイティブゲームの開発チームが、お客様に提供している「商品」とは一体なのでしょうか?
私はお客様が得る体感だと考えています。

  • 「敵を、ギリギリのところまで追い詰めながら、あと1歩のところで負けてしまったあの悔しさ
  • 「どうしても欲しかったあの一枚を、ついにガチャから引き当てたあの興奮

こういった「体感」こそが商品であり、ゲームはそれを届けるための媒介です。 本当の商品はゲームをプレイしたお客様の頭の中で生まれていると私は考えています。

データとは何なのか?

「私たちが提供している商品は体感である」という前提に立った時、 データ、またデータ分析はどのような意味を持つでしょうか?

なお、ここで言っているデータとは、日々お客様の行動から生まれるトランザクションデータ(ユーザーデータ)の事を指しています。

データはお客様の仕草

データとはお客様が示す"仕草"であると私は考えています。

とてもざっくりした例ですが、「ゲームからの離脱」という行動を起こしたお客様は、おそらく「良い体感を得ていなかった」のでしょう。

もっと細かく行動を観察すれば、より精緻にお客様がどんな体感を得たのか推測する事ができます。

「お客様が感じた事を推測できる具体的な現象の記録」がデータです。 これはお客様の仕草と言い換えることも出来ると思います。

データはお客様の影

ネイティブゲームは時に膨大な数のお客様に遊んで頂けます。数百万、あるいはもっと沢山のダウンロードを記録しているアプリも中にはあります。

このような膨大な数のお客様が、ゲーム内で毎秒毎秒、様々な行動を取ります。 この全てを把握する事は、人間の認識能力の限界を超えています。

そこでデータが役に立ちます。 例えば、ソーシャルゲームのKPIとして特に有名であるDAU*1であれば、「ゲームにアクセスする」という仕草を示したお客様の数が分かります。

KPIは、特定の視点のみを残し、その他の情報が削ぎ落とされた情報ですが、全体を俯瞰しています。 これは、と言い換える事ができると思います。

別の言い方をすると、把握しきれない全体像に対して、ある関数を適用して次元を落とした写像がデータだと言えるかも知れません。

役割

さて、前述の前提にたったとき、データ分析がチームでどのような役割を果たしうるのかについて考えを述べたいと思います。

作りたかった物が作れたのか知る

本記事ではネイティブゲーム開発チームの商品は体感だという前提を置きました。 体感はお客様の頭の中で発生します。

私たちが何らかの施策を実施した時、作りたかった体感が作れたかどうか? はゲームだけを見ても分かりません。お客様の仕草を観察し、体感を推測し、はじめて推測する事ができます。そのような時にデータ分析は役立ちます。

問題を発見する

もし何のKPIも無ければ、膨大な数のお客様に対してその全員の動向を把握する事は出来ません。

全体像を俯瞰するある視点であるKPIを設定し、それを定常的に観察する事で、全体のどこかに異常が起きた時に気づく事が出来ます。

問題をより解きやすい形に変換する

記事の本論とずれてしまう形の引用であり恐縮ですが、この記事に掲載されていたデータ分析の事例がとても良かったので引用させていただきます。

careerhack.en-japan.com

「直近3ヶ月の売り上げが下がっている」という問題意識は、出発点として望ましいですが、 「直近3ヶ月の売り上げが下がっている。どうしたら良いか?」という問題設定のままでは漠然としていて解くのが困難です。この問にそのまま答えようとすれば「皆で頑張る」と言った漠然とした解しか得られないでしょう。

この事例では、問題の原因となっている事象を発見する事で、以下のように問題を変換していると解釈しました。

  • 変換前:「直近3ヶ月の売り上げが下がっている。どうしたら良いか?」
  • 変換後: 「4ヶ月前オープンした焼き鳥店を中心に競合が増え、1次会の利用が減っているため、直近3ヶ月の売り上げが下がっている。どうしたら良いか?」

変換後の問題設定の方がずっと具体的で有効な解を導けると思います。

アンコントローラブルな変数に対して間接的に介入できる、コントローラブルな変数を見つける」と言い換えることが出来るかも知れません。

得られた知見を貫通させる

「データ分析」という役割は、ある意味でとてもか弱いです。
データ分析で得られた知見は、理解し、活用され、お客様の元に届かない限り価値が無いからです。

成果が出るかどうかが、「 プランナー 、エンジニア、デザイナーなど他の専門家に理解して貰えるかどうか、信頼して貰えるかどうか」に強く依存した役割であると言えます。

そこで、それら他の専門家の方々に、データ分析について良く理解していただける下地を作る事が必要だと考えています。

そのために、データ分析についてチームメンバー全員により詳しくなって貰えるような活動を行ったりすることで、そのような下地の形成に繋がると考えています。

成果は組織の外にしかありません。価値を組織の外まで貫通させなければなりません。

データ分析の道具

よく使われている言語

データ分析ではどのようプログラミング言語が使われているでしょうか?

R,Python,SQL, AWK...色々あると思いますが、

  • SQLtableや、Rのdata.frameのような表に近い形式のデータ構造を扱えて、

  • SQLそのものや、Rのdplyrのようなデータに対してクエリを発効できる機能を持った、

言語が使われるケースが多いように感じます。

また、Rのggolot2のようにデータをグラフとしてプロットする機能が使えると、出来る事の種類がぐっと増えます。

キャッチアップ

プログラミング言語の習得方法として、何か手頃なサイズのプログラムを組んで見る。という方法があります。データ分析についても何か手頃な分析をしてみると未知の言語の使い方が効率的に学習できると思います。kaggleのチュートリアル課題などがお勧めです。

shinya131-note.hatenablog.jp

お勧めの書籍

最後に、データ分析を行ってみたいと考えている方にお勧めの書籍を紹介します。

www.amazon.co.jp

この本は、データ分析の初学者に対して、データ分析についてとても広い視野で解説してくれる本です。

分析の目的をどのように設定すべきか分析の結果をどのように製品に反映するかという前後の工程も含めて解説してくれているとともに、分析その物についての考え方も解説してくれる本で大変参考になりました。

まとめ

  • ネイティブアプリ開発チームが作っているのは、体感という商品です。
  • 体感はお客様の頭の中で発生していて直接知ったり操作したりする事は出来ません。
  • データはお客様の仕草でありお客様の影です。
  • データを分析することで、お客様の体感を推測したり、問題をより解きやすい形に変換したりする事が出来ます。
  • データ分析を通して得られた知見は、使われなければ何の価値も生みません。
  • kaggleはデータ分析のためのプログラミング言語習得の練習として適しています。
  • 「データ解析の実務プロセス入門」おすすめです。

以上です。何かの参考になれば幸いです。


【その1】ドリコム Advent Calendar 2015の22日目はFuyaさんの記事です。

*1:Daily Active Users: ある一日の内にアクティブになったお客様の数。なにをもってアクティブになったと考えるかにはいくつかの考え方がありますが、ここでは単にゲームを起動してくださった方の人数としました

R : POSIXctをas.Date() する時にtzを指定しない場合、POSIXctのタイムゾーンに何が設定されていてもUTCとみなして処理されているのでは?

POSIXctはタイムゾーンを伴った日時型であり、これをas.Date()という組み込み関数に渡すと、Dateという日時型に変換できる。

この関数の挙動にどうにも違和感がある。
どうもこの関数、POSIXctをas.Date() する時にtzを指定しない場合、POSIXctのタイムゾーンに何が設定されていてもUTCとみなして処理されているようなのだ。

> as.POSIXct('2015-01-01 00:00:00')
[1] "2015-01-01 JST"
> as.Date(as.POSIXct('2015-01-01 00:00:00'))
[1] "2014-12-31"
> as.Date(as.POSIXct('2015-01-01 00:00:00', tz="UTC"))
[1] "2015-01-01"

実装を読んでみた

as.DateはRにおいて、総称関数と呼ばれるタイプの関数である。
これは、与えられた引数の型を判定して、型に応じて異なる処理を行う仕組みである。
この総称関数の実装を読むには以下のようにする。

> methods(as.Date)
[1] as.Date.IDate*    as.Date.POSIXct   as.Date.POSIXlt   as.Date.character as.Date.date      as.Date.dates     as.Date.default  
[8] as.Date.factor    as.Date.numeric  
see '?methods' for accessing help and source code

> as.Date.POSIXct
function (x, tz = "UTC", ...) 
{
    if (tz == "UTC") {
        z <- floor(unclass(x)/86400)
        attr(z, "tzone") <- NULL
        structure(z, class = "Date")
    }
    else as.Date(as.POSIXlt(x, tz = tz))
}
<bytecode: 0x5d0f708>
<environment: namespace:base>

as.Date.POSIXcttzにデフォルト引数として"UTC"が指定されている。

function (x, tz = "UTC", ...) 

これでは、引数のPOSIXcttzがなんであれ、(as.Date()tzが明示的に指定されない限り)"UTC"として処理されてしまう。

(…と、読み取ったんですが合ってますかね? 何か見落としがあったら教えて下さい。)

現状のas.Dateのインターフェースデザインの課題

このようなAPIのデザインはフールプルーフの原則にのっとっていないと感じる。
この関数は、「as.Date()を使う時はtzを明示的に指定する必要がある」という認識を欠いた人に対して手痛い打撃を与えることになる。

もし自分が実装者だったら?

  • tzを明示的に指定しない場合に例外を出すか、引数となったPOSIXcttzを使用するようにする。
    • 今から変えたら後方互換性が凄い事になるので無理だろうけど…

自分もこういうデザインをしないように気をつけます…

ggplot2 ヒートマップの並び順を変更したい。

stackoverflow.com

factorのlevels順に表示されるっぽいので、levelsを任意の順番に並び替えればできるらしい。できた。

R 「エラー: 関数 "grid.newpage" を見つけることができませんでした」と言われる

結論

library(grid)する。

> # エラーになる
> grid.newpage()
 エラー:  関数 "grid.newpage" を見つけることができませんでした 
>
> # gridを読み込めば大丈夫
> library(grid)
> grid.newpage() 

説明

grid.newpage()ggplot2に含まれていない。

R data.tableから一定量のレコードをサンプリングしたい

結論

dplyrつかう。

library(dplyr)

dt %>% sample_n(100) # 100レコード取り出す
dt %>% sample_frac(0.1) # 全レコードの10%を取り出す

R ある関数が現在のスコープに存在するか調べたい

結論

たとえばlibrary()関数が現在のスコープに存在するか確認する場合:

target_function_name <- 'library'
exists(target_function_name)

[R] キーワード付き引数みたいな事がしたい

  • rubyで言うところのキーワード付き引数みたいな事がしたい。

結論

  • 普通に引数を定義するだけで、キーワード付き引数のように振る舞う。
function_argument_biheibia_test <- function(argument1, argument2) {
  print(argument2)
}
function_argument_biheibia_test(argument2='test_message')

# => "test_message"