I am trying to generate a bipartite graph where I color specific links based on certain nodes (i.e., plant species names). So far, I have been able to color the nodes of interest, but not their corresponding links.
Below, I provide a minimal reproducible example where I show some species in yellow, and I would like all the links stemming from those species to be colored as well (for instance, also in yellow). Up to now, I have only been able to color the links based on their weights, but not according to any other kind of attribute.
I am finding it quite difficult to access this type of information — possibly because the way I’m manipulating the ggraph
object isn’t appropriate for this purpose.
Below is a minimal reproducible example:
library(dplyr)
library(ggraph)
# Set seed for reproducibility
set.seed(123)
# Number of plant and pollinator species
n_plants <- 10
n_pollinators <- 10
# Create a random binary matrix (1 = interaction, 0 = no interaction)
net <- matrix(sample(0:1, n_plants * n_pollinators, replace = TRUE, prob = c(0.7, 0.3)),
nrow = n_plants, ncol = n_pollinators)
# Name rows and columns
rownames(net) <- paste0("Plant_", 1:n_plants)
colnames(net) <- paste0("Pollinator_", 1:n_pollinators)
#Network 1
# Calculate row and column degrees
row_degrees <- rowSums(net > 0)
col_degrees <- colSums(net > 0)
# Order rows and columns
ordered_rows <- order(row_degrees, decreasing = TRUE)
ordered_columns <- order(col_degrees, decreasing = TRUE)
# Reorder matrix based on degrees
ordered_net <- net[ordered_rows, ordered_columns]
rownames(ordered_net) <- rownames(net)[ordered_rows]
colnames(ordered_net) <- colnames(net)[ordered_columns]
# Create weights tibble
weights <- tibble(
weight = c(col_degrees[ordered_columns], row_degrees[ordered_rows]),
name = c(colnames(ordered_net), rownames(ordered_net)),
trophic = c(rep("2", length(col_degrees[ordered_columns])), rep("1", length(row_degrees[ordered_rows]))) # Trophic levels
)
# Create layout for the bipartite graph
layout <- create_layout(ordered_net, "bipartite") %>%
mutate(position = ifelse(y > 0, "upper", "lower"))
layout <- left_join(layout, weights, by = "name")
# Organize data for better visualization
layout_false = layout %>%
filter(type=="FALSE")
layout_true = layout %>%
filter(type=="TRUE")
x_ordered_false = arrange(layout_false,x)
x_ordered_true = arrange(layout_true, x)
n_polls=layout_false %>% nrow(.)
n_plants=layout_true %>% nrow(.)
max_counts = max(c(n_polls, n_plants))
result_sequence_polls <- seq(from = 1, to = max_counts, length.out = n_polls)
result_sequence_plants <- seq(from = 1, to = max_counts, length.out = n_plants)
layout_false$x = result_sequence_polls
layout_true$x = result_sequence_plants
layout = bind_rows(layout_false, layout_true)
#Create species vector to color code those
spp_vector = c("Plant_9", "Plant_7", "Plant_8")
#Create new col with colors
layout <- layout %>%
mutate(node_color = case_when(
name %in% spp_vector ~ "yellow",
TRUE ~ "black"))
# Plot the ggraph
p1 = ggraph(layout) +
geom_edge_link2(aes(edge_width = weight, color= from)) +
geom_node_point(aes(shape = trophic, size = weight, color= node_color)) + # Use 'weight' for size
geom_node_text(aes(label = name,
hjust = ifelse(position == "upper", 0, 1)),
size = 1.5, angle = 90, vjust = 0.4, colour = "black") +
scale_colour_identity() +
scale_shape_manual(values = c(19, 19)) +
scale_size_continuous(range = c(0.5, 4)) +
scale_edge_width(range = c(0.5, 2.5)) +
theme(
panel.grid = element_blank(),
panel.background = element_blank(),
panel.border = element_blank(),
plot.background = element_blank(),
legend.position = "none",
plot.margin = margin(t = 80, r = 10, b = 80, l = 10),
plot.title = element_text(vjust=25)
) +
coord_cartesian(expand = FALSE, clip = "off")
p1
The original data passed to ggraph
gets transformed by geom_edge_link
, i.e. by default get_edges()
is called to extract the edges data from the graph object or the data frame in your case. As a result the columns get renamed but the information is still available, e.g. the info on the from
node are stored in columns with prefix node1.
and with prefix node2.
for the to
node.
Hence, you can use these column names to map on aesthetics. Also note the use of I()
or AsIs
(instead of scale_edge_colour_identity
).
library(ggraph)
head(get_edges()(layout))
#> from to weight direction circular x y xend yend node1.x node1.y node1.type
#> 1 1 12 1 right FALSE 1 1 2 0 1 1 FALSE
#> 2 1 15 1 left FALSE 1 1 5 0 1 1 FALSE
#> 3 1 16 1 right FALSE 1 1 6 0 1 1 FALSE
#> 4 1 18 1 left FALSE 1 1 8 0 1 1 FALSE
#> 5 2 11 1 right FALSE 2 1 1 0 2 1 FALSE
#> 6 2 12 1 right FALSE 2 1 2 0 2 1 FALSE
#> node1.name node1..ggraph.orig_index node1.circular node1..ggraph.index
#> 1 Plant_1 1 FALSE 1
#> 2 Plant_1 1 FALSE 1
#> 3 Plant_1 1 FALSE 1
#> 4 Plant_1 1 FALSE 1
#> 5 Plant_4 2 FALSE 2
#> 6 Plant_4 2 FALSE 2
#> node1.position node1.weight node1.trophic node1.node_color node2.x node2.y
#> 1 upper 4 1 black 2 0
#> 2 upper 4 1 black 5 0
#> 3 upper 4 1 black 6 0
#> 4 upper 4 1 black 8 0
#> 5 upper 4 1 black 1 0
#> 6 upper 4 1 black 2 0
#> node2.type node2.name node2..ggraph.orig_index node2.circular
#> 1 TRUE Pollinator_4 12 FALSE
#> 2 TRUE Pollinator_2 15 FALSE
#> 3 TRUE Pollinator_3 16 FALSE
#> 4 TRUE Pollinator_8 18 FALSE
#> 5 TRUE Pollinator_1 11 FALSE
#> 6 TRUE Pollinator_4 12 FALSE
#> node2..ggraph.index node2.position node2.weight node2.trophic
#> 1 12 lower 4 2
#> 2 15 lower 3 2
#> 3 16 lower 3 2
#> 4 18 lower 2 2
#> 5 11 lower 4 2
#> 6 12 lower 4 2
#> node2.node_color edge.id
#> 1 black 1
#> 2 black 2
#> 3 black 3
#> 4 black 4
#> 5 black 5
#> 6 black 6
p1 <- ggraph(layout) +
geom_edge_link(aes(edge_width = weight, color = I(node1.node_color))) +
geom_node_point(aes(shape = trophic, size = weight, color = node_color)) + # Use 'weight' for size
geom_node_text(
aes(
label = name,
hjust = ifelse(position == "upper", 0, 1)
),
size = 1.5, angle = 90, vjust = 0.4, colour = "black"
) +
scale_colour_identity() +
scale_shape_manual(values = c(19, 19)) +
scale_size_continuous(range = c(0.5, 4)) +
scale_edge_width(range = c(0.5, 2.5)) +
theme(
panel.grid = element_blank(),
panel.background = element_blank(),
panel.border = element_blank(),
plot.background = element_blank(),
legend.position = "none",
plot.margin = margin(t = 80, r = 10, b = 80, l = 10),
plot.title = element_text(vjust = 25)
) +
coord_cartesian(expand = FALSE, clip = "off")
p1