Set condition for axis.text.x in general theme() in R - r

I am working on creating a unified theme for a large project including many different types of plots. I have set an overall theme where the angle for axis.text.x.bottom is 0 (horizontal), however some of my x-axis labels are quite long. I would like to set the angle of axis.text.x.bottom equal to 0 for labels of length less than 3 and equal to 90 (read bottom to top) for labels of length greater than 3, without having to specify the theme for each individual plot.
*** this is a highly simplified example
library(ggplot2)
report_theme(axis.text.x.bottom = element_text(angle = 0))
testdata1 = matrix(NA, nrow = 10, ncol = 2)
testdata1[,1] = 1:10
testdata1[,2] = 1:10
testdata1 = as.data.frame(testdata1)
g1 = ggplot(testdata1, aes(x = testdata1[,1], y = testdata1[,2])) + geom_point()
g1 + report_theme
testdata2 = testdata1
testdata2[,1] = c("number 1", "number 2", "number 3", "number 4", "number 5", "number 6", "number 7", "number 8", "number 9", "number 10")
g2 = ggplot(testdata, aes(x = testdata2[,1], y = testdata2[,2])) + geom_point()
g2 + report_theme
The plot for testdata1 looks fine since the x-axis labels are numbers, however the x-axis labels for testdata2 are long strings, and hence can't easily be read horizontally because they overlap.
I know how to change the angle for each plot individually, but it requires knowing the name of the data frame being used to plot, and I would like to be able to do it within the overall theme report_theme. How can I do this when a different data frame is being used for each plot?

Related

How to create annotation with annotate and ggarange R

I need to write some text at my shared labels.
I have 4 subplots in my plot and they share the same label axis, so I would like to put some annotation at the Y label (and X label too, but the x label I manage excluding the x labels from all plots except from the last one).
I've tried the annotate function but nothing seems to change.
a <- ggarrange(pet, a63, a64, a65,
labels = c(" Polietileno Tereftalato (PET)", "Amostra 63",
"Amostra 64", "Amostra 65"),
ncol = 1, nrow = 4, label.x=0.18, label.y= 1, align ="v",
vjust=2, hjust=0.2,
legend = "bottom")
a + annotate("text", x = 0, y = 25, label ="Absorbância")
a

Lollipop chart with repeated elements in different groups

I am trying to plot a lollipop chart with 5 groups and repeated elements in those groups. If all elements have different names it works as expected:
Intended behavior:
The problem is that I want to plot only 5 algorithms in different groups, and when I actually name them from Algorithm 1-5 this happens with the plot:
Unexpected behavior:
This is my snippet that produces the correct behavior of the lollipop chart (except for the wrong labels):
library(ggpubr)
# Create dataset
data <- data.frame(
algorithm=paste( "Algorithm ", seq(1,25), sep=""),
category=as.factor(c( rep('A', 5), rep('B', 5), rep('C', 5), rep('D', 5), rep('E', 5))),
metric=c(rep(rev(96:100), 5))
)
ggdotchart(data, x = "algorithm", y = "metric",
color = "category", # Color by groups
palette = c("#264653", "#2a9d8f", "#e9c46a", "#f4a261", "#e76f51"), # Custom color palette
sorting = "descending", # Sort value in descending order
add = "segments", # Add segments from y = 0 to dots
rotate = TRUE, # Rotate vertically
group = "category", # Order by groups
dot.size = 7, # Large dot size
label = round(data$metric), # Add mpg values as dot labels
font.label = list(color = "white", size = 8,
vjust = 0.5), # Adjust label parameters
ggtheme = theme_pubr() # ggplot2 theme
) +
labs(y = "Metric (%)", color="")
This is the new data snippet that causes this behavior:
# Create dataset
data <- data.frame(
algorithm=rep(paste( "Algorithm ", seq(1,5), sep=""), 5),
category=as.factor(c( rep('A', 5), rep('B', 5), rep('C', 5), rep('D', 5), rep('E', 5))),
metric=c(rep(rev(96:100), 5))
)
How can I possibly solve this issue?
Once produced, we can edit this like any other ggplot object. We can use scale_x_discrete() to manipulate the axis labels, which avoids any confusion with the original plot definition and construction under the hood of ggdotchart(). Using your first plot as p, we can do:
alg_labels <- rep(paste( "Algorithm ", seq(1,5), sep=""), 5)
p +
scale_x_discrete(
labels = alg_labels
)

