Retaining trailing zeros from string plotted with `geom_text()` [duplicate] - r

This question already has an answer here:
Stop parsing out zeros after decimals in ggplot2's annotate
(1 answer)
Closed 2 years ago.
I have seen similar questions and solutions but none as far as I can see relatable to geom_text() in particular. Any guidance is greatly appreciated.
Say I want a plot point estimates and confidence intervals of:
# create tbl
ni <- tribble(
~ method, ~ mean_difference, ~ lo95, ~ hi95,
"NC", 3.235762, -0.5063099, 6.977835,
"IPTW", 3.256231, -0.5063099, 6.977835,
"EM", 5.642857, -1.995181, 13.280896,
)
Next I create a string var pasting together [rounded] mean_difference, lo95, and hi95 — which will be specified as the label for geom_text
# convert to point estimate and confidence intervals to strings (to keep trailing zeros for plot)
to_string <- function(
var,
n_digits = 1,
n_small = 1){
as.character(format(round(var, digits = n_digits), nsmall = n_small))
}
ni <- ni %>%
mutate(
mean_difference_lab = to_string(mean_difference),
lo95_lab = to_string(lo95),
hi95_lab = to_string(hi95),
lab = paste(
mean_difference_lab,
" (",
lo95_lab,
"-",
hi95_lab,
")",
sep = "")
)
This parses correctly in console.
print(ni$lab)
And yet, the trailing zeros are removed from the string when I plot it as:
ni %>%
ggplot(aes(x = mean_difference, y = method)) +
geom_point(
size = 6,
shape = 18) +
geom_errorbarh(aes(
xmin = lo95,
xmax = hi95,
height = 0
)) +
geom_text(aes(
family = 'Courier',
label = lab),
parse = TRUE,
nudge_y = -0.2) +
scale_x_continuous(breaks = seq(- 6, 14, 2))
Can any help spare my blushes, please?

