GDSを使用してSlackのインフルエンサーを割り出す #Neo4j #GDS #データ分析
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
概要
Neo4jによるデータ分析の知見を得るため、社内のSlackのデータを元に、Neo4jを使用したデータ分析を試してみました。 SlackのデータをAPIで取得してNeo4jのグラフデータにし、それを元にGDSの中心性解析アルゴリズム(PageRank)を使用して影響力のある発言者を割り出しました。
直感に反しない結果が得られたので、GDSを使用した分析の一例としてご参考にしていただければと思います。
こちらが今回の取り組みの概要です。
下記では、どんな手順で、どんな工夫を行ったか、詳細に解説していきます。
クリエーションライン社ではフルリモートで作業をしています。
チームでのコミュニケーションはSlackが主体となるため、Slackのデータに着目しました。
コミュニケーションが活発な人がより価値を生み出しているという仮説の元に、重要人物を特定できないかと考えました。
分析は上記のように2つのステップで行いました。
工夫した点をまとめました。
下記で行う手順の説明の中で、それぞれについて詳しく解説していきます。
1つ目のステップでSlackのデータをNeo4jのグラフデータに変換します。
データの変換のために、Pythonのスクリプトを作成しました。
まず、Pythonスクリプトにより変換された結果のグラフデータに含まれるノードについて説明します。
今回分析対象としたのは、投稿者を表すPersonノードと、スレッドメッセージを表すThreadMessageノードです。
投稿者とは、Slackにメッセージを投稿したそれぞれの人のことです。
スレッドメッセージとは、Slackでスレッドの各要素となっているメッセージのことで、スレッド1つに対し1つの親メッセージと0個以上の子メッセージがあります。
ここで工夫した点があります。
今回は、同じスレッドで話をしているということに着目してデータを分析するので、Neo4jのグラフデータでもメッセージをスレッドごとに整理しました。
次に、Pythonスクリプトにより変換された結果のグラフデータに含まれるリレーションシップについて説明します。
今回分析対象としたのは、メッセージをSlackに投稿するという意味のHASリレーションシップと、スレッド内でメッセージが別のメッセージの次に投稿されているという意味のTHREAD_NEXT_TOリレーションシップです。
実際のグラフデータを見てみましょう。
中心にある赤い丸は'R.N.'という名前を持つ投稿者です。
ここから9本のHASリレーションシップが伸びているのが見て取れます。このことから、'R.N.'は合計で9つのメッセージを投稿しているということが分かります。
また、それぞれのメッセージを表す青や水色の丸の間にTHREAD_NEXT_TOリレーションシップがあるのが見て取れます。これらにより、どのメッセージがどのメッセージの次に投稿されているのかが分かるようになっています。
2つ目のステップでグラフデータを加工し、GDSによる分析を行います。
大まかな方針を考えました。同じスレッドで話している人同士をリレーションシップで結ぶことを目標にします。
ここで工夫した点です。
人同士の関係を考えるのに、同じスレッドで話しているかどうかという点に着目しました。
同じスレッドで話している人同士をリレーションシップで結ぶために、グラフデータを加工します。
最初のステップとして、それぞれのスレッドを表すThreadノードを作成し、各スレッドのメッセージ(青丸)からリレーションシップを張りたいと思います。
まず、それぞれのThreadノードを作成し、各スレッドに1つずつ存在する親メッセージからBELONGS_TOリレーションシップを張ります。
このような結果が得られました。
Threadノードは黄色の丸で表されています。
次に、親メッセージ以外の全てのメッセージからも、Threadノードに対してBELONGS_TOリレーションシップを張ります。
このようにして、全てのメッセージから、それぞれが属しているThreadノードに対して、BELONGS_TOリレーションシップが張られました。
次に、それぞれの投稿者が各スレッドに投稿した回数をcountプロパティに持つPOSTリレーションシップを張ります。
図の場合だと、佐藤さんは右側の3つのメッセージ(青丸)からなるスレッドBのうち、子メッセージ2つに対してHASリレーションシップを持っているので、佐藤さんはスレッドBに2回投稿しています。従って、佐藤さんからスレッドBに対してcount=2のプロパティを持つPOSTリレーションシップを張ります。
また、佐藤さんは左側の2つのメッセージからなるスレッドAのうち、親メッセージ1つを投稿しているので、佐藤さんからスレッドAに対してcount=1のプロパティを持つPOSTリレーションシップを張ります。
さらに、山田さんは右側のスレッドBのうち、親メッセージ1つを投稿しているので、山田さんからスレッドBに対してcount=1のプロパティを持つPOSTリレーションシップを張ります。
山田さんは左側のスレッドAに対してはメッセージを投稿していないので、山田さんからスレッドAに対してPOSTリレーションシップは張られません。
このような結果が得られます。
さらに、2人の人が同一のスレッドに対して投稿している場合、投稿回数の和をcountプロパティに持つリレーションシップCOMMUNICATEをその2人の間に張ります。
山田さんと佐藤さんは、2人ともスレッドBに対して投稿しており、投稿回数は山田さんが1回、佐藤さんが2回と投稿しているので、山田さんと佐藤さんの間にはcount=1+2=3のプロパティを持つCOMMUNICATEリレーションシップを張ります。
(スレッドAについて、佐藤さんは1回投稿していますが、山田さんは投稿していませんので、こちらの回数はcountプロパティには影響しないことに注意してください)
ここで工夫した点です。
リレーションシップの追加は通常CREATEクエリを使用しますが、この場合はそうすると山田さんから佐藤さんへリレーションシップが張られるのと同時に佐藤さんから山田さんにもリレーションシップが張られてしまいます。
このことを防ぐため、CREATEクエリの代わりにMERGEクエリを使用し、作成するリレーションシップの向きも指定しないようにしています。(こうすると、2ノード間にどちらか片方の向きのリレーションシップが1つだけ張られます)
<下記は補足です>
sum関数が使用されているのが見て取れると思います。
これはスレッドB以外にも山田さんと佐藤さんが2人とも投稿しているスレッドがあった場合、そのスレッドへの投稿回数の和もcountプロパティに加算するために用いています。sumの結果を2で割っているのは、countプロパティを計算するときに、山田さんと佐藤さんを入れ替えた場合も加算され、結果として2倍の値になってしまうので、その補正のためです。
COMMUNICATEリレーションシップが張られました。
countプロパティの値は3です。
PersonノードとCOMMUNICATEリレーションシップのみを抽出してみました。
これを見ることで、例えば、'K.Y.'と'R.N.'は同じスレッドに投稿したことがあるが、'K.Y.'と'F.K.'は同じスレッドに投稿したことがないといったことが分かります。
人同士の関係が視覚的に明確になったと言えるでしょう。
いよいよGDSによる分析に入っていきます。
上記の手順でグラフデータを加工することによってCOMMUNICATEリレーションシップが得られました。
このリレーションシップは、同じスレッドで話している人同士の間に張られており、同じスレッドで話している回数の和をcountプロパティに持ちます。
このCOMMUNICATEリレーションシップを元にPageRankを計算すれば、多くの人と関わりのある重要人物を割り出すことができそうです。
COMMUNICATEリレーションシップからPageRankを計算してみます。countプロパティを重みとして考慮します。
ここで、工夫した点です。
countプロパティを考慮することで、単に2人が同じスレッドで話しているということだけでなく、話している回数も計算結果に反映します。
countプロパティを考慮しないで計算した場合より、直感に一致する結果が得られました。
PageRankについての詳細な説明はこちら。ページランク - Wikipedia
PageRankはTwitterなどのSNSの分析にも用いられます。今回のケースはその応用です。
参考論文 - Social Network Analysis of Twitter to Identify Issuer of Topic using PageRank
PageRankの計算は、プロジェクショングラフの作成とGDSのPageRankアルゴリズムの呼び出しの2ステップから成ります。
GDSの概要やクエリの書き方については、下記の弊社ブログや公式ドキュメントを参照してください。
Neo4j SandBox Graph Data Science プロジェクトを体験してみた(前編) #Neo4j #SandBox #GDS
公式ドキュメント - Projecting graphs using native projections
公式ドキュメント - PageRank - Neo4j Graph Data Science
ここで、工夫した点です。
赤字で書いた部分は、PageRankを計算するときに、countプロパティを重みとして考慮するための設定です。
こちらの設定により、同じスレッドへの投稿回数を考慮してPageRankの計算を行います。
もう1点、工夫した点です。
2つのPersonノードの間にはただ1つのCOMMUNICATEリレーションシップが張られますが、どちらの方向に張られるかは今回意味をなしません。
PageRankは本来リレーションシップの向きを考慮するアルゴリズムですが、今回はCOMMUNICATEリレーションシップの方向を無視し、無向グラフとして解釈させる必要がありました。
そのために、赤字で書いた部分の設定を行っています。
実際にPageRankを計算し、上位5件を取り出したのが右の結果になります。
Slackでの会話に積極的だと思われる人が上位に来る結果となり、同じチームのメンバーとして直感に一致しました。
最後に工夫した点をまとめます。
今回はPageRankの重み付け要素として、同じスレッドで話している回数の合計値を使用しましたが、これが適切かどうかは更なる検証が必要と考えられます。
より適切な重み付けやより適切なアルゴリズムの選定について、コメントがございましたらお知らせいただければ幸いです。