Last active
April 30, 2018 15:28
-
-
Save EliotAndres/10d87567a2f81209f4c9be62c421a5df to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Plotting your friend network using Plotly, NetworkX and python-louvain\n", | |
"\n", | |
"\n", | |
"In this notebook I'll show you how to plot a friend graph.\n", | |
"\n", | |
"Original notebook author: [Lucas Allen](https://twitter.com/lucasallenio), [original code](https://github.com/lgallen/twitter-graph)\n", | |
"\n", | |
"Author: [Eliot Andres](https://twitter.com/eliotandres)\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 23, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": [ | |
"<script>requirejs.config({paths: { 'plotly': ['https://cdn.plot.ly/plotly-latest.min']},});if(!window.Plotly) {{require(['plotly'],function(plotly) {window.Plotly=plotly;});}}</script>" | |
], | |
"text/vnd.plotly.v1+html": [ | |
"<script>requirejs.config({paths: { 'plotly': ['https://cdn.plot.ly/plotly-latest.min']},});if(!window.Plotly) {{require(['plotly'],function(plotly) {window.Plotly=plotly;});}}</script>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"import plotly.offline as py\n", | |
"from plotly.graph_objs import *\n", | |
"\n", | |
"import community\n", | |
"import networkx as nx\n", | |
"import colorlover as cl\n", | |
"import numpy as np\n", | |
"import pickle\n", | |
"\n", | |
"py.init_notebook_mode(connected=True)\n", | |
"\n", | |
"# This is your Facebook id. It can also be a number\n", | |
"CENTRAL_ID = 'eliotandres'\n", | |
"\n", | |
"# This is the pickle file containing the raw graph data\n", | |
"GRAPH_FILENAME = 'friend_graph_clean.pickle'" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 24, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"\n", | |
"# To generate the friend graph, see:\n", | |
"# friend_graph a dict of lists in the form {'friend1': ['friend2, 'friend3']}\n", | |
"with open(GRAPH_FILENAME, 'rb') as f:\n", | |
" friend_graph = pickle.load(f)\n", | |
"\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 26, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Firtered out 46 items\n" | |
] | |
} | |
], | |
"source": [ | |
"\n", | |
"# First, we'll clean the edges of the grap\n", | |
"edges = []\n", | |
"nodes = [CENTRAL_ID]\n", | |
"\n", | |
"# Only keep friends with at least 2 common friends\n", | |
"central_friends = {}\n", | |
"\n", | |
"for k, v in friend_graph.items():\n", | |
" # This contains the list number of mutual friends.\n", | |
" # Doing len(v) does not work because ometimes instead of returning mutual\n", | |
" # friends, Facebook returns all the person's friends\n", | |
" intersection_size = len(np.intersect1d(list(friend_graph.keys()), v))\n", | |
" if intersection_size > 2:\n", | |
" central_friends[k] = v\n", | |
" \n", | |
"print('Firtered out {} items'.format(len(friend_graph.keys()) - len(central_friends.keys())))\n", | |
"\n", | |
"# Extract edges from graph\n", | |
"for k, v in central_friends.items():\n", | |
" for item in v:\n", | |
" if item in central_friends.keys():\n", | |
" edges.append((k, item))\n", | |
"\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 28, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Added 13257 edges\n" | |
] | |
} | |
], | |
"source": [ | |
"# Create the graph. \n", | |
"# Small reminder: friends are the nodes and friendships are the edges here\n", | |
"G = nx.Graph()\n", | |
"G.add_nodes_from([CENTRAL_ID])\n", | |
"G.add_nodes_from(central_friends.keys())\n", | |
"\n", | |
"G.add_edges_from(edges)\n", | |
"print('Added {} edges'.format(len(edges) ))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Define Positioning\n", | |
"\n", | |
"Plotly does not have a true library for graph theory. We will use NetworkX to define the position of coordinates of a scatterplot and the attributes to describe the nodes of that scatterplot. Once a positioning is determined above, define it below as \"pos\", a dictionary of x and y coordinates." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 29, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"pos = nx.spring_layout(G)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\n", | |
"## Community detection\n", | |
"\n", | |
"In this step we'll color the different friends clusters. We'll use the louvain method described in Fast unfolding of communities in large networks, Vincent D Blondel, Jean-Loup Guillaume, Renaud Lambiotte, Renaud Lefebvre, Journal of Statistical Mechanics: Theory and Experiment 2008(10), P10008 (12pp). Details [here](https://github.com/taynaud/python-louvain)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 30, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"part = community.best_partition(G)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Define scatter_nodes() and scatter_edges()\n", | |
"\n", | |
"These functions create Plotly \"traces\" of the nodes and edges using the layout defined in \"pos\". " | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 32, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Get a list of all node ids\n", | |
"nodeID = G.node.keys()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 33, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# The louvain community library returns cluster ids, we have turn them into colors using color lovers\n", | |
"\n", | |
"colors = cl.scales['12']['qual']['Paired']\n", | |
"\n", | |
"def scatter_nodes(pos, labels=None, color='rgb(152, 0, 0)', size=8, opacity=1):\n", | |
" # pos is the dict of node positions\n", | |
" # labels is a list of labels of len(pos), to be displayed when hovering the mouse over the nodes\n", | |
" # color is the color for nodes. When it is set as None the Plotly default color is used\n", | |
" # size is the size of the dots representing the nodes\n", | |
" # opacity is a value between [0,1] defining the node color opacity\n", | |
"\n", | |
" trace = Scatter(x=[], \n", | |
" y=[], \n", | |
" mode='markers', \n", | |
" marker=Marker(\n", | |
" showscale=False,\n", | |
" colorscale='Greens',\n", | |
" reversescale=True,\n", | |
" color=[], \n", | |
" size=10,\n", | |
" line=dict(width=0)))\n", | |
" for nd in nodeID:\n", | |
" trace['x'].append(pos[nd][0])\n", | |
" trace['y'].append(pos[nd][1])\n", | |
" color = colors[part[nd] % len(colors)]\n", | |
" trace['marker']['color'].append(color)\n", | |
" attrib=dict(name='', text=labels , hoverinfo='text', opacity=opacity) # a dict of Plotly node attributes\n", | |
" trace=dict(trace, **attrib)# concatenate the dict trace and attrib\n", | |
" trace['marker']['size']=size\n", | |
"\n", | |
" return trace " | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 34, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def scatter_edges(G, pos, line_color='#a3a3c2', line_width=1, opacity=.2):\n", | |
" trace = Scatter(x=[], \n", | |
" y=[], \n", | |
" mode='lines'\n", | |
" )\n", | |
" for edge in G.edges():\n", | |
" trace['x'] += [pos[edge[0]][0],pos[edge[1]][0], None]\n", | |
" trace['y'] += [pos[edge[0]][1],pos[edge[1]][1], None] \n", | |
" trace['hoverinfo']='none'\n", | |
" trace['line']['width']=line_width\n", | |
" if line_color is not None: # when it is None a default Plotly color is used\n", | |
" trace['line']['color']=line_color\n", | |
" return trace " | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 35, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Node label information available on hover. Note that some html tags such as line break <br> are recognized within a string.\n", | |
"labels = []\n", | |
"\n", | |
"for nd in nodeID:\n", | |
" labels.append('{} ({})'.format(nd, part[nd],))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 36, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"trace1 = scatter_edges(G, pos, line_width=0.25)\n", | |
"trace2 = scatter_nodes(pos, labels=labels)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Configure the plot and call Plotly" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 46, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"width=600\n", | |
"height=600\n", | |
"axis=dict(showline=False, # hide axis line, grid, ticklabels and title\n", | |
" zeroline=False,\n", | |
" showgrid=False,\n", | |
" showticklabels=False,\n", | |
" title='' \n", | |
" )\n", | |
"layout=Layout(title= '',\n", | |
" \n", | |
" font= Font(),\n", | |
" showlegend=False,\n", | |
" autosize=False,\n", | |
" width=width,\n", | |
" height=height,\n", | |
" xaxis=dict(\n", | |
" title='Facebook friend graph',\n", | |
" titlefont=dict(\n", | |
" size=14,\n", | |
" color='#fff'),\n", | |
" showgrid=False,\n", | |
" \n", | |
" showline=False,\n", | |
" showticklabels=False,\n", | |
" zeroline=False\n", | |
" ),\n", | |
" yaxis=YAxis(axis),\n", | |
" margin=Margin(\n", | |
" l=40,\n", | |
" r=40,\n", | |
" b=85,\n", | |
" t=100,\n", | |
" pad=0,\n", | |
" \n", | |
" ),\n", | |
" hovermode='closest',\n", | |
" paper_bgcolor='rgba(0,0,0,1)',\n", | |
" plot_bgcolor='rgba(0,0,0,1)'\n", | |
" )\n", | |
"\n", | |
"\n", | |
"data=Data([trace1, trace2])\n", | |
"\n", | |
"fig = Figure(data=data, layout=layout)\n", | |
"py.iplot(fig)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.6.4" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 1 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment