Exploring preferential attachment in networks in R


While TAing an undergraduate course called Networked Life (NETS112), at the Engineering School at Penn last fall, the class came across an interesting phenomenon: “success breeds success” or “cumulative advantage". So, I became interested in how cumulative advantage plays out in networks?

To help visualize this question, I adapted R scripts from Albert Barabási’s team (https://github.com/ggData/barabasi) and ran the following SHINY app below. This interactive app aims to show how in some real-world contexts (e.g. the web) the more connected a node is, the more likely it is to receive new links. In contrast, this is not the case in a purely random model of the world.

Please note the app works best with a Desktop browser (not mobile).

The full R script to deploy the app can be found at the very end!


Shiny APP R Script

library(shiny)
library(igraph)
library(ggplot2)
library(markdown)
library(rmarkdown)
library(shinyWidgets)
library(bslib)



# Define UI for network exploration 
UI = shinyUI(fluidPage(
  
  titlePanel("Preferential Attachment Network Explorer"),
  theme = bs_theme(version = 4, bootswatch = "spacelab"),
  # Sidebar with controls 
  sidebarLayout(
    sidebarPanel(
      h5("By: Mia Jovanova"),
      h6("This interactive exploration shows connectivity patterns across two families of networks."),
      h6("Select network options and compare resulting output:"),
      br(),
      radioButtons("graph", "Graph type:",
                   c("Preferential Attachment" = "barabasi",
                     "Random Attachment" = "erdos")
      ),
      sliderInput("n", 
                  "Number of nodes:", 
                  value = 25,
                  min = 5, 
                  max = 100),
      conditionalPanel("input.graph == 'erdos'",
                       sliderInput("p", 
                                   "Probability of attachment:", 
                                   value = 0.1,
                                   min = 0, 
                                   max = 0.3)
      ),
      hr(),
      h5("Background:"),
      h6("Preferential attachment:"), 
      a(href="https://en.wikipedia.org/wiki/Barab%C3%A1si%E2%80%93Albert_model", "Barabasi scale-free networks"),
      h6("Random attachment:"), 
      a(href="https://en.wikipedia.org/wiki/Erd%C5%91s%E2%80%93R%C3%A9nyi_model", "Erdos-Renyi model"),
      br(),
      h6("Reference:"),
      a(href="https://github.com/ggData/barabasi", "Github source code"),
    ),
    
    # Show a tabset that includes a plot, summary, and table view
    # of the generated distribution
    mainPanel(tabsetPanel(type = "tabs", 
                          tabPanel("Network output", 
                                   verticalLayout(
                                     plotOutput("netplot"),
                                     fluidRow(
                                       column(4,
                                              h4("Average path length"),
                                              verbatimTextOutput("path"),
                                              p("The average number of hops from any node to any other
                                                    node in the graph (average degrees of separation).")
                                       ),
                                      
                                       column(4,
                                              h4("Graph Density"),
                                              verbatimTextOutput("graph_density"),
                                              p("Ratio of actual number of edges to total 
                                                    number of possible edges")
                                       )
                                     )
                                   )
                          ),
                          tabPanel("Degree Distribution", 
                                   verticalLayout(
                                     p("Relative distribution: the proportion of nodes having a given 
                                           number of connected neighbours."),
                                     hr(),
                                     plotOutput("degdist")
                                   )
                          ),
    )
    )
  )
))

Server = shinyServer(function(input, output) {
  
  graph <- reactive({ 
    if (input$graph == "barabasi") {
      barabasi.game(input$n, directed=FALSE)
    } else {
      erdos.renyi.game(input$n, input$p)
    }
  })
  
  output$netplot <- renderPlot({
    plot(graph(), vertex.color = adjustcolor("SkyBlue2", alpha.f = .5), 
         vertex.label.color = adjustcolor("black", .5))
  })
  
  # Generate a summary of the data
  output$path <- renderPrint({
    average.path.length(graph())
  })
  
  output$graph_density <- renderPrint({
    graph.density(graph())
  })
  
  output$degdist <- renderPlot({
    p <- degree.distribution(graph())
    d <- 1:length(p)
    data <- data.frame(proportion=p, degree=d)
    p <- ggplot(data, aes(x=degree, y=proportion))
    p <- p + geom_point() + geom_line() + ylim(0,1.0)
    p <- p + theme_minimal() 
    p <- p + xlab("Degree (Number of neighbours)")
    p <- p + ylab("Proportion of all Nodes")
    print(p)
  })
  
})

# Run the application 
shinyApp(ui = UI, server = Server)