Application shiny pour représenter votre réseau facebookA shiny app that displays your facebook network

Les scripts pour réaliser une interface web qui vous permet de représenter votre réseau facebook ? Voir l’article en anglais…
This post follows this post and this other post that, respectively, described how to extract your friendship network from facebook and how to create a Graphical User Interface with R and the package shiny. In this, I simply provide a graphical user interface made with shiny to display your facebook network, with different options for representing the nodes (color, size…). The initial function commented on this post to collect information from your facebook network was re-written due to a recent change in the facebook graph API.

An example of how the application works is provided in this movie:

and the application may be testable here (where it is hosted by rstudio but I don’t know for sure how the application might react to many simultaneous connections because gathering the information from facebook takes several minutes at each time so you’d better be patient…).

Installing necessary packages

In this example, I am using several packages: shiny (now available on CRAN repositories), igraph to manipulate networks, RCurl and rjson to import and extract data from facebook and finally, RColorBrewer to better handle colors.

ui.R

The file ui.R contains the description of the interface: mine is made of a left hand side panel where the options are set by the user (the access token pasted from the facebook graph API explorer and the meaning of node color, size and label) and of a right hand side panel where the results are displayed. The main functions used in this panel are:

  • textInput to obtain a character string from the user (here, the facebook access token);
  • selectInput to make the user select an option from a list (here, meaning of the node color, size and label);
  • checkboxInput to display a checkbox clickable by the user;
  • p, br() and HTML for various HTML tags that can be used to print free text.

The interface also contains a right hand side panel, which is the main panel where the results are displayed. Thanks to the function conditionalPanel, this panel displays a short user guide until an access token is given. Then, the main panel is divided into two tabs to organize outputs in a graphical part (where the network is displayed) and a statistical parts (where simple facts about the network are summarized). The outputs are displayed by using the functions plotOutput (that displays a graph coming from the server.R script and referenced by a name, here graph) and tableOutput (that displays a data frame coming from the server.R script and referenced by a name, here stats).

The full script is given below:

shinyUI(pageWithSidebar(
	# Title of the application
	headerPanel("Visualize your facebook network..."),

	# Left hand side panel
	sidebarPanel(
		textInput("token",strong("Copy your access token here:"), ""),
		selectInput("ncol", "Node color represents:",choices = c("community","betweenness","degree","uniform")),
		selectInput("nsize", "Node size represents:",choices = c("degree","betweenness","uniform")),
		selectInput("nname","Node names are:",choices=c("initials","facebook id","names")),
		checkboxInput("lcc"," Only the largest connected component is displayed.",TRUE),
		br(),
		p(HTML("Script sources and explanations can be found on my blog.")),
		p(HTML("This application is kindly provided by tuxette ;-)"))
	),

	# Right hand side panel
	mainPanel(
		# If the "token" text area is empty
		conditionalPanel(
			condition = "input.token == '' ",
			h4("Basic user guide"),
			p("To visualize your facebook network, please go to ", a("the facebook graph API Explorer", href="https://developers.facebook.com/tools/explorer")," to generate a token and copy it in the field on the left hand side panel. Do not forget to select proper permissions to give you access to your friends' data."),
			p("You may wait for a long time for the first network to be displayed (until all your data are collected from facebook) and then you can change the way the network is displayed by selecting various options for the node colors, size and label.")
		),
		# If the "token" text area is not empty
		conditionalPanel(
			condition = "input.token != '' ",
			tabsetPanel(
				tabPanel("Chart",p(HTML("Wait a few minutes... I'm working...")),plotOutput("chart")),
				tabPanel("Statistics",tableOutput("stats"))
			)
		)
	)
))

server.R

The server.R script reacts to changes in the input collected in the ui.R (each referenced by a label and useable with input$LABEL in the server.R script) and calculates outputs that can be numerical values, data frames, charts… This script is made of 5 main functions:

  • facebook is the generic function to collect information through the facebook graph API;
  • fb.friends uses facebook and the access token provided by the user to collect the list of friends. It is a reactive function, i.e., a function that reats to changes in the user’s input options;
  • mainnet uses facebook and the access token provided by the user to collect the information about mutual friendship among your friends. It is also a reactive function;
  • output$chart uses the two functions fb.friends and mainnet to define a network and then to display it accordingly to the options specified by the user. It uses the package igraph to manipulate networks and it is also a reactive function that reacts to changes in the options chosen for representing the network;
  • output$stats uses the two functions fb.friends and mainnet to define a network and then to calculate simple statistics. Mine told me that my network contains 150 friends among which Sabrina is the one who is the most connected.

The full script is given below:

library(RCurl)
library(rjson)
library(igraph)
library(RColorBrewer)

# Generic function that collects data from facebook through the facebook graph API
facebook =  function( path = "me", access_token = token, options){
    if( !missing(options) ){
        options = sprintf( "?%s", paste( names(options), "=",
unlist(options), collapse = "&", sep = "" ) )
    } else {
        options = ""
    }
    data = getURL( sprintf(
"https://graph.facebook.com/%s%s/?access_token=%s", path, options,
access_token ) )
    print(sprintf( "https://graph.facebook.com/%s%s/?access_token=%s",
path, options, access_token ))
    fromJSON( data )
}

