Replicate the interactive network visualization but relying on your publication data. 1. Decide whether you want to plot the co-author relationship starting from the one-mode network or whether you want to link authors to papers and then transform this to a one-mode network. Recall that the latter retains solo authors, in case you have those in your data set.
I decided to start from the two mode network because I wanted to retain the solo authors in my data set.
I started with two-mode form and then get the co-author via transformation because I want to retain solo authors in the network.
We wanted to visualize the data through this progress because we want this data-storytelling process to be more interactive, more informative, and it will also help validate our methodological narrative in return.
########################Complex Systems Networks##########################
library(igraph)
##
## Attaching package: 'igraph'
## The following objects are masked from 'package:stats':
##
## decompose, spectrum
## The following object is masked from 'package:base':
##
## union
library(splitstackshape)
getwd()
## [1] "/Users/aihe/Desktop/UPenn/[Summer 23] SSNA"
setwd("/Users/aihe/Desktop")
source <- read.csv("AI_SLL.csv")
# clean the data, keep only authors and articles
source <- source[source$Author.s..ID!="[No author id available]",]
### create a author-author matrix through transformation of a two-mode edgelist#
authors <- as.data.frame(source[,2])
colnames(authors) <- "AU"
source_split <- cSplit(authors, splitCols = "AU", sep = ";", direction = "wide", drop = TRUE)
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
#No needed but just in case
source_split <- data.frame(lapply(source_split, trimws), stringsAsFactors = FALSE)
mat_source_split <- as.matrix(source_split)
combined <- cbind(source$EID, mat_source_split)
mat_combined_eid_source_split <- as.matrix(combined)
edgelist_two_mode <- cbind(mat_combined_eid_source_split[, 1], c(mat_combined_eid_source_split[, -1]))
edgelist_two_mode <- edgelist_two_mode[!is.na(edgelist_two_mode[,2]), ]
df_edgelist_twomode <- as.data.frame(edgelist_two_mode)
#this is edgelist between EID & author ID (w/ replication)
head(edgelist_two_mode)
## [,1] [,2]
## [1,] "2-s2.0-85143794140" "57223640712"
## [2,] "2-s2.0-85160812282" "57753915200"
## [3,] "2-s2.0-85160269286" "58288848400"
## [4,] "2-s2.0-85159570136" "58258344100"
## [5,] "2-s2.0-85158902849" "58061860400"
## [6,] "2-s2.0-85147960691" "57301216200"
dim(edgelist_two_mode)
## [1] 737 2
g2 <- graph.edgelist(edgelist_two_mode[, 2:1], directed = TRUE)
g2
## IGRAPH 0e29c60 DN-- 906 737 --
## + attr: name (v/c)
## + edges from 0e29c60 (vertex names):
## [1] 57223640712->2-s2.0-85143794140 57753915200->2-s2.0-85160812282
## [3] 58288848400->2-s2.0-85160269286 58258344100->2-s2.0-85159570136
## [5] 58061860400->2-s2.0-85158902849 57301216200->2-s2.0-85147960691
## [7] 50262958000->2-s2.0-85148333106 57221813377->2-s2.0-85142524779
## [9] 57769896800->2-s2.0-85132609552 41561548400->2-s2.0-85159058001
## [11] 57222072140->2-s2.0-85156121354 58205160300->2-s2.0-85154056174
## [13] 57191156064->2-s2.0-85152072289 58158727000->2-s2.0-85150968581
## [15] 57209902552->2-s2.0-85150939650 57211158344->2-s2.0-85150790502
## + ... omitted several edges
g2 <- graph.edgelist(edgelist_two_mode[, 2:1], directed = FALSE)
plot(g2)
# which one is it?
V(g2)$type <- V(g2)$name %in% edgelist_two_mode[ , 1]
table(V(g2)$type)
##
## FALSE TRUE
## 727 179
#creating ID bank
getwd()
## [1] "/Users/aihe/Desktop"
source <- read.csv("/Users/aihe/Desktop/AI_SLL.csv")
source <- source[source$Author.s..ID!="[No author id available]",]
source$au <- gsub(" Jr.,", "",
gsub(" II.,", "",
gsub(" Jr.", "",
gsub(" M.S.", "",
gsub(" M.S.,", "",
gsub(" II.", "", source[,1]))))))
source$au <- gsub("\\.,", ";", source$au)
source$au <- tolower(source$au)
df_all_authors <- as.data.frame(source[ , ncol(source)])
colnames(df_all_authors) <- "AU"
authornames_split <- cSplit(df_all_authors, splitCols = "AU", sep = ";", direction = "wide", drop = TRUE)
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
dim(authornames_split)
## [1] 179 41
df_authors_id <- as.data.frame(source[ , 2])
colnames(df_authors_id) <- "AU"
author_id_split <- cSplit(df_authors_id, splitCols = "AU", sep = ";", direction = "wide", drop = TRUE)
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
dim(author_id_split)
## [1] 179 41
df_authorid_authorname_unlisted <- data.frame(id = unlist(author_id_split), names = unlist(authornames_split))
df_authorid_authorname_unlisted <- df_authorid_authorname_unlisted[!is.na(df_authorid_authorname_unlisted$id),]
dim(df_authorid_authorname_unlisted)
## [1] 737 2
df_authorid_authorname_unlisted <- df_authorid_authorname_unlisted[!duplicated(df_authorid_authorname_unlisted$id),]
dim(df_authorid_authorname_unlisted)
## [1] 727 2
df_edgelist_twomode$names <- df_authorid_authorname_unlisted$names[match(df_edgelist_twomode$V2, df_authorid_authorname_unlisted$id)]
#####Add pub_count as an attribute at the edge level
pub_count <- as.data.frame(table(unlist(author_id_split)))
#df_authorid_authorname_unlisted$pub_count <- pub_count$Freq[match(df_authorid_authorname_unlisted$id, pub_count$Var1)]
a <- df_edgelist_twomode
g<-graph.data.frame(a[,c("V2","V1")])
g
## IGRAPH a9e738e DN-- 906 737 --
## + attr: name (v/c)
## + edges from a9e738e (vertex names):
## [1] 57223640712->2-s2.0-85143794140 57753915200->2-s2.0-85160812282
## [3] 58288848400->2-s2.0-85160269286 58258344100->2-s2.0-85159570136
## [5] 58061860400->2-s2.0-85158902849 57301216200->2-s2.0-85147960691
## [7] 50262958000->2-s2.0-85148333106 57221813377->2-s2.0-85142524779
## [9] 57769896800->2-s2.0-85132609552 41561548400->2-s2.0-85159058001
## [11] 57222072140->2-s2.0-85156121354 58205160300->2-s2.0-85154056174
## [13] 57191156064->2-s2.0-85152072289 58158727000->2-s2.0-85150968581
## [15] 57209902552->2-s2.0-85150939650 57211158344->2-s2.0-85150790502
## + ... omitted several edges
#Need the two-mode edgelist from Deliverable 3 (EID+names+IDs) --> g2
#The following code adds the two-mode structure to the graph "g"
V(g)$type <- V(g)$name %in% a[,c("V2")]
table(V(g)$type)[2]
## TRUE
## 727
i<-table(V(g)$type)[2]
#Gets centrality measures
cent<-data.frame(bet=betweenness(g, normalized=T, directed = FALSE)/max(betweenness(g, normalized=T, directed = FALSE)),eig=evcent(g)$vector, degree=degree(g, mode="total"))
cent$name<-rownames(cent) #Ids in this case
head(cent);tail(cent)
## bet eig degree name
## 57223640712 0.000000000 0 1 57223640712
## 57753915200 0.000000000 0 1 57753915200
## 58288848400 0.000000000 0 1 58288848400
## 58258344100 0.000000000 0 1 58258344100
## 58061860400 0.004878049 0 2 58061860400
## 57301216200 0.000000000 0 1 57301216200
## bet eig degree name
## 2-s2.0-85063775408 0.003658537 0 3 2-s2.0-85063775408
## 2-s2.0-85047475402 0.001219512 0 2 2-s2.0-85047475402
## 2-s2.0-85050231849 0.000000000 0 1 2-s2.0-85050231849
## 2-s2.0-85021632458 0.000000000 0 1 2-s2.0-85021632458
## 2-s2.0-85058058613 0.001219512 0 2 2-s2.0-85058058613
## 2-s2.0-85049375044 0.000000000 0 1 2-s2.0-85049375044
###
cent$bet[1:i]<-cent$bet[1:i]/max(cent$bet[1:i])
cent$eig[1:i]<-cent$eig[1:i]/max(cent$eig[1:i])
summary(cent[1:i,])
## bet eig degree name
## Min. :0.000000 Min. :0.0000 Min. :1.000 Length:727
## 1st Qu.:0.000000 1st Qu.:0.0000 1st Qu.:1.000 Class :character
## Median :0.000000 Median :0.0000 Median :1.000 Mode :character
## Mean :0.001501 Mean :0.0564 Mean :1.014
## 3rd Qu.:0.000000 3rd Qu.:0.0000 3rd Qu.:1.000
## Max. :1.000000 Max. :1.0000 Max. :2.000
cent$bet[(i+1):nrow(cent)]<-cent$bet[(i+1):nrow(cent)]/max(cent$bet[(i+1):nrow(cent)])
cent$eig[(i+1):nrow(cent)]<-cent$eig[(i+1):nrow(cent)]/max(cent$eig[(i+1):nrow(cent)])
summary(cent[(i+1):nrow(cent),])
## bet eig degree name
## Min. :0.000000 Min. :0.000000 Min. : 1.000 Length:179
## 1st Qu.:0.001220 1st Qu.:0.000000 1st Qu.: 2.000 Class :character
## Median :0.003658 Median :0.000000 Median : 3.000 Mode :character
## Mean :0.019110 Mean :0.005587 Mean : 4.117
## 3rd Qu.:0.012195 3rd Qu.:0.000000 3rd Qu.: 5.000
## Max. :1.000000 Max. :1.000000 Max. :41.000
#Gets time invariant actor attributes
#actors<- source[!duplicated(source$Author.s..ID), c("Author.s..ID", "Year")]
#Transformation to one-mode
mat_g2_incidence <- get.incidence(g2)
mat_g2_incidence_to_1 <- mat_g2_incidence%*%t(mat_g2_incidence)
diag(mat_g2_incidence_to_1)<-0
g <- graph.adjacency(mat_g2_incidence_to_1, mode = "undirected")
g
## IGRAPH 3017fae UN-- 727 2540 --
## + attr: name (v/c)
## + edges from 3017fae (vertex names):
## [1] 57223640712--14627618600 57223640712--56412981200 57753915200--57209094353
## [4] 58288848400--58289105500 58288848400--58289499100 58288848400--57570827800
## [7] 58288848400--57200371657 58288848400--6508378209 58288848400--58289499200
## [10] 58288848400--58289499300 58288848400--58288979300 58288848400--57201813646
## [13] 58061860400--58241995800 58061860400--57204101861 57301216200--57200151370
## [16] 50262958000--7005495251 50262958000--57220804469 57221813377--57063266900
## [19] 57221813377--57063266900 57221813377--7004581222 57221813377--7004581222
## [22] 57769896800--57054490100 57769896800--57774134300 57769896800--57760555100
## + ... omitted several edges
dim(mat_g2_incidence_to_1)
## [1] 727 727
g<-graph.adjacency(mat_g2_incidence_to_1, mode = "undirected", weighted = TRUE, diag=F)
g
## IGRAPH c6c5985 UNW- 727 2535 --
## + attr: name (v/c), weight (e/n)
## + edges from c6c5985 (vertex names):
## [1] 57223640712--14627618600 57223640712--56412981200 57753915200--57209094353
## [4] 58288848400--58289105500 58288848400--58289499100 58288848400--57570827800
## [7] 58288848400--57200371657 58288848400--6508378209 58288848400--58289499200
## [10] 58288848400--58289499300 58288848400--58288979300 58288848400--57201813646
## [13] 58061860400--58241995800 58061860400--57204101861 57301216200--57200151370
## [16] 50262958000--7005495251 50262958000--57220804469 57221813377--57063266900
## [19] 57221813377--7004581222 57769896800--57054490100 57769896800--57774134300
## [22] 57769896800--57760555100 57769896800--57760448100 57769896800--37120645800
## + ... omitted several edges
#Interactive Visualization
V(g)$label<-V(g)$name
V(g)$name<-1:length(V(g))
g
## IGRAPH c6c5985 UNW- 727 2535 --
## + attr: name (v/n), label (v/c), weight (e/n)
## + edges from c6c5985 (vertex names):
## [1] 1--176 1--329 2--177 3--178 3--330 3--443 3--527 3--580 3--618
## [10] 3--641 3--652 3--662 5--179 5--195 6--180 7--181 7--331 8--182
## [19] 8--332 9--183 9--333 9--444 9--528 9--581 9--619 11--184 11--334
## [28] 12--185 13--186 13--335 14--187 14--336 14--445 15--188 15--337 15--446
## [37] 16--189 16--338 16--447 16--529 16--582 17--190 17--339 17--448 18--191
## [46] 18--340 18--449 18--530 18--583 18--620 18--642 18--653 18--663 18--671
## [55] 18--678 18--683 18--689 18--693 18--696 18--698 18--700 18--702 18--704
## [64] 18--706 19-- 37 19--192 21--193 22--194 23--196 23--341 23--450 23--531
## + ... omitted several edges
#checkpoint
############
#In HTML visualizations (like NetworkD3) we need two datasets, one for links, another for nodes attributes.
############
#Gets edgelist from graph, also any other attribute at the edge level to be included in the mapping
links<-as.data.frame(cbind(get.edgelist(g), E(g)$weight))
#Needs to be numeric
links$V1<-as.numeric(as.character(links$V1))
links$V2<-as.numeric(as.character(links$V2))
str(links)
## 'data.frame': 2535 obs. of 3 variables:
## $ V1: num 1 1 2 3 3 3 3 3 3 3 ...
## $ V2: num 176 329 177 178 330 443 527 580 618 641 ...
## $ V3: num 1 1 1 1 1 1 1 1 1 1 ...
links$V3<-round(as.numeric(as.character(links$V3)),3)
head(links)
## V1 V2 V3
## 1 1 176 1
## 2 1 329 1
## 3 2 177 1
## 4 3 178 1
## 5 3 330 1
## 6 3 443 1
colnames(links)<-c("source","target","value")
#Counts begin at zero in computer programming
links[,1:2]<-(links[,1:2]-1)
dim(links)
## [1] 2535 3
V(g)$pub_count<- pub_count$Freq[match(V(g)$label, pub_count$Var1)]
V(g)$author_name <- df_authorid_authorname_unlisted$names[match(df_authorid_authorname_unlisted$id, V(g)$label)]
V(g)$size<- cent$bet[match(V(g)$label, cent$name)]
V(g)$eig <- cent$eig[match(V(g)$label, cent$name)]
V(g)$deg <- cent$degree[match(V(g)$label, cent$name)]
summary(V(g)$size)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.000000 0.000000 0.000000 0.001501 0.000000 1.000000
nodes <- data.frame(name= c(paste("ID: ", V(g)$label[1:i],
", Pub Count: ", V(g)$pub_count,
", Name: ", V(g)$author_name,
sep="")),
size=abs(V(g)$size),
EV_cent=abs(V(g)$eig),
deg_cent=abs(V(g)$deg)
)
nodes$name<-as.character(nodes$name)
head(nodes);tail(nodes)
## name size EV_cent
## 1 ID: 57223640712, Pub Count: 1, Name: aattouri i 0.00000000 0
## 2 ID: 57753915200, Pub Count: 1, Name: almuhana h 0.00000000 0
## 3 ID: 58288848400, Pub Count: 1, Name: leidinger m.l 0.00000000 0
## 4 ID: 58258344100, Pub Count: 1, Name: almelhes s.a. 0.00000000 0
## 5 ID: 58061860400, Pub Count: 2, Name: moulieswaran n 0.02797203 0
## 6 ID: 57301216200, Pub Count: 1, Name: chaudhury s 0.00000000 0
## deg_cent
## 1 1
## 2 1
## 3 1
## 4 1
## 5 2
## 6 1
## name size EV_cent deg_cent
## 722 ID: 57871605700, Pub Count: 1, Name: triefenbach f 0 1 1
## 723 ID: 57787054200, Pub Count: 1, Name: wei p 0 1 1
## 724 ID: 57226017336, Pub Count: 1, Name: yu h 0 1 1
## 725 ID: 56393534400, Pub Count: 1, Name: zheng s 0 1 1
## 726 ID: 15060858000, Pub Count: 1, Name: tur g 0 1 1
## 727 ID: 35607445600, Pub Count: 1, Name: natarajan p. 0 1 1
nodes$group<-NA
nodes$pub_count <- as.numeric(V(g)$pub_count)
nodes$group <- cut(nodes$pub_count, c(1, 2, max(nodes$pub_count) + 1), right = TRUE, labels = FALSE)
table(is.na(nodes$group))
##
## FALSE TRUE
## 10 717
table(nodes$group)
##
## 1
## 10
head(nodes[is.na(nodes$group),],20)
## name size EV_cent
## 1 ID: 57223640712, Pub Count: 1, Name: aattouri i 0 0
## 2 ID: 57753915200, Pub Count: 1, Name: almuhana h 0 0
## 3 ID: 58288848400, Pub Count: 1, Name: leidinger m.l 0 0
## 4 ID: 58258344100, Pub Count: 1, Name: almelhes s.a. 0 0
## 6 ID: 57301216200, Pub Count: 1, Name: chaudhury s 0 0
## 7 ID: 50262958000, Pub Count: 1, Name: sankaran g.c 0 0
## 9 ID: 57769896800, Pub Count: 1, Name: liu x 0 0
## 10 ID: 41561548400, Pub Count: 1, Name: hockly n. 0 0
## 11 ID: 57222072140, Pub Count: 1, Name: rad h.s 0 0
## 12 ID: 58205160300, Pub Count: 1, Name: hilao-valencia p.m 0 0
## 13 ID: 57191156064, Pub Count: 1, Name: ouared a 0 0
## 14 ID: 58158727000, Pub Count: 1, Name: sargazi moghadam t 0 0
## 15 ID: 57209902552, Pub Count: 1, Name: singh a 0 0
## 16 ID: 57211158344, Pub Count: 1, Name: havinga i 0 0
## 17 ID: 57220870257, Pub Count: 1, Name: barbosa b 0 0
## 18 ID: 58127773400, Pub Count: 1, Name: yang l.w.y 0 0
## 20 ID: 7006806419, Pub Count: 1, Name: kralik j.d. 0 0
## 21 ID: 57225105038, Pub Count: 1, Name: mahdi a.y 0 0
## 22 ID: 14039593800, Pub Count: 1, Name: mouti s 0 0
## 23 ID: 57204435819, Pub Count: 1, Name: olenyi t 0 0
## deg_cent group pub_count
## 1 1 NA 1
## 2 1 NA 1
## 3 1 NA 1
## 4 1 NA 1
## 6 1 NA 1
## 7 1 NA 1
## 9 1 NA 1
## 10 1 NA 1
## 11 1 NA 1
## 12 1 NA 1
## 13 1 NA 1
## 14 1 NA 1
## 15 1 NA 1
## 16 1 NA 1
## 17 1 NA 1
## 18 1 NA 1
## 20 1 NA 1
## 21 1 NA 1
## 22 1 NA 1
## 23 1 NA 1
nodes$group<-ifelse(is.na(nodes$group), "Published 1 article", ifelse(nodes$group==1, "Published 2 articles", "Published 1 article"))
counts<-data.frame(table(nodes$group))
counts
## Var1 Freq
## 1 Published 1 article 717
## 2 Published 2 articles 10
counts$labels <- paste(counts$Var1, ", N= ", counts$Freq, sep="")
nodes$groups <- counts$labels[match(nodes$group, counts$Var1)]
head(nodes)
## name size EV_cent
## 1 ID: 57223640712, Pub Count: 1, Name: aattouri i 0.00000000 0
## 2 ID: 57753915200, Pub Count: 1, Name: almuhana h 0.00000000 0
## 3 ID: 58288848400, Pub Count: 1, Name: leidinger m.l 0.00000000 0
## 4 ID: 58258344100, Pub Count: 1, Name: almelhes s.a. 0.00000000 0
## 5 ID: 58061860400, Pub Count: 2, Name: moulieswaran n 0.02797203 0
## 6 ID: 57301216200, Pub Count: 1, Name: chaudhury s 0.00000000 0
## deg_cent group pub_count groups
## 1 1 Published 1 article 1 Published 1 article, N= 717
## 2 1 Published 1 article 1 Published 1 article, N= 717
## 3 1 Published 1 article 1 Published 1 article, N= 717
## 4 1 Published 1 article 1 Published 1 article, N= 717
## 5 2 Published 2 articles 2 Published 2 articles, N= 10
## 6 1 Published 1 article 1 Published 1 article, N= 717
colnames(nodes)
## [1] "name" "size" "EV_cent" "deg_cent" "group" "pub_count"
## [7] "groups"
colnames(links)
## [1] "source" "target" "value"
library(networkD3)
library(magrittr)
library(htmlwidgets)
##
## Attaching package: 'htmlwidgets'
## The following object is masked from 'package:networkD3':
##
## JS
library(htmltools)
netviz<-forceNetwork(Links = links, Nodes = nodes,
Source = 'source', Target = 'target',
NodeID = 'name',
Group = "groups",
charge = -30, # node repulsion
linkDistance = JS("function(d) { return d.linkDistance; }"),
linkWidth = JS("function(d) { return Math.sqrt(d.value)*2; }"),
opacity = 0.8,
Nodesize = 'size',
radiusCalculation = JS("Math.sqrt(d.nodesize*30)+4"),
zoom = T,
fontSize=14,
bounded= F,
legend= TRUE,
linkColour = ifelse(links$value == 1, "#CCFFFF", ifelse(links$value == 2, "#e3eaa7", "#abb2b9 ")),
colourScale = JS("d3.scaleOrdinal(d3.schemeCategory10)"))
HTMLaddons <-
"function(el, x) {
d3.select('body').style('background-color', ' #212f3d ')
d3.selectAll('.legend text').style('fill', 'white')
d3.selectAll('.link').append('svg:title')
#change only the title
.text(function(d) { return 'Grade course: ' + d.value + ', Campus: ' + d.campus ; })
var options = x.options;
var svg = d3.select(el).select('svg')
var node = svg.selectAll('.node');
var link = svg.selectAll('link');
var mouseout = d3.selectAll('.node').on('mouseout');
function nodeSize(d) {
if (options.nodesize) {
return eval(options.radiusCalculation);
} else {
return 6;
}
}
d3.selectAll('.node').on('click', onclick)
function onclick(d) {
if (d3.select(this).on('mouseout') == mouseout) {
d3.select(this).on('mouseout', mouseout_clicked);
} else {
d3.select(this).on('mouseout', mouseout);
}
}
function mouseout_clicked(d) {
node.style('opacity', +options.opacity);
link.style('opacity', +options.opacity);
d3.select(this).select('circle').transition()
.duration(750)
.attr('r', function(d){return nodeSize(d);});
d3.select(this).select('text').transition()
.duration(1250)
.attr('x', 0)
.style('font', options.fontSize + 'px ');
}
}
"
netviz$x$links$linkDistance <- (1/links$value)*1500
netviz$x$links$campus <- links$campus
onRender(netviz, HTMLaddons)