2019年11月20日水曜日

PlotlyでStack OverflowのDeveloper Surveyを可視化する

Pythonで共起ネットワークを作成するでは、PythonライブラリのMatplotlibとNetworkXで夏目漱石の『こころ』のテキストをもとに共起ネットワークを作成した。作成したネットワーク図は画像なので、単純に表示して見るだけなのだが、PythonライブラリのPlotlyを使うとインタラクティブな可視化ができる。

今回は、KH CoderでStack OverflowのDeveloper Surveyを可視化するで利用したStack Overflow Annual Developer Surveyのデータを使って、インタラクティブな共起ネットワークを作成する。


環境


Windows10(1903)のWSL(Ubuntu 18.04)とJupyter Notebookを使用。




必要ライブラリなどのインストール


Plotlyはpipでインストールできる。


他にNetworkXとGraphvizも使う。インストールはPythonで共起ネットワークを作成するを参照。


データのクリーニングとJaccard係数の算出


使用するデータはStack Overflow Annual Developer Surveyで、2019年のデータ(developer_survey_2019.zip)をダウンロードして解凍し、survey_results_public.csvをJupyter Notebookの作業ディレクトリに置いておく。KH CoderでStack OverflowのDeveloper Surveyを可視化すると同じように、過去に経験のあるプログラミング言語やデータベース、フレームワークなどの開発で使われる技術に関する質問の回答結果を使用する。データクリーニングのためのコードを流用するのでsurvey_loader.pyとして保存しておく。以下は使用する質問項目。

  • LanguageWorkedWith(プログラミング言語、スクリプト、マークアップ言語)
  • DatabaseWorkedWith(データベース)
  • PlatformWorkedWith(プラットフォーム)
  • WebFrameWorkedWith(ウェブフレームワーク)
  • MiscTechWorkedWith(その他の技術)
  • DevEnviron(開発環境)

それから、「;」区切りの回答を回答者ごとにリストにして、技術ペアごとのJaccard係数を算出する。方法はPythonで共起ネットワークを作成すると同じ関数を使用するので、Gistの全体コードをcoonetwork_sample.pyとして保存しておく。survey_loader.pyとcoonetwork_sample.pyはJupyter Notebookの作業ディレクトリに置いておく。


ネットワーク図の作成


回答ペアごとのJaccard係数を算出したら、NetworkXとGraphvizでネットワークのノードとエッジの配置を決め、そのデータを使ってPlotlyで可視化する。NetworkXで決めたノードとエッジの配置をもとにPlotlyで可視化する方法がNetwork Graphs in Pythonにあるので、これを参考にした。コードは以下の通り。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import os
import numpy as np
import pandas as pd
 
import networkx as nx
from networkx.drawing import nx_agraph
from plotly import graph_objs as go
 
# Stack Overflow Annual Developer Surveyのデータをクリーニングする関数をインポート
from survey_loader import to_df, clean_df
 
# Jaccard係数を算出する関数をインポート
from coonetwork_sample import bform2pair, pair2jaccard
 