if I understand your description you're getting:
but you want:
The only thing I changed was the argument parse=TRUE to parse=FALSE, i. e.
ni %>%
ggplot(aes(x = mean_difference, y = method)) +
geom_point(
size = 6,
shape = 18) +
geom_errorbarh(aes(
xmin = lo95,
xmax = hi95,
height = 0
)) +
geom_text(aes(
family = 'Courier',
label = lab),
parse = FALSE, # changed
nudge_y = -0.2) +
scale_x_continuous(breaks = seq(- 6, 14, 2))
(note that there are some awkward spaces when using parse=FALSE - these however are already in the data, i. e. what's shown in the plot is the same as what you get when looking at ni$lab)
Does this answer your question?

Related

Plotting range for same variable across two conditions

I have an input matrix consists of 5 columns and 12 rows.
I am trying to plot a range for same variables (lets say width) across two methods/conditions (Paper, estimated). I am able to plot range across one methods/condition using code:
Input <- read.table("File.txt", header = T, sep = "\t")
ggplot(Input, aes(x=Trait))+
geom_linerange(aes(ymin=min,ymax=max),linetype=3,color="Black")+
geom_point(aes(y=min),size=3,color="darkgreen")+
geom_point(aes(y=max),size=3,color="darkgreen")+ labs(y="-log10(P)", x="Traits") +
theme_bw()
But I want to plot each variable across methods together in the same plot. I can do this by adding an extra suffix with each variable Is there a nicer way to do this? I have tried shape=Method but it's not working for me, Any help will be highly appreciated.
I would suggest mapping Method on color instead of shape. But hey. It's your plot. (; To achieve your desired result without adding a suffix you could make use of position_dodge like so:
library(tibble)
library(ggplot2)
ggplot(Input, aes(x = Trait, shape = Method)) +
geom_linerange(aes(ymin = min, ymax = max, group = Method), linetype = 3, color = "Black", position = position_dodge(.6)) +
geom_point(aes(y = min), color = "darkgreen", size = 3, position = position_dodge(.6)) +
geom_point(aes(y = max), color = "darkgreen", size = 3, position = position_dodge(.6)) +
labs(y = "-log10(P)", x = "Traits") +
theme_bw()
DATA
set.seed(42)
Input <- tibble(
Method = rep(c("Paper", "Estimated"), each = 3),
Trait = rep(c("Width", "Density", "Lenght"), 2),
Count = rep(c(2, 4, 10), 2),
min = runif(6, 5, 7),
max = min + runif(6, 0, 10)
)

How do I modify labels produced by scales package?

So I'm making pyramid visualizations. I'm using scale_y_continuous(labels = scales::label_number_si(accuracy = 0.1)) to produce the labels. However, I want to get rid of the negative sign on the female section of the graph.
I think the best way to keep the SI suffixes, but remove the negative sign is to modify the labels output by label_number_si, but labels = abs(label_number_si()) gives the following error: Error in abs: non-numeric argument to mathematical function
Any insight is appreciated.
EDIT: Use demo_continuous(c(-1e10,1e10), label = label_number_si()) labels should look as they do below EXCEPT that negative numbers should not have a "-" in front
I bet there's a simpler way to do this but I haven't figured it out yet.
Here's an example that replicates your question's result using the normal scales::label_number_si:
ggplot(data = data.frame(x = 1000*c(-5:-1, 1:5),
type = rep(1:2, each = 5))) +
geom_col(aes(x,abs(x),fill = type), orientation = "y") +
scale_x_continuous(labels = scales::label_number_si()) +
facet_wrap(~type, scales = "free_x")
We could make a custom version of scales::label_number_si which makes them absolute values in the last step. To make this, I used command-click (Mac OS X) on the function name to see the underlying function's code, and then just pasted that into a new function definition with minor modifications.
label_number_si_abs <- function (accuracy = 1, unit = NULL, sep = NULL, ...)
{
sep <- if (is.null(unit))
""
else " "
function(x) {
breaks <- c(0, 10^c(K = 3, M = 6, B = 9, T = 12))
n_suffix <- cut(abs(x), breaks = c(unname(breaks), Inf),
labels = c(names(breaks)), right = FALSE)
n_suffix[is.na(n_suffix)] <- ""
suffix <- paste0(sep, n_suffix, unit)
scale <- 1/breaks[n_suffix]
scale[which(scale %in% c(Inf, NA))] <- 1
scales::number(abs(x), accuracy = accuracy, scale = unname(scale),
suffix = suffix, ...)
}
}
We could replace with the custom function to get abs value labels:
ggplot(data = data.frame(x = 1000*c(-5:-1, 1:5),
type = rep(1:2, each = 5))) +
geom_col(aes(x,abs(x),fill = type), orientation = "y") +
scale_x_continuous(labels = label_number_si_abs()) +
facet_wrap(~type, scales = "free_x")

Two-line annotations using plotmath expressions in ggplot

I want to italicise a single character in the second line of a annotation in ggplot
Here's the plot
iris %>%
ggplot(aes(x = Sepal.Length,
y = Species,
fill = Species)) +
stat_summary(geom = "bar", fun = "mean") +
theme(legend.position = "none") -> p
Now I can annotate the plot like so and get a single character italicised while the rest is unitalicised.
p + annotate("text",
label = "italic(N)==74",
x = 3,
y = 2,
parse = T)
Now say I want a two-line annotation with certain characters pasted in. I can do it this way without using plotmath
p + annotate("text",
label = paste("AUC = ",
round(60.1876,3),
"\nN = ",
74,
sep = ""),
x = 3,
y = 2,
size = 4)
As you can see the N character is unitalicised. Is there any way to get it looking like the second graph but with the N italicised, either using plotmath or some other technique?
Use atop to draw text on top of text. See ?plotmath.
p +
annotate(
"text",
label = paste0("atop(AUC == ", round(60.1876, 3), ",italic(N) == 74)"),
x = 3,
y = 2,
size = 4,
parse = TRUE
)

R: ggplot2 let the characters of geom_text exactly cover one X unit

I want to highlight text based on the position in a string, for example if we have this text:
this is a really nice informative piece of text
Then I want to say let's draw a rectangle around positions 2 till 4:
t[his] is a really nice informative piece of text
I tried to do so in ggplot2 using the following code:
library(ggplot2)
library(dplyr)
box.data <- data.frame(
start = c(4,6,5,7,10,7),
type = c('BOX1.start', 'BOX1.start', 'BOX1.start','BOX1.end', 'BOX1.end', 'BOX1.end'),
text.id = c(1,2,3,1,2,3)
)
text.data <- data.frame(
x = rep(1,3),
text.id = c(1,2,3),
text = c('Thisissomerandomrandomrandomrandomtext1',
'Thisissomerandomrandomrandomrandomtext2',
'Thisissomerandomrandomrandomrandomtext3')
)
ggplot(data = text.data, aes(x = x, y = text.id)) +
scale_x_continuous(limits = c(1, nchar(as.character(text.data$text[1])))) +
geom_text(label = text.data$text, hjust = 0, size = 3) +
geom_line(data = box.data, aes(x = start, y = text.id, group = text.id, size = 3, alpha = 0.5, colour = 'red'))
This produces the following graph:
My method fails as a letter does not cover exactly one unit of the x-axis, is there any way to achieve this?
I just figured out that I can split the string in characters and plot these, perhaps it is useful for someone else.
library(ggplot2)
library(dplyr)
library(splitstackshape)
# First remember the plotting window, which equals the text length
text.size = nchar(as.character(text.data$text[1]))
# Split the string into single characters, and adjust the X-position to the string position
text.data <- cSplit(text.data, 'text', sep = '', direction = 'long', stripWhite = FALSE) %>%
group_by(text.id) %>%
mutate(x1 = seq(1,n()))
# Plot each character and add highlights
ggplot(data = text.data, aes(x = x1, y = text.id)) +
scale_x_continuous(limits = c(1, text.size)) +
geom_text(aes(x = text.data$x1, y = text.data$text.id, group = text.id, label = text)) +
geom_line(data = box.data, aes(x = start, y = text.id, group = text.id, size = 3, alpha = 0.5, colour = 'red'))
Which produces this plot:
Perhaps the marking should extend a little but upwards and downwards, but that's an easy fix.

Is it possible to generate a gganimate plot with random numbers? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I wish to generate a gganimate object that shows n random points in a specific range. The other limitation is that it should plot 2^n points, meaning 2, 4, 8, 16, 32, 64... points. I did this to calculate the decimals of pi but I wish to plot this animation so I can show how it improves the results given more random numbers in a nicer way.
This is what I have so far:
results <- c()
for(i in c(1:20)) {
r <- 1
limit <- 2^i
points <- data.frame(
x = runif(limit, -r, r),
y = runif(limit, -r, r))
points$d <- sqrt(points$x^2 + points$y^2)
points$type <- ifelse(points$d < r, "c", "s")
picalc <- 4 * length(points$type[points$type=="c"]) / limit
error <- pi - picalc
label <- paste0('Pi calc : ', round(picalc, 6), '\nError : ', round(error, 6))
iter <- data.frame(n = limit, picalc = picalc, error = error, label = label)
results <- rbind(results, iter)
}
# GGANIMATE
library(ggplot2)
library(gganimate)
p <- ggplot(results, aes(x = runif(n, -1, 1), y = runif(n, -1, 1))) +
geom_point(lwd = 2, alpha = 0.3) +
theme_minimal() +
geom_text(aes(x = 0, y = 0, label = label), size = 5) +
labs(caption = 'Number of random points : {frame_time}') +
transition_time(n)
animate(p, nframes = nrow(results), fps = 5)
Any suggestions?
Here's how I would "show how it improves the results given more random numbers in a nicer way."
library(ggplot2)
library(gganimate)
p <- ggplot(results, aes(x = n, y = error)) +
geom_point(lwd = 2, alpha = 0.3) +
theme_minimal() +
geom_text(aes(x = 0, y = 0, label = label), size = 5, hjust = 0) +
scale_x_log10(breaks = c(2^(1:4), 4^(2:10)), minor_breaks = NULL) +
labs(caption = 'Number of random points : {2^frame}') +
transition_manual(n) +
shadow_trail(exclude_layer = 2)
animate(p, nframes = nrow(results), fps = 5)
To show the kind of picture described in the question, you'd need to have the points labeled with the frame they belong to. (Also, as currently written, the points are randomly assigned afresh each iteration. It would be better to set all your points first, and then stick with those, in growing window sizes, to calculate the results.)
To do this quickly, I'm taking the last points frame (as it exists when i is at the end of the loop), and adding a number for what frame it should belong to. Then I can plot the points of each frame using transition_manual, and keep the past frames using shadow_trail.
Note, ggplot will be slower than I cared to wait if you run it with 1M points, so I did an abridged version up to 2^15 = 32k.
# Note, I only ran the orig loop for 2^(1:15), lest it get too slow
points2 <- points %>%
mutate(row = row_number(),
count = 2^ceiling(log2(row)))
point_plot <- ggplot(points2,
aes(x = x, y = y, color = type, group = count)) +
geom_point(alpha = 0.6, size = 0.1) +
theme_minimal() +
labs(caption = 'Number of random points : {2^(frame-1)}') +
transition_manual(count) +
shadow_trail()
animate(point_plot, nframes = 15, fps = 2)

Resources