I have below ggplot
library(ggplot2)
library(quantmod)
dat = data.frame(val = 1:20, qtr = as.yearqtr(seq(as.Date('2000-01-01'), length.out = 20, by = '3 months')))
ggplot(data = dat) +
geom_line(aes(x = qtr, y = val)) +
geom_vline(xintercept = as.yearqtr(as.Date('2003-04-01')))
I want to add one extra tick in x-axis where my vertical line crosses the x-axis. The corresponding tick label will also be manual i.e. 'Sep : 2003-04-01'. Is there any function available in ggplot to achieve this?
Thanks for your help.
You could define your bespoke date breaks as a separate vector.
And the labels as a matching text vector including the bespoke text for the x intercept tick.
Adjustments to the axis text for legibility.
library(ggplot2)
library(quantmod)
intercept_date <- as.yearqtr(as.Date('2003-04-01'))
x_breaks <- c(as.yearqtr(seq(as.Date('2000-01-01'), length.out = 5, by = '12 months')), intercept_date)
x_labels <- c(as.character(as.yearqtr(seq(as.Date('2000-01-01'), length.out = 5, by = '12 months'))), "Sep : 2003-04-01")
ggplot(data = dat) +
geom_line(aes(x = qtr, y = val)) +
geom_vline(xintercept = intercept_date)+
scale_x_yearqtr(breaks = x_breaks,
labels = x_labels)+
theme(axis.text.x = element_text(angle = 30, hjust = 1))
Created on 2022-10-16 with reprex v2.0.2
Option 2
In response to comment: intercept breaks and labels defined within scale_x_yearqtr:
ggplot(data = dat) +
geom_line(aes(x = qtr, y = val)) +
geom_vline(xintercept = as.yearqtr(as.Date('2003-04-01')))+
scale_x_yearqtr(breaks = c(as.yearqtr(seq(as.Date('2000-01-01'), length.out = 5, by = '12 months')), as.yearqtr(as.Date('2003-04-01'))),
labels = c(as.character(as.yearqtr(seq(as.Date('2000-01-01'), length.out = 5, by = '12 months'))), "Sep : 2003-04-01"))+
theme(axis.text.x = element_text(angle = 30, hjust = 1))
Related
Suppose we want to plot this data:
library(ggplot2)
library(sf)
library(raster)
library(colorRamps)
min_lon <- 10
max_lon <- 17
min_lat <- 8
max_lat <- 17
grid_size <- 0.5
lon_grids <- 1 + ((max_lon - min_lon)/grid_size)
lat_grids <- 1 + ((max_lat - min_lat)/grid_size)
points <- data.frame(lon = rep(seq(min_lon, max_lon, grid_size), lat_grids), lat = rep(seq(min_lat, max_lat, grid_size), each = lon_grids))
points$Var <- runif(min= 10, max = 48, 285)
points$value <-cut(points$Var, breaks= seq(10.08, 47.80, length.out = 13), dig.lab = 1)
ggplot() +
coord_sf(xlim = c(min_lon, max_lon), ylim = c(min_lat, max_lat)) +
theme_bw()+
geom_raster(data = points, aes(x = lon, y = lat, fill = value), interpolate = FALSE) +
labs(x="Longitude", y="Latitude")+
scale_fill_manual(values = matlab.like(n = 13), name = "[m]",
labels = sprintf("%.2f", seq(10.08, 47.80, length.out = 13)),
guide = guide_legend(reverse = TRUE))+theme(legend.position = "bottom")
This code produces the following graph:
Two problems I am facing here:
To make it discrete, I used the cut function. I chose the breaks= seq(10.08, 47.80, length.out = 13) arbitrary based on the minimum and maximum values with a random length of 13. Is there any criteria to decide the correct range?
Is there any way to make the legend look like this?
One option would be to use e.g. scale_fill_stepsn with guide_binswhich does not require to manually discretize the variable mapped on fill. Additionally I use a custom function to set the breaks of the legend instead of the default mechanism to set the number of breaks.
set.seed(123)
library(ggplot2)
library(colorRamps)
base <- ggplot() +
coord_sf(xlim = c(min_lon, max_lon), ylim = c(min_lat, max_lat)) +
theme_bw() +
geom_raster(data = points, aes(x = lon, y = lat), interpolate = FALSE) +
labs(x = "Longitude", y = "Latitude") +
theme(legend.position = "bottom")
base +
aes(fill = Var) +
scale_fill_stepsn(colors = matlab.like(n = 13), name = "[m]",
breaks = function(x) seq(x[[1]], x[[2]], length.out = 13),
labels = ~ sprintf("%.0f", .x),
guide = guide_bins(axis = FALSE,
show.limits = TRUE))
I am trying annotate individual plots of a facet plot. I have set the order to 2008, 1999 using factor levels.
But when I add the geom_text to the ggplot, the order of the plots change. See examples below. What am I doing wrong? How can I solve this?
library(tidyverse)
df <- mpg %>% mutate(year = factor(year, levels = c(2008,1999)))
anno <- data.frame(xstar = c(5, 2), ystar = c(100, 70),
lab = c("text1","text2"),
year = c("2008","1999"))
df %>% ggplot(aes(class, displ)) +
geom_col(aes(fill=drv)) +
facet_grid(~year) +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
geom_text(data = anno, aes(x = xstar, y = ystar, label = lab))
Convert the year column in your annotation dataframe also to a factor with the same levels and order as in your main df:
library(ggplot2)
df <- mpg
df$year = factor(df$year, levels = c(2008, 1999))
anno <- data.frame(
xstar = c(5, 2), ystar = c(100, 70),
lab = c("text1", "text2"),
year = factor(c("2008", "1999"), levels = c(2008, 1999))
)
ggplot(df, aes(class, displ)) +
geom_col(aes(fill = drv)) +
geom_text(data = anno, aes(x = xstar, y = ystar, label = lab)) +
facet_grid(~year) +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))
I'm trying to do a nice graph with ggplot but I'm still faces a barrier.
When I use facet_grid at the end of my code, somethings wrong happen. A helping hand would be great!
This is my code :
# Package
library(ggplot2)
# Function
firstup <- function(x) {
x <- tolower(x)
substr(x, 1, 1) <- toupper(substr(x, 1, 1))
x
}
Create data
data_F = data.frame(DATE = seq(as.Date('2020-01-21'), as.Date('2020-03-06'), by = 'days'),
NB = sample(1:20, 46, replace=TRUE))
Manage the data
data_F = data.frame(DATE = data_F$DATE,
year = as.numeric(format(data_F$DATE, format = "%Y")),
month = as.factor(format(data_F$DATE, format = "%B")),
days = as.numeric(format(data_F$DATE, format = "%d")),
NB = data_F$NB)
Relevel month with the right order
data_F$month = as.factor(firstup(data_F$month))
data_F$month = factor(data_F$month,unique(data_F$month))
month = factor(data_F$month,unique(data_F$month))
month = unique(month)
month = as.factor(month)
The main plot
plot1 = ggplot(data_F,aes(x=DATE,y=NB)) +
geom_bar(stat = "identity", colour="black", fill = "dodgerblue3", width=0.5) +
scale_x_date(breaks = data_F$DATE, labels = data_F$days, minor_breaks = NULL,
expand = expansion(add = 0.3))+
scale_y_continuous(limits = c(0, 65), breaks = seq(0, 65, by = 5), minor_breaks = seq(0, 65, by = 1))
plot1
Creating the segment data
data.segm = data.frame(x=data_F$DATE,y=Inf, xend = data_F$DATE, yend=-Inf,
month=data_F$month)
Show two days, for example at the row 6 and 35
i = 6
plot1 = plot1 + geom_segment(data = data.segm, aes_string(x=data.segm$x[[i]],y=data.segm$y[[i]],
xend=data.segm$xend[[i]],yend=data.segm$yend[[i]]),
colour = alpha("gray90",0.5),size=8,inherit.aes = F)
i = 35
plot1 = plot1 + geom_segment(data = data.segm, aes_string(x=data.segm$x[[i]],y=data.segm$y[[i]],
xend=data.segm$xend[[i]],yend=data.segm$yend[[i]]),
colour = alpha("gray90",0.5),size=8,inherit.aes = F)
plot1
And know my problem with facet_grid
plot2 = plot1 + facet_grid(.~month, space="free_x", scales="free_x", switch="x")
plot2
Jonas
I'm trying to figure it out why this function for x axis breaks works perfectly for 2 to 6 days breaks, but gives me an error when I change to 7 days (Error: breaks and labels must have the same length). Thank you
Data Frame
library(tidyverse)
df <- data.frame(date = seq(as.Date("2019-01-01"), as.Date("2019-12-31"), by = "day"))
df$counts <-sample(seq(from = 20, to = 50, by = 5), size = 365, replace = TRUE)
df<- df %>%
mutate(date = as.Date(date),
counts = as.numeric(counts))
Code
breaks_daily = seq(from = min(df$date), to = max(df$date), by = "1 day")
# Then make the 7 days interval labels
labels_7_days = format(seq(from = min(df$date), to = max(df$date), by = "7 days"), "%b-%d")
labels_final = c(sapply(labels_7_days, function(x) {
c(x, rep("", 6))
}))
#
if ((length(breaks_daily) %% 7) == 0) {
labels_final <- labels_final
} else {
labels_final<- labels_final[-length(labels_final)]
}
myplot <- ggplot(df,
aes(y = counts, x = date)) +
geom_bar(stat = "identity", position = "dodge", fill = "#99CCFF", width=1) +
labs(x="Date", y="Quantity of Fruits") +
scale_x_date(labels = labels_final, breaks = breaks_daily, expand=c(0,0)) +
scale_y_continuous(limits = c(0, 70),
breaks = seq(0, 70, 10),
expand = c(0, 0)) +
ggtitle(paste0("Figure 2: Fruits Example" )) +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, size = 35),
axis.text.y = element_text(size = 35),
axis.text = element_text(size = 35),
axis.title = element_text(size = 40, face="bold"),
axis.title.y = element_text(vjust = -2),
axis.title.x = element_text(vjust = -1),
axis.ticks.length = unit(.5, "cm"))
myplot
Not a direct answer to your question, but why don't you just use the inbuilt functionality? Sometimes it is not necessary to reinvent the wheel... ?
library(tidyverse)
df <- data.frame(date = seq(as.Date("2019-01-01"), as.Date("2019-12-31"), by = "day"))
df$counts <-sample(seq(from = 20, to = 50, by = 5), size = 365, replace = TRUE)
ggplot(df, aes(y = counts, x = date)) +
geom_col() +
scale_x_date(date_breaks = "1 week",date_labels = "%b %d")+
theme(axis.text.x = element_text(angle = 90, vjust = 0.5))
update
Here how to keep the ticks in-between (although I don't think your visualisation gains a lot with it)
You need to bring the two vectors to the same length. When using your label creator, you are creating six empty spaces for each week until the maximum (and including it!), then of course making "too many labels". Just subset the vector by using only the length of your breaks.
P.S. geom_col is identical to geom_bar(stat = "identity") , and in your example you don't need position = position_dodge, as you have no group defined. This argument only makes sense when you are dodging by a group.
library(tidyverse)
df <- data.frame(date = seq(as.Date("2019-01-01"), as.Date("2019-12-31"), by = "day"))
df$counts <-sample(seq(from = 20, to = 50, by = 5), size = 365, replace = TRUE)
breaks_daily <- seq(from = min(df$date), to = max(df$date), by = "1 day")
labels_7_days <- format(seq(from = min(df$date), to = max(df$date), by = "7 days"), "%b-%d")
labels_final <- c(sapply(labels_7_days, function(x) {
c(x, rep("", 6))
})) [1:length(breaks_daily)] #that is the crucial bit
ggplot(df, aes(y = counts, x = date)) +
geom_col(fill = "#99CCFF", width=1) +
labs(x="Date", y="Quantity of Fruits") +
scale_x_date(labels = labels_final, breaks = breaks_daily, expand=c(0,0)) +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5))
Created on 2020-05-30 by the reprex package (v0.3.0)
I am using ggplot to make a bar chart. I have already used the scales package to change the scientific "2e5" formatting to the full number with commas to separate. I have been unable to change the axis tick labels so that a value of 1,000,000 appears as 1M, and 3,500,000 as 3.5M, etc. How do I do this?
See below for code:
# Generate dummy dataframe
df <- structure(list(month = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
), foo = c(2322636.14889234, 8676432.48522654, 207993.984222412,
3310791.19816422, 7540729.19022292, 7316447.75252789, 2410026.6979076,
6202864.60500211, 8700672.56037146, 1334956.53280988, 505991.168320179,
3106733.97500068)), row.names = c(NA, -12L), class = "data.frame")
# create plot
plot.1 <- ggplot2::ggplot(data = df, aes(x = month, y = foo)) +
geom_bar(stat = 'identity', fill = 'darkorchid4', width = 0.5) +
theme_minimal() +
labs(title = "Monthly foo measurements", x = "Month",
y = "Amount of foo" ) +
scale_y_continuous(labels = scales::comma)
Thanks in advance!
ggplot2::ggplot(data = df, aes(x = month, y = foo)) +
geom_bar(stat = 'identity', fill = 'darkorchid4', width = 0.5) +
theme_minimal() +
labs(title = "Monthly foo measurements", x = "Month",
y = "Amount of foo" ) +
scale_y_continuous(label = scales::unit_format(unit = "M", scale = 1e-6, sep = ""))