This document visualizes a signed network with both positive and negative relationships between actors involved in the Syrian Conflict. The visualization includes community detection, centrality metrics, and interactive features.
# Load network data
network_data <- read.csv("~/SignedNetwork.csv")
# Create igraph object
g <- graph_from_data_frame(network_data, directed = FALSE)
# Add edge colors based on the sign
E(g)$color <- ifelse(network_data$Sign == "Positive", "green", "red")
# Assign weights for layout
E(g)$layout_weight <- ifelse(network_data$Sign == "Positive", 1, 0.5)
# Community detection
communities <- cluster_louvain(g)
# Calculate network metrics
betweenness_scores <- betweenness(g, normalized = TRUE)
closeness_scores <- closeness(g, normalized = TRUE)
eigenvector_scores <- eigen_centrality(g)$vector
degree_scores <- degree(g, normalized = TRUE)
# Create summary statistics
network_summary <- data.frame(
Metric = c("Nodes", "Edges", "Density", "Communities",
"Positive Edges", "Negative Edges"),
Value = c(
vcount(g),
ecount(g),
graph.density(g),
length(unique(membership(communities))),
sum(E(g)$color == "green"),
sum(E(g)$color == "red")
)
)
# Display summary
knitr::kable(network_summary, caption = "Network Summary Statistics")
Metric | Value |
---|---|
Nodes | 194.0000000 |
Edges | 439.0000000 |
Density | 0.0234496 |
Communities | 9.0000000 |
Positive Edges | 214.0000000 |
Negative Edges | 225.0000000 |
# Prepare nodes dataframe
nodes <- unique(c(network_data$Actor.1, network_data$Actor.2))
nodes_df <- data.frame(
id = nodes,
label = nodes,
stringsAsFactors = FALSE
)
# Create clean edge dataframe
edges_df <- network_data %>%
mutate(
from = Actor.1,
to = Actor.2,
# Create a consistent edge ID for undirected edges
edge_id = apply(cbind(Actor.1, Actor.2), 1, function(x) paste(sort(x), collapse="--"))
) %>%
distinct(edge_id, .keep_all = TRUE) %>%
mutate(
# Edge styling
color = case_when(
Sign == "Positive" ~ "#00FF00", # Bright green
Sign == "Negative" ~ "#FF0000", # Bright red
TRUE ~ "#CCCCCC" # Default gray
),
# Edge width based on type
width = case_when(
Sign == "Positive" ~ 2,
Sign == "Negative" ~ 1,
TRUE ~ 1
),
# Add edge labels
label = Sign,
# Add tooltips
title = paste("From:", Actor.1, "<br>To:", Actor.2, "<br>Type:", Sign)
) %>%
select(from, to, color, width, label, title)
# Define a color palette for communities
community_colors <- rainbow(length(unique(membership(communities))))
# Enhance nodes with metrics and styling
nodes_df <- nodes_df %>%
mutate(
# Add community group
group = as.numeric(membership(communities)),
# Calculate node sizes based on degree
degree = degree(g),
size = 10 + (degree/max(degree)) * 40,
# Add node tooltips
title = paste0(
"Node: ", label, "<br>",
"Degree: ", degree, "<br>",
"Community: ", group
),
# Assign colors based on community
color = community_colors[group]
)
# Create the visualization
visNetwork(nodes_df, edges_df, width = "100%", height = "600px") %>%
visIgraphLayout(layout = "layout_with_fr") %>%
visOptions(
highlightNearest = list(enabled = TRUE, degree = 1),
selectedBy = list(variable = "group", multiple = TRUE),
manipulation = FALSE
) %>%
visLayout(randomSeed = 123) %>%
visPhysics(
solver = "forceAtlas2Based",
forceAtlas2Based = list(
gravitationalConstant = -100,
centralGravity = 0.01,
springLength = 150,
springConstant = 0.08,
damping = 0.4,
avoidOverlap = 0.5
),
stabilization = list(
enabled = TRUE,
iterations = 1000
)
) %>%
visInteraction(
hideEdgesOnDrag = TRUE,
hover = TRUE
) %>%
visLegend(
addNodes = data.frame(
label = paste("Community", sort(unique(nodes_df$group))),
shape = "dot",
color = community_colors[sort(unique(nodes_df$group))]
),
addEdges = data.frame(
label = c("Positive", "Negative"),
color = c("#00FF00", "#FF0000")
),
useGroups = FALSE,
position = "left",
width = 0.1,
zoom = FALSE
)
# Analyze communities
community_summary <- data.frame(
Community = 1:max(nodes_df$group),
Size = as.numeric(table(nodes_df$group))
) %>%
mutate(Percentage = round(Size / sum(Size) * 100, 2))
# Display community summary
knitr::kable(community_summary,
caption = "Community Size Distribution",
col.names = c("Community", "Number of Nodes", "% of Network"))
Community | Number of Nodes | % of Network |
---|---|---|
1 | 24 | 12.37 |
2 | 48 | 24.74 |
3 | 28 | 14.43 |
4 | 11 | 5.67 |
5 | 27 | 13.92 |
6 | 29 | 14.95 |
7 | 24 | 12.37 |
8 | 1 | 0.52 |
9 | 2 | 1.03 |
# Visualize community sizes
ggplot(community_summary, aes(x = factor(Community), y = Size)) +
geom_bar(stat = "identity", fill = "steelblue") +
theme_minimal() +
labs(
title = "Community Size Distribution",
x = "Community ID",
y = "Number of Nodes"
)
# First, add all centrality metrics to nodes_df if not already present
nodes_df <- nodes_df %>%
mutate(
betweenness = betweenness_scores[id],
closeness = closeness_scores[id],
eigenvector = eigenvector_scores[id],
value = degree_scores[id]
)
# Create centrality summary
centrality_summary <- nodes_df %>%
select(label, betweenness, closeness, eigenvector, value) %>%
rename(
Node = label,
Betweenness = betweenness,
Closeness = closeness,
Eigenvector = eigenvector,
Degree = value
) %>%
arrange(desc(Betweenness)) %>%
head(10)
# Display top 10 nodes by betweenness centrality
knitr::kable(centrality_summary,
caption = "Top 10 Nodes by Centrality Metrics",
digits = 3)
Node | Betweenness | Closeness | Eigenvector | Degree |
---|---|---|---|---|
Syrian Government | 0.464 | 0.571 | 1.000 | 0.482 |
Turkish Government | 0.207 | 0.529 | 0.920 | 0.228 |
Peshmerga | 0.176 | 0.428 | 0.354 | 0.155 |
HTS (Hayat Tahrir al-Sham) | 0.145 | 0.515 | 0.868 | 0.264 |
Hezbollah | 0.142 | 0.482 | 0.863 | 0.207 |
SDF (Syrian Democratic Forces) | 0.078 | 0.497 | 0.841 | 0.187 |
Russian Forces | 0.069 | 0.492 | 0.826 | 0.181 |
FSA (Free Syrian Army) | 0.057 | 0.487 | 0.663 | 0.124 |
USA | 0.053 | 0.442 | 0.650 | 0.150 |
Qatar | 0.040 | 0.415 | 0.149 | 0.041 |
# Calculate density within communities
community_density <- sapply(1:max(nodes_df$group), function(comm) {
nodes_in_comm <- nodes_df$id[nodes_df$group == comm]
subg <- induced_subgraph(g, nodes_in_comm)
graph.density(subg)
})
density_df <- data.frame(
Community = 1:length(community_density),
Density = community_density
)
# Visualize community densities
ggplot(density_df, aes(x = factor(Community), y = Density)) +
geom_bar(stat = "identity", fill = "darkgreen") +
theme_minimal() +
labs(
title = "Network Density by Community",
x = "Community ID",
y = "Density"
)