How to arrange `ggplot2` objects side-by-side and ensure equal plotting areas?

I am trying to arrange two ggplot2 plots side by side, i.e., in a two-column
layout using the package gridExtra. I am interested in ensuring that both
plots have equal plotting area (i.e., the gray plot panel is the same for both
plots) regardless of the height of the x-axis labels. As you can see in the
example below, when longer x-axis labels are used, gridExtra::grid.arrange()
seems to compensate this by adjusting the plotting area (i.e., the grayed out
part of the plot).
# Dummy data.
data <- data.frame(x = 1:10, y = rnorm(10))
# Dummy labels.
x_labels_short <- 1:10
x_labels_long <- 100001:100010
# Common settings for both `ggplot2` plots.
layers <- list(
labs(
x = "Plot title"
),
theme(
axis.text.x = element_text(
angle = 90,
vjust = 0.5,
hjust = 1
)
)
)
# `ggplot2 plot (a).
plot_a <- ggplot(data, aes(x, y)) +
scale_x_continuous(breaks = 1:10, labels = x_labels_short) +
layers
# `ggplo2` plot (b).
plot_b <- ggplot(data, aes(x, y)) +
scale_x_continuous(breaks = 1:10, labels = x_labels_long) +
layers
# Showing the plots side by side.
gridExtra::grid.arrange(
plot_a,
plot_b,
ncol = 2
)
Output:
What I want is for both plots to (1) have equal plotting area and (b) the x-axis
title of plot_a to be aligned with that of plot_b (i.e., the x-axis title of
plot_a to be offset based on the length of of the x-axis labels of plot_b).
If this is not clear, this is what I want to achieve would look like with base
R.
# Wrapper for convenience.
plot_gen <- function(data, labels) {
plot(
NULL,
xlim = c(1, 10),
ylim = c(min(data$y), max(data$y)),
xlab = "",
ylab = "y",
xaxt = "n"
)
axis(
side = 1,
at = 1:10,
labels = labels,
las = 2
)
title(
xlab = "Plot title",
line = 4.5
)
}
# Get `par`.
old_par = par(no.readonly = TRUE)
# Set the two-column layout.
layout(matrix(1:2, ncol = 2))
# Adjust margins.
par(mar = old_par$mar + c(1.5, 0, 0, 0))
# Plot base plot one.
plot_gen(data, x_labels_short)
# Plot base plot two.
plot_gen(data, x_labels_long)
# Restore `par`.
par(old_par)
# Restore layout.
layout(1:1)
Output:
Quick mention. I found a similar question on SO (i.e.,
How to specify the size of a graph in ggplot2 independent of axis labels), however I fail to see how the
answers address the problem. Also, the plots I am trying to arrange are based
on different data and I don't think I can use a facet_wrap approach.
One suggestion: the patchwork package.
library(patchwork)
plot_a + plot_b
It also works for more complex layouts, e.g.:
(plot_a | plot_b) / plot_a

Label specific points in a plot