def build_interactive_network(G, pos, pr_values):
    # Plotlyで共起ネットワークを可視化
    # nodeの大きさと色をページランクアルゴリズムによる重要度(pr_values)により変える
     
    # edgeデータの作成
    edge_x = []
    edge_y = []
    for edge in G.edges():
        x0, y0 = pos[edge[0]]
        x1, y1 = pos[edge[1]]
        edge_x.append(x0)
        edge_x.append(x1)
        edge_x.append(None)
        edge_y.append(y0)
        edge_y.append(y1)
        edge_y.append(None)
 
    edge_trace = go.Scatter(
        x=edge_x, y=edge_y,
        line=dict(width=0.5, color='#888'),
        hoverinfo='none',
        mode='lines')
 
    # nodeデータの作成
    node_x = []
    node_y = []
    for node in G.nodes():
        x, y = pos[node]
        node_x.append(x)
        node_y.append(y)
 
    node_trace = go.Scatter(
        x=node_x, y=node_y,
        text=list(G.nodes()),
        textposition='top center',
        mode='markers+text',
        hoverinfo='text',
        marker=dict(
            showscale=True,
            # colorscale options
            #'Greys' | 'YlGnBu' | 'Greens' | 'YlOrRd' | 'Bluered' | 'RdBu' |
            #'Reds' | 'Blues' | 'Picnic' | 'Rainbow' | 'Portland' | 'Jet' |
            #'Hot' | 'Blackbody' | 'Earth' | 'Electric' | 'Viridis' |
            colorscale='Rainbow',
            reversescale=False,
            color=[],
            size=10,
            colorbar=dict(
                thickness=15,
                title='Page Ranking',
                xanchor='left',
                titleside='right'
            ),
            line_width=2))
 
    # nodeの色、サイズ、マウスオーバーしたときに表示するテキストの設定
    node_adjacencies = []
    node_text = []
    for node, adjacencies in enumerate(G.adjacency()):
        # nodeに接続するedgeの数
        node_adjacencies.append(len(adjacencies[1]))
        node_text.append('{} connection(s)'.format(len(adjacencies[1])))
 
    node_trace.marker.color = pr_values
    node_trace.marker.size = [value*1000 for value in pr_values]
    node_trace.hovertext = node_text
     
    # ネットワーク図の可視化
    fig = go.Figure(data=[edge_trace, node_trace],
                 layout=go.Layout(
                    showlegend=False,
                    hovermode='closest',
                    margin=dict(b=20,l=5,r=5,t=40),
                    xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
                    yaxis=dict(showgrid=False, zeroline=False, showticklabels=False))
                    )
    fig.show()
     
def build_network(jaccard_dict):
    # 回答ペアごとのJaccard係数をもとにnodeとedgeのレイアウトを決めてネットワーク図を可視化
 
    G = nx.Graph()
 
    #  接点/単語(node)の追加
    # ソートしないとネットワーク図の配置が実行ごとに変わる
    nodes = sorted(set([j for pair in jaccard_dict.keys() for j in pair]))
    G.add_nodes_from(nodes)
 
    print('Number of nodes=', G.number_of_nodes())
 
    #  線(edge)の追加
    for pair, coef in jaccard_dict.items():
        G.add_edge(pair[0], pair[1], weight=coef)
 
    print('Number of edges=', G.number_of_edges())
 
    # nodeの配置方法の指定
    seed = 0
    np.random.seed(seed)
    # k = node間反発係数
    #pos = nx.spring_layout(G, k=0.3, seed=seed)
 
    # できるだけnodeが重ならないようにする(Graphvizを使う)
    pos = nx_agraph.graphviz_layout(
        G,
        prog='neato',
        args='-Goverlap="scalexy" -Gsep="+6" -Gnodesep=0.8 -Gsplines="polyline" -GpackMode="graph" -Gstart={}'.format(seed))
 
    # ページランクアルゴリズムによる重要度
    pr = nx.pagerank(G)
 
    # 共起ネットワークの可視化
    build_interactive_network(G, pos, list(pr.values()))
 
def main():
    df = to_df()
    df = clean_df(df)
 
    df['tech'] = df[df.columns[1:]].apply(lambda row: ';'.join(row).split(';'), axis=1)
    print(df['tech'].head())
     
    # Jaccard係数の計算
    pair_count = bform2pair(df['tech'].tolist(), min_cnt=20)
    jaccard_dict = pair2jaccard(pair_count, df['tech'].tolist(), edge_th=0.4)
     
    # 共起ネットワーク作成
    build_network(jaccard_dict)
     
if __name__ == '__main__':
    main()


結果


Jupyter Notebookでコードを実行すると以下のような共起ネットワーク図が作成された。

さらに、メニューがあってズームなどができるし、マウスオーバーでテキストなどを表示できる。



0 件のコメント:

コメントを投稿