# Shiny ouputs calculation
shinyServer(function(input, output) {

	# Collect information about the friends on facebook
  fb.friends = reactive(function(){
		friends = facebook( path="me/friends" , access_token=input$token)
		friends.id = sapply(friends$data, function(x) x$id)
		friends.name = sapply(friends$data, function(x) iconv(x$name,"UTF-8","ASCII//TRANSLIT"))
		initials = function(x) paste(substr(x,3,3), collapse=" ")
		friends.initial = sapply(strsplit(friends.name," "), initials)

		list("initials"=friends.initial,"name"=friends.name,"fbid"=friends.id)
	})

	# Collect mutual friendship information on facebook
	mainnet = reactive(function() {
		the.friends = fb.friends()
		N = length(the.friends$initials)
		friendship.matrix = matrix(0,N,N)
		for (i in 1:N) {
			tmp = facebook( path=paste("me/mutualfriends", the.friends$fbid[i], sep="/") , access_token=input$token)
			mutualfriends = sapply(tmp$data, function(x) x$id)
			friendship.matrix[i,the.friends$fbid %in% mutualfriends] = 1
		}
		colnames(friendship.matrix) = the.friends$name
		rownames(friendship.matrix) = the.friends$name
		friendship.matrix
  })

	# Display the network
	output$chart = reactivePlot(function() {
	the.friends = fb.friends()
	adj = mainnet()
	the.full.graph = graph.adjacency(adj,mode="undirected")
	V(the.full.graph)$initials = the.friends$initials
	V(the.full.graph)$fbid = the.friends$fbid
	if (input$lcc) {
		the.clusters = clusters(the.full.graph)
		the.graph = induced.subgraph(the.full.graph,which(the.clusters$membership==which.max(the.clusters$csize)))
	} else {
		the.graph = the.full.graph
	}
	if (input$ncol=="uniform") {
		V(the.graph)$color = rep("seagreen",vcount(the.graph))
	} else if (input$ncol=="degree") {
		dclust = cut(degree(the.graph),9,label=F)
		V(the.graph)$color = brewer.pal(9,"YlOrRd")[dclust]
	} else if (input$ncol=="betweenness") {
		bclust = cut(betweenness(the.graph),9,label=F)
		V(the.graph)$color = brewer.pal(9,"YlOrRd")[bclust]
	} else if (input$ncol=="community") {
		all.cc = clusters(the.graph)
		lcc = induced.subgraph(the.graph,which(all.cc$membership==which.max(all.cc$csize)))
		clust = multilevel.community(lcc)
		V(the.graph)$color = rep("yellow",vcount(the.graph))
		V(the.graph)$color[match(clust$names,V(the.graph)$name)] = brewer.pal(12,"Paired")[clust$membership]
	}

	if (input$nsize=="degree") {
		vsize = degree(the.graph)/max(degree(the.graph))*8
	} else if (input$nsize=="betweenness") {
		vsize = betweenness(the.graph)/max(betweenness(the.graph))*8
	} else if (input$nsize=="uniform") {
		vsize = rep(5,vcount(the.graph))
	}

	if (input$nname=="names") {
		vname = V(the.graph)$name
	} else if (input$nname=="facebook id") {
		vname = V(the.graph)$fbid
	} else if (input$nname=="initials") {
		vname = V(the.graph)$initials
	}

	par(mar=rep(0,4))
	set.seed(21121444)
	plot(the.graph,layout=layout.auto,vertex.label=vname,vertex.label.font=1,vertex.label.color="black",vertex.color=V(the.graph)$color, vertex.frame.color=V(the.graph)$color,vertex.size=vsize)
	})

	# Calculate basic statistics
	output$stats = reactiveTable(function() {
		the.friends = fb.friends()
		adj = mainnet()
		the.full.graph = graph.adjacency(adj,mode="undirected")
		deg = degree(the.full.graph)
		the.bet = betweenness(the.full.graph)
		data.frame(
			Statistics = c("Number of friends", "Density", "Transitivity", "Average degree", "Max degree", "Average betweenness", "Max betweenness", "Best degree", "Best betweenness"),
			Values = as.character(c(vcount(the.full.graph), paste(round(graph.density(the.full.graph)*100,0),"%",collapse=" "), paste(round(transitivity(the.full.graph)*100,0),"%",collapse=" "), round(mean(deg),1), max(deg), round(mean(the.bet),0), round(max(the.bet),0), names(which.max(deg)), names(which.max(the.bet)))),
			stringsAsFactors=FALSE)
	})
})

In case your would like to use and/or modify this application on your own computer (without the need to rely on the rstudio server), make sure all the necessary packages are installed, then copy/paste the two scripts ui.R and server.R in given directory (for instance, “DIR”) and, in R, run

library(shiny)
runApp("PATH2DIR/DIR")

where “PATH2DIR” is the path from your working directory to “DIR”.