How to label edges in ggplot's geom_net library?
library(geomnet)
library(ggplot2)
x <- structure(list(from = c("a", "b", "d", "f", "g", "e", "c", "i",
"e", "h", "i", "i", "j", "j"), to = c("", "", "", "", "", "a",
"b", "c", "d", "e", "f", "g", "h", "i"), edge_val = c(NA, NA,
NA, NA, NA, 1.6, 2.25, 1.75, 0.95, 1.8, 3.2, 2.6, 2.95, 2.45)), .Names = c("from",
"to", "edge_val"), class = "data.frame", row.names = c(NA, -14L
))
ggplot(x, aes(from_id = from, to_id = to, linewidth = edge_val)) +
geom_net(layout.alg = "fruchtermanreingold", labelgeom = "text",repel = TRUE,
size = 4, labelon = TRUE, vjust = -1, ecolour = "grey80",
directed = FALSE, fontsize = 4, ealpha = 0.5) +
theme_net()
The idea would be to plot the edge value on the edges as:
I made some modifications to the code used for geom_net() (found here). It can be used like this:
# similar code as question, with linelabel = edge_val added to aes() & geom_net2
ggplot(x,
aes(from_id = from, to_id = to, linewidth = edge_val, linelabel = edge_val)) +
geom_net2(layout.alg = "fruchtermanreingold", labelgeom = "text", repel = TRUE,
size = 4, labelon = TRUE, vjust = -1, ecolour = "grey80",
directed = FALSE, fontsize = 4, ealpha = 0.5) +
theme_net()
To create geom_net2():
Step 1: Create a modified version of the draw_panel function used by geomnet::GeomNet, with line labels if aes(...) includes a mapping for linelabel.
old.draw_panel <- environment(GeomNet$draw_panel)$f
new.draw_panel <- old.draw_panel
# convert function body to a list, for easier code chunk insertions
body(new.draw_panel) <- as.list(body(new.draw_panel))
# geomnet code includes usage of %||%, which is an unexported function
# (it is identical to the exported version in rlang / purrr, so you can skip
# this step if you have one of those packages loaded)
body(new.draw_panel) <-
append(body(new.draw_panel),
substitute(
"%||%" <- function(a, b) {if (!is.null(a)) a else b}
), after = 1)
# remove the last chunk of code, which returns a grobTree for the geom layer
# (we'll add on a new grobTree later)
body(new.draw_panel) <-
body(new.draw_panel)[-length(body(new.draw_panel))]
# define label_line as NULL
body(new.draw_panel) <-
append(body(new.draw_panel),
substitute(
label_line <- NULL
))
# if aes(...) includes a mapping for linelabel, use it for label_line, positioned at the
# midpoint of each line
body(new.draw_panel) <-
append(body(new.draw_panel),
substitute(
if (!is.null(data$linelabel)){
label_line.df <- subset(data, to != "")
label_line.df$x <- (label_line.df$x + label_line.df$xend) / 2
label_line.df$y <- (label_line.df$y + label_line.df$yend) / 2
label_line.df$label <- label_line.df$linelabel
label_line <- ggplot2::GeomText$draw_panel(label_line.df,
panel_scales, coord)
}
))
# return a grobTree, with label_line added
body(new.draw_panel) <-
append(body(new.draw_panel),
substitute(
ggplot2:::ggname("geom_net2",
grid::grobTree(edges_draw, selfies_draw, selfies_arrows,
GeomPoint$draw_panel(vertices, panel_scales, coord),
label_grob, label_line))
))
body(new.draw_panel) <- as.call(body(new.draw_panel))
rm(old.draw_panel)
Step 2: Create GeomNet2 ggproto, which inherits from geomnet::GeomNet, but uses the modified draw_panel function.
GeomNet2 <- ggproto(`_class` = "GeomNet2",
`_inherit` = geomnet::GeomNet,
draw_panel = new.draw_panel)
Step 3: Create geom_net2 function, which is similar to geomnet::geom_net, except that it uses GeomNet2 as its geom.
geom_net2 <- function (
mapping = NULL, data = NULL, stat = "net", position = "identity", show.legend = NA,
na.rm = FALSE, inherit.aes = TRUE, layout.alg="kamadakawai", layout.par=list(),
directed = FALSE, fiteach=FALSE, selfloops = FALSE, singletons = TRUE, alpha = 0.25,
ecolour=NULL, ealpha=NULL, arrow=NULL, arrowgap=0.01, arrowsize=1, labelon=FALSE,
labelcolour=NULL, labelgeom = 'text', repel = FALSE,
vertices=NULL, ...) {
ggplot2::layer(
geom = GeomNet2, mapping = mapping, data = data, stat = stat,
position = position, show.legend = show.legend, inherit.aes = inherit.aes,
params = list(na.rm = na.rm, layout.alg=layout.alg, layout.par=layout.par,
fiteach=fiteach, labelon=labelon, labelgeom=labelgeom, ecolour = ecolour,
ealpha=ealpha, arrow=arrow, arrowgap=arrowgap, directed=directed, repel=repel,
arrowsize=arrowsize, singletons=singletons, labelcolour=labelcolour,
vertices=vertices, selfloops = selfloops,
...)
)
}
You can use the ggplot_build object to get the positions for the text labels. You need to set the seed so that the plot is not redrawn with a new layout.
library(geomnet)
library(ggplot2)
set.seed(1)
p <- ggplot(x, aes(from_id = from, to_id = to, linewidth = edge_val)) +
geom_net(layout.alg = "fruchtermanreingold", labelgeom = "text",repel = TRUE,
size = 4, labelon = TRUE, vjust = -1, ecolour = "grey80",
directed = FALSE, fontsize = 4, ealpha = 0.5) +
theme_net()
# grab plot data
g <- ggplot_build(p)
edgeData <- subset(g$data[[1]], !is.na(linewidth))
# draw labels
# x and y coords are mid between vertices
set.seed(1) # use the same seed
p + geom_text(data=edgeData,
aes(x=(xend+x)/2, y=(yend+y)/2, label=linewidth),
inherit.aes = FALSE)
If it's not compulsory geomnet package, we can take a look to ggraph package.
Here something to think about:
library(tidyverse)
library(tidytext)
library(tidygraph)
library(ggraph)
library(ggrepel)
# first we have to give to ggraph data as it likes:
edges <- x[-c(1:5),] # edges
colnames(edges) <- c('a','b','edge_val') # colnames
# second the nodes, taking all the nodes in the edges. You can also give them a weight.
nodes <- rbind(data.frame(node = edges$a, n = 1),data.frame(node = edges$b, n = 1)) %>% group_by(node) %>% summarise(n = sum(n))
Now you have to avoid a possible bug in the package, as stated here:
# here the fix
edges$a <- match(edges$a, nodes$node)
edges$b <- match(edges$b, nodes$node)
# you have to give to the graph data in this way
tidy <- tbl_graph(nodes = nodes, edges = edges, directed = T)
tidy <- tidy %>%
activate(nodes)
# lastly, the plot
set.seed(1)
ggraph(tidy, layout = "gem") +
geom_node_point(aes(size=1, color = 1)) +
geom_edge_link(alpha = 0.8,aes(label = edge_val)) +
scale_edge_width(range = c(0.2, 2)) +
geom_text_repel(aes(x = x, y=y , label=node), size = 6) +
# here some warnings about font...
theme_graph()
Related
I am working with the R programming language.
I have the following dataset:
library(treemap)
library(dplyr)
var1 <- c("A", "B", "C", "D", "E")
var1<- sample(var1, 1000, replace=TRUE, prob=c(0.2, 0.2, 0.2, 0.2, 0.2))
var1<- as.factor(var1)
var2 <- c("abc", "bcd", "egf", "hiu", "lkj", "oiu", "piu", "xsr", "zze", "tre")
var2<- sample(var2, 1000, replace=TRUE, prob=c(0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1))
var2<- as.factor(var2)
my_data = data.frame(var1, var2)
my_data = data.frame(my_data %>% group_by(var1, var2) %>% summarise(counts = n()))
I am now using this code to make a treemap for the entire dataset:
resize.win <- function(Width=6, Height=6)
{
# works for windows
dev.off(); # dev.new(width=6, height=6)
windows(record=TRUE, width=Width, height=Height)
}
resize.win(10,10)
treemap(my_data, index = c("var1", "var2"), vSize = "counts", type = "index", palette = "Set1", title = "my map")
Now, I would like to make an individual treemap for each unique factor of var1 using a LOOP, and then save each of them. I tried to do this with a loop:
levs = my_data$var1
factors = lapply(levs[sapply(levs, is.factor)], levels)
factors = factors[[1]]
my_list = list()
for (i in 1:length(factors))
{
index_i = factors[[i]]
title_i = paste0("Tree Map", index_i)
data_i = my_data[which(my_data$var1 == index_i),]
treemap_i = treemap(data_i, index = c("var1", "var2"), vSize = "counts", type = "index",
palette = "Set1", title = title_i)
my_list[[i]] = treemap_i
save_title_i = paste0("treemap_",i, ".png")
png(save_title_i )
}
However, I don't think I am doing this right since the outputs are being corrupted:
Can someone please show me how to fix this?
Thanks!
If you want to save the plot locally you need to remember two things:
use png()(bmp, jpeg, pdf etc.) before the plot to open the file
use dev.off() after the plot to close the file
*In addition, you can change the dimensions of the plot you want to save by:
png(save_title_i , width = 800, height = 800)
for (i in 1:length(factors))
{
index_i = factors[[i]]
title_i = paste0("Tree Map", index_i)
data_i = my_data[which(my_data$var1 == index_i),]
save_title_i = paste0("treemap_",i, ".png")
png(save_title_i ) # open the file
treemap_i = treemap(data_i, index = c("var1", "var2"), vSize = "counts", type = "index", palette = "Set1", title = title_i)
my_list[[i]] = treemap_i
dev.off() # close the file
}
I am trying to use the ComplexHeatmap R package to generate heatmaps and also print the values.
NES_final is a list of list containing expt at first level and scenario at second level. Within each expt I need to create a combined heatmap of all scenario.
The heatmap is created as expected, but while printing only the last value of scenario in each expt gets printed for all heatmaps within that expt.
Test data:
NES_final = list("expt1" = list("scenario1" = matrix(rnorm(36), nrow=3, ncol=3),
"scenario2" = matrix(rnorm(36), nrow=3, ncol=3),
"scenario3" = matrix(rnorm(36), nrow=3, ncol=3)),
"expt2" = list("scenario1" = matrix(rnorm(36), nrow=3, ncol=3),
"scenario2" = matrix(rnorm(36), nrow=3, ncol=3),
"scenario3" = matrix(rnorm(36), nrow=3, ncol=3)),
"expt3" = list("scenario1" = matrix(rnorm(36), nrow=3, ncol=3),
"scenario2" = matrix(rnorm(36), nrow=3, ncol=3),
"scenario3" = matrix(rnorm(36), nrow=3, ncol=3)))
Code:
for(expt in names(NES_final)){
NES <- NES_final[[expt]]
heatMap <- NULL
for(scenario in names(NES)){
heatMap <- heatMap + Heatmap(NES[[scenario]],
cluster_rows = FALSE,
cluster_columns = FALSE,
name = scenario,
column_title = scenario,
show_heatmap_legend = TRUE,
cell_fun = function(j, i, x, y, width, height, fill){
grid.text(scenario, x, y, gp = gpar(fontsize = 2))
}
)
}
svglite(paste0("/Analysis_1/Heatmap_", expt, ".svg"), width = length(NES) * 25, height = 6)
draw(heatMap, column_title = expt)
dev.off()
}
I was able to find a solution using the lapply function instead of the for loop for scenario.
heatMap <- lapply(names(NES), function(scenario){
Heatmap(NES[[scenario]],
cluster_rows = TRUE,
cluster_columns = FALSE,
name = scenario,
column_title = scenario,
show_heatmap_legend = FALSE,
cell_fun = function(j, i, x, y, width, height, fill){
grid.text(scenario, x, y, gp = gpar(fontsize = 5))
} ) }) }
heatMap <- Reduce("+", heatMap)
draw(heatMap, column_title = expt, padding = unit(c(0.5, 0.5, 0.5, 15), "cm"))
I am trying to update multiple data attributes in a figure generated via plotly.
This is my script based on this post:
library(plotly)
library(magrittr)
library(data.table)
X <- data.table(x = c(1, 2, 1, 2), y = c(1, 1, 2, 2), z = rep(c("a", "b"), each = 2))
gg <- ggplot() + geom_point(data = X, aes(x = x, y = y, color = z))
ggplotly(gg) %>%
layout(
updatemenus = list(
list(
buttons = list(
list(method = "restyle",
args = list(
list(x = list(X[z == "a", x])),
list(y = list(X[z == "a", y]))
),
label = "a"),
list(method = "restyle",
args = list(
list(x = list(X[z == "b", x])),
list(y = list(X[z == "b", y]))
),
label = "b")
)
)
)
)
However, as can be seen, the update menu does not work as intended. Not sure what the problem is.
There are too many lists in your above args parameter.
Furthermore, when calling restyle without specifying the trace number you are restyling all traces.
If you want to restyle a single trace you need to provide its trace index:
library(plotly)
library(magrittr)
library(data.table)
DT <- data.table(x = c(1, 2, 10, 20), y = c(1, 1, 20, 20), z = rep(c("a", "b"), each = 2))
gg <- ggplot() + geom_point(data = DT, aes(x = x, y = y, color = z))
ggplotly(gg) %>%
layout(
updatemenus = list(
list(
buttons = list(
list(method = "restyle",
args = list(list(x = DT[z == "a", x], y = DT[z == "a", y]), 0L),
label = "a"),
list(method = "restyle",
args = list(list(x = DT[z == "b", x], y = DT[z == "b", y]), 1L),
label = "b")
)
)
)
)
I'm not sure what you are trying to achive.
If you want to show trace "a" when "a" is selected and trace "b" when "b" is selected this filter approach is problematic, as it completly removes traces (reduces the dataset) from the plot and once the trace is removed you can no longer restyle it.
If you just want to toggle the trace visibility you should use the visible parameter instead of modifying the data:
DT <- data.table(x = c(1, 2, 10, 20), y = c(1, 1, 20, 20), z = rep(c("a", "b"), each = 2))
gg <- ggplot() + geom_point(data = DT, aes(x = x, y = y, color = z))
ggplotly(gg) %>% style(visible = FALSE, traces = 2) %>%
layout(
updatemenus = list(
list(
buttons = list(
list(method = "restyle",
args = list(list(visible = c(TRUE, FALSE)), c(0, 1)),
label = "a"),
list(method = "restyle",
args = list(list(visible = c(FALSE, TRUE)), c(0, 1)),
label = "b")
)
)
)
)
PS: Plotly.restyle expects the trace count starting from 0 (JS) and style() from 1 (R)
Using addTraces/deleteTraces as done here would be another approach. However, if the data stays the same I guess toggling the visibility is less computationally intensive.
I use the example from the site, but I want to remove the points so that only the connection of the medians remains.
My code.
# for reproducibility
set.seed(123)
library(ggstatsplot)
df_disgust <- dplyr::filter(bugs_long, condition %in% c("LDHF", "HDHF"))
p2 <-
ggstatsplot::ggwithinstats(
data = df_disgust,
x = condition,
y = desire,
xlab = "Condition",
ylab = "Desire to kill bugs",
type = "np",
conf.level = 0.99,
title = "Non-parametric Test",
package = "ggsci",
palette = "uniform_startrek",
point.args = list(size = 0, alpha = 0.5),
point.path = FALSE,
centrality.plotting = TRUE,
outlier.tagging = T,
ggtheme = ggthemes::theme_map()
)
ggstatsplot::combine_plots(
plotlist = list(p2),
plotgrid.args = list(nrow = 2),
annotation.args = list(
title = "Effect of disgust on desire to kill bugs ",
caption = "Source: Bugs dataset from `jmv` R package"
)
)
The parameter that is responsible for the points, as I understood point.args = list(size = 3, alpha = 0.5),but when I set size = 0 the dots don't disappear.
I didn't find any other parameters that would be responsible for the points
You can just set the transparency for respective geometric layers to 0.
library(ggstatsplot)
df_disgust <- dplyr::filter(bugs_long, condition %in% c("LDHF", "HDHF"))
ggwithinstats(df_disgust,
condition,
desire,
point.args = list(alpha = 0),
centrality.point.args = list(alpha = 0),
point.path = FALSE
)
grouped_ggwithinstats(df_disgust,
condition,
desire,
grouping.var = gender,
point.args = list(alpha = 0),
centrality.point.args = list(alpha = 0),
point.path = FALSE
)
Created on 2022-05-13 by the reprex package (v2.0.1.9000)
I am creating forest plots using the meta package in R. I have added a line of text using text.addline1 (see bottom of plot). How do I make that text bold? It says see "gpar" in the cran documentation but I can't work it out.
library(meta)
## make data
df1 <- data.frame(matrix(, nrow=14, ncol=6))
colnames(df1) <- c("estimate", "std.error", "N", "study", "Exposure", "or" )
df1$estimate <- c("1.0588634", "1.0210044", "1.0484577", "0.9872621", "1.0122652",
"0.9934573", "0.9429622", "1.0320382", "0.9654555", "1.0499671", "1.0400071",
"1.0208341", "0.9954885", "0.9879208")
df1$std.error <- c(0.054092871, 0.037674050, 0.093067276, 0.054198110, 0.008714173, 0.010266589,
0.056993648, 0.058017078, 0.058050610, 0.089878607, 0.056951120, 0.011041428,
0.013533755, 0.090526260)
df1$N <- 500
df1$study <- "Study"
df1$Exposure <- "Smoker"
df1$Exposure[7:14] <- "Non smoker"
df1$or <- c(0.057196058, 0.020786829, 0.047320225, -0.012819723, 0.012190549, -0.006564245,
-0.058729114, 0.031535708, -0.035155279, 0.048758876, 0.039227559, 0.020620087,
-0.004521733, -0.012152786)
meta_obj <- metagen(or, std.error, sm = "OR", data = df1,
studlab = study, comb.fixed = TRUE, comb.random = FALSE, byvar = Exposure)
# FOREST ------------------------------------------------------------------
forest(meta_obj, smlab = "Text here", spacing = 1.5,
xlab = "Odds ratio",
xlim = c(0.5, 2),
leftcols = c("studlab", "N"),
col.diamond = "red", col.by = "black",
overall.hetstat = FALSE,
print.I2 = TRUE, print.tau2 = FALSE,
subgroup = T, overall = F,
text.addline1 = "HOW DO I MAKE THIS BOLD?",
ref = 1)
See argument 'ff.addline':
forest(meta_obj, smlab = "Text here", spacing = 1.5,
xlab = "Odds ratio",
xlim = c(0.5, 2),
leftcols = c("studlab", "N"),
col.diamond = "red", col.by = "black",
overall.hetstat = FALSE,
print.I2 = TRUE, print.tau2 = FALSE,
subgroup = T, overall = F,
ref = 1,
text.addline1 = "USE ARGUMENT 'ff.addline = \"bold\"'",
ff.addline = "bold",
fs.addline = 12,
colgap.left = "2.8cm")