Here I have two vectors: "Pr" and "Z"
here is my code:
Z=seq(1,10,by=0.5)
Pr=lapply(Z,functionZ)
plot(Z,Pr,main="CAT Bond Price with increasing attachment points",xlab="Attachment Point",ylab="Price")
grid(nx = NULL, ny = NULL, col = "blue", lty = "dotted")
I want to select only two points in the plot, namely those where Z=5, and Z=7.5 and label them as "Class A" and "Class B" respectively. How can I do that?
To stick with your base R plotting, you can use the text function.
Since you do not provide your FunctionZ I just use some example. I placed the labels to the left of the points. Depending on the shape of your function, you may wish to adjust the pos argument to place them elsewhere.
Z=seq(1,10,by=0.5)
functionZ = function(x) x + sin(x)
Pr=lapply(Z,functionZ)
plot(Z,Pr,main="CAT Bond Price with increasing attachment points",
xlab="Attachment Point",ylab="Price")
grid(nx = NULL, ny = NULL, col = "blue", lty = "dotted")
text(x=c(5,7.5), y=functionZ(c(5,7.5)), labels= c("Class A", "Class B"), pos=2)
Your example is not reproducible, so I made up some random data and plotted with ggplot:
Z=seq(1,10,by=0.5)
Pr <- 1:19
L <- c("Point1", rep(NA, 17), "Point19")
df <- data.frame(Pr, Z, L)
library(ggplot2)
ggplot(aes(x=Pr, y=Z, label=L), data=df) +
geom_point() +
geom_label() +
xlab("Attachment Point") +
ylab("Price") +
ggtitle("CAT Bond Price with increasing attachment points") +
theme_classic()
You just define which ones you want to show labels for in a separate labels column and ggplot does the rest.
In your case, when you define L just do
L <- ifelse(Z==7, "Class A", ifelse(Z==7.5, "Class B", NA))

Prevent long x-axis ticklabels from being cut off in bar charts with plotly in R

I am trying to use plotly to plot a bar chart with long strings as x-axis labels. However, these strings are cut off by plotly like this shown here:
Going through the list of attributes of plotly axis, I've tried setting such as tickangle (which doesn't make sense, I realize now) and several others, but all are of no use.
You can adjust the margins in a plotly layout in the layout function.
Reproducible example since one was not provided:
d <- data.frame(traitMean = apply(iris[-5], 2, mean))
# long labels
labs <- c("Long name for this", "Long name for that",
"Long names everywhere", "Petal Width")
If you plot this with the default margins, the labels will be cutoff:
# example where ticklabels are cutoff
plot_ly(y = d[["traitMean"]], x = labs, type = "bar") %>%
layout(xaxis = list(tickangle = 45))
You can adjust the bottom margin from the default in the margin argument of layout. margin takes a named list where b is the name for the "bottom" margin. 160 px works in this example, but you may need to find a value that works for your labels.
plot_ly(y = d[["traitMean"]], x = labs, type = "bar") %>%
layout(margin = list(b = 160), xaxis = list(tickangle = 45))
Text can get cut off on bars when the textposition="outside". To avoid this, along with setting the margins to fix y-axis label truncation, set the cliponaxis = FALSE to fix the value label truncation.
Here is an example of the value label truncation in spite of adding top and bottom margins to remove y-axis label truncation:
library(plotly)
plot_ly(
x = c("1. Group 1", "2. Txn","3. AOV","4. Account/Recv CV","5. Cost %","6. Lost %","7. Take Rate","8. Group 2"),
y = c(3.8,0,0,0,0,0,0,3.8),
name = "SF Zoo",
type = "waterfall",
measure = c("relative", "relative", "relative", "relative", "relative", "relative", "relative","total"),
text = c(3.8,0,0,0,0,0,0,3.8), textposition = 'outside'
) %>%
layout(margin = list(b = 20,t=20))
Resulting Graph has the value 3.8 cut off.
When you add cliponaxis = FALSE the cut off is removed
plot_ly(
x = c("1. Group 1",
"2. Txn",
"3. AOV",
"4. Account/Recv CV",
"5. Cost %",
"6. Lost %",
"7. Take Rate",
"8. Group 2"),
y = c(3.8,0,0,0,0,0,0,3.8),
name = "SF Zoo",
type = "waterfall",
measure = c("relative", "relative", "relative", "relative", "relative", "relative", "relative","total"),
text = c(3.8,0,0,0,0,0,0,3.8), textposition = 'outside', cliponaxis = FALSE
) %>%
layout(margin = list(b = 20,t=20))
Hope this helps

Resources