###### tags: `R` `ggplot2::geom_linerange` `ggplot2::ggplot` # Plot time series data ## Control multiple legends. Control top X axis breaks **data file shared on Google drive** [plot-data_Daily-number-containers-collected-refunded_holidays.csv](https://drive.google.com/file/d/1qKRicSeJ-fhDrUUJIRohRPivrqozIFZl/view?usp=sharing) [plot-data_number-to-place-on-top-of-bars.csv](https://drive.google.com/file/d/1b5OR8QpWTQ2NW352Ta_3R8orwgvImGZX/view?usp=sharing) [plot-title-data_totals.csv](https://drive.google.com/file/d/1lzLgRWjsnanGQFsc7cdA_Ex2abWtKMza/view?usp=sharing) [plot-title-data_running-totals.csv](https://drive.google.com/file/d/1VghRsXzO-1RfjpYxk5KHJLHMLa3RI2uQ/view?usp=sharing) **Plot** ![Daily-number-containers-collected-refunded_holidays_plot-has-problems](https://hackmd.io/_uploads/SyCM0lI2p.png) **R code to produce the plot above** * Problem 1: Black diamond appears in legend 1 items. How to remove them? * Problem 2: Top X axis has duplicated weeks. How to show every week rather than every 2 weeks? ```r! # Directory dir.drive.C <- "C:" dir.R <- file.path(dir.drive.C, "R") dir.R.packages <- file.path(dir.R, "R-4.3.2") dir.main <- file.path(dir.drive.C,"GoogleDrive") dir.containers <- file.path(dir.main,"containers-for-changes") dir.output <-file.path(dir.containers,"output") # install.packages("gsheet", lib = dir.R.packages, dependencies = TRUE) # install.packages("ggrepel", lib = dir.R.packages, dependencies = TRUE) # install.packages("tidyverse", lib = dir.R.packages, dependencies = TRUE) # install.packages("ggbreak", lib = dir.R.packages, dependencies = TRUE) # install.packages("forcats", lib = dir.R.packages, dependencies = TRUE) # install.packages("tsibble", lib = dir.R.packages, dependencies = TRUE) # install.packages("ggtext", lib = dir.R.packages, dependencies = TRUE) # install.packages("hutilscpp", lib = dir.R.packages, dependencies = TRUE) library(tzdb, lib.loc = dir.R.packages) library(vroom, lib.loc = dir.R.packages) library(gsheet, lib.loc = dir.R.packages) library(stringr, lib.loc = dir.R.packages) library(httr, lib.loc = dir.R.packages) library(curl, lib.loc = dir.R.packages) library(lubridate, lib.loc = dir.R.packages) library(ggridges, lib.loc = dir.R.packages) library(ggplot2, lib.loc = dir.R.packages) library(labeling, lib.loc = dir.R.packages) library(farver, lib.loc = dir.R.packages) library(cowplot, lib.loc = dir.R.packages) library(magick, lib.loc = dir.R.packages) library(tidyr, lib.loc = dir.R.packages) library(png, lib.loc = dir.R.packages) library(jpeg, lib.loc = dir.R.packages) library(RCurl, lib.loc = dir.R.packages) library(grid, lib.loc = dir.R.packages) library(ggrepel, lib.loc = dir.R.packages) library(readr, lib.loc = dir.R.packages) library(forcats, lib.loc = dir.R.packages) library(tidyverse, lib.loc = dir.R.packages) library(ggbreak, lib.loc = dir.R.packages) library(timeDate, lib.loc = dir.R.packages) library(tsibble, lib.loc = dir.R.packages) library(cowplot, lib.loc = dir.R.packages) library(ggthemes, lib.loc = dir.R.packages) library(markdown, lib.loc = dir.R.packages) library(xfun, lib.loc = dir.R.packages) library(ggtext, lib.loc = dir.R.packages) library(pals, lib.loc = dir.R.packages) library(hutilscpp, lib.loc = dir.R.packages) # Download shared CSV files from Google drive. Read them into R ## This is the plot data containers.holidays.collection.days <- read.csv( file = file.path(dir.output,"plot-data_Daily-number-containers-collected-refunded_holidays.csv") ,header = TRUE) |> # Put date columns to Date type dplyr::mutate( date.of.activity=as.Date(date.of.activity) ) # dim(containers.holidays.collection.days) 168 11 ## This is number data to place on top of stacked bars totals.all.types <- read.csv(file = file.path(dir.output,"plot-data_number-to-place-on-top-of-bars.csv") ,header = TRUE) |> # Put date columns to Date type dplyr::mutate( date.of.activity=as.Date(date.of.activity) ) # dim(totals.all.types) 56 4 ## This is plot title data totals <- read.csv(file = file.path(dir.output,"plot-title-data_totals.csv") ,header = TRUE) # dim(totals) 2 6 ## This is plot title data with running totals containers.2 <- read.csv(file=file.path(dir.output,"plot-title-data_running-totals.csv") ,header = TRUE) # dim(containers.2) 115 9 # Create plot title, subtitle plot.title.stacked.bars <- "Container collections and refunds in 2024\n" # Function to format numbers my_comma <- scales::label_comma(accuracy = 1, big.mark = ",", decimal.mark = ".") plot.subtitle.stacked.bars <- paste0(totals$number.activities[1], " collections made\n" ,my_comma(totals$total[1]), " containers collected\n" ,tail(containers.2$number.stock,n=1)," containers in stock\n" ,totals$number.activities[2], " refunds received\n" ,my_comma(totals$total[2]), " containers refunded") # bar width, position setttings width <- .75 position <- ggplot2::position_stack(vjust=.5) #---------- # Color #---------- # Use a palette with multiple distinct colors pals::pal.bands(kelly, glasbey, polychrome) # Only 22 colors are available with 'kelly'. # Only 32 colors are available with 'glasbey'. # Only 36 colors are available with 'polychrome'. # Pick first 3 color from kelly() pals::pal.bands(glasbey(n=length(unique(containers.holidays.collection.days$container.type)))) # Display color from color code ## #8 light blue for PET, #2 red for cans, #9 brown for glass colors <- as.vector(pals::glasbey(n=9))[c(8,2,9)] scales::show_col(colors) # Pair legend item and color legend.items.color <- c( PET=colors[1] ,cans=colors[2] ,glass=colors[3]) # Order legend item plot.legend.ordered <- c("PET","cans","glass") # length(plot.legend.ordered) 3 plot.legend.label.ordered <- c("PET bottles","Cans","Glass bottles") # length(plot.legend.ordered) 3 # Caption caption <- paste0("Data source: container-collection-refund-responses.gsheet\n" ,"Colour palette: pals::glasbey\n" ,"Script file: containers-collected-refund.R") # Make stacked bar plot with two legends ## Legend 1 for container type ## Legend 2 for day type stacked.bars.problems <- ggplot2::ggplot(data=containers.holidays.collection.days ,aes(x=date.of.activity ,y=container.number.adjusted # Stacks of the bar order, from top to bottom, controlled by the levels ,fill=factor(container.type, levels = c("PET","cans","glass")) ,label=container.number.adjusted))+ # Control transparency of bar color ggplot2::geom_col(alpha=0.6)+ # Draw sum number above the stacked bars using another dataset totals.all.types ggplot2::geom_text(data=totals.all.types ,aes(x=date.of.activity, y=total, label=total.label, fill=NULL) ,vjust=-0.2 ,size=2.5)+ # Months on bottom X axis, weeks on top X axis ggplot2::scale_x_date(expand = c(0, 0) # expand = c(0,0) to remove margins ,date_breaks = "1 month" ,date_labels = "%b-%Y" # %b Abbreviated month name in current locale (Aug) ,date_minor_breaks = "1 week" # Add a secondary x axis showing week number ,sec.axis = ggplot2::sec_axis( trans= ~ . ,labels = scales::date_format("W%w"))) + # Set breaks on Y axis ggplot2::scale_y_continuous(breaks = seq(0, 300, 50))+ ggplot2::labs(title=plot.title.stacked.bars ,subtitle = plot.subtitle.stacked.bars # Add data source as footnote ,caption = caption ,x = "" ,y = "Number of containers" # Change legend title from variable name to text ,fill="Container type " #,color="Day type" )+ # Remove default theme background color ggplot2::theme_bw()+ ggplot2::theme_minimal()+ # Apply The Economist theme ggthemes::theme_economist_white()+ ggplot2::theme(plot.title = element_text(hjust = -0.15, vjust=2.12, colour="black", size = 14, face="bold") ,legend.position = "top" ,legend.justification='left' ,legend.direction='horizontal' ,legend.text = element_text(size = 8.5) ,legend.box = "vertical" # Reduce gap between legends ,legend.spacing.y = unit(-0.25, "cm") ,plot.caption = element_text(hjust = 0) # Left align caption ,axis.title = element_text(size = 20) ,axis.text = element_text(size = 15) ,panel.grid.minor = element_blank() ,panel.grid.major = element_line(color = "gray", linewidth = 0.5) )+ ggplot2::geom_point(aes(x=date.of.activity ,y=y.coordinate.holidays.collection.days ,color=factor(day.type, levels = c("Public holiday", "Recycle collection")) ) ,shape=18 # 15 for solid squares, 18 solid diamonds ,size=5 ,show.legend = TRUE)+ # Modify legend for aesthetic=fill ggplot2::scale_fill_manual( # Change legend item color from default to colors # Use palette from pals package. as.vector() needed to remove color name values=legend.items.color ,labels=plot.legend.label.ordered) + # Modify legend for aesthetic= color ggplot2::scale_color_manual( name="Day type" ,values = c("Public holiday" = "black","Recycle collection" = "orange") )+ # Setting the order of legends ggplot2::guides(fill= guide_legend(order = 1) ,colour=guide_legend(order=2)) # Export plot to a png base <-300 png(file=file.path(dir.output,"Daily-number-containers-collected-refunded_holidays_plot-has-problems.png") ,width=base*8,height=base*6,res=250) stacked.bars.problems dev.off() ``` --- ## Stacked bar graph color not matched legend color. New unspecified color appears in bar graph and legend **data file shared on Google drive** https://drive.google.com/file/d/14vfxeGN5xjaBA4tCbORzzse26hj0cupZ/view?usp=drive_link **Color and legend** ```r! #---------- # Color #---------- # Use a palette with multiple distinct colors pals::pal.bands(kelly, glasbey, polychrome) # Only 22 colors are available with 'kelly'. # Only 32 colors are available with 'glasbey'. # Only 36 colors are available with 'polychrome'. # Pick first 8 color from glasbey() pals::pal.bands(glasbey(n=length(plot.legend.ordered))) # Display color from color code colors <- as.vector(pals::glasbey(n=8)) scales::show_col(colors) # Pair legend item and color x <- c( Badminton=colors[1] ,`Bike Fitting`=colors[2] ,Ride=colors[3] ,Run=colors[4] ,`Strength & Stability workout`=colors[5] ,Swim=colors[6] ,`Table Tennis`=colors[7] ,Walk=colors[8]) ``` ```r! scales::show_col(colors) ``` ![first-8-colors-glasbey](https://hackmd.io/_uploads/Hyqe4acBa.png) **problem** activity.type "Strength & Stability Workout" should be in pink as the legend color. But it is in gray bars ```r! # Read tsv into R activities.2023 <- read.table( file = file.path(dir.Strava.output,"Strava-activities-2023.tsv") ,sep = "\t" ,header = TRUE) |> # Change character Dates to date dplyr::mutate(start.date.local=as.Date(start.date.local))# dim(activities.2023) 341 3 #------------- # Legend #------------- # Order legend item alphabetically plot.legend.ordered <- sort(unique(activities.2023$activity.type)) # length(plot.legend.ordered) 8 ggplot2::ggplot(activities.2023 ,aes(x=start.date.local ,y=moving.time.hour ,fill = activity.type))+ ggplot2::geom_bar(position = "stack", stat = "identity") + ggplot2::scale_x_date( limits = as.Date(c('2023-01-01','2023-12-31')) ,expand = c(0, 0) # expand = c(0,0) to remove margins ,date_breaks = "1 month" ,date_minor_breaks = "1 week" ,date_labels = "%b" # Abbreviated month name in current locale (Aug) # Add a secondary x axis showing week number ,sec.axis = ggplot2::sec_axis( trans= ~ . ,breaks= c(seq.Date(as.Date("2023-01-01"), by="month", length.out = 12) ,"2023-12-25") ,labels= scales::date_format("%W")) )+ ggplot2::scale_y_continuous(limits = c(0,8) ,breaks = seq(from=0, to=8, by=1) ,expand = c(0, 0) # expand = c(0,0) to remove margins )+ ggplot2::labs(title="Daily hours spent on physical exercise activity in 2023" ,subtitle = "GPS-tracked activities: Ride, Run, Walk" ,x = "" ,y = "Moving time (h)" ,fill="Activity type")+ # Remove default theme background color ggplot2::theme_bw()+ ggplot2::theme_minimal()+ # Change bar colors ## Use ggplot2::scale_color_manual() to modify aesthetics = "colour" ## Use ggplot2::scale_fill_manual() to modify aesthetics = "fill" ggplot2::scale_fill_manual( # Use palette from pals package. as.vector() needed to remove color name values = colors ,labels=plot.legend.ordered #values=x # Reorder legend items from most frequent to least frequent # legend text longer than the cutoff is wrapped to multiple lines ,limits=stringr::str_wrap(plot.legend.ordered,width=20) ) ``` Code above generates this plot ![bar graph color not matched legend color](https://hackmd.io/_uploads/BkiMfacHT.png) ```r! tail(activities.2023,n=3) # start.date.local moving.time.hour activity.type #339 2023-11-15 2.182778 Ride #340 2023-11-15 1.505278 Strength & Stability Workout #341 2023-11-16 1.158056 Run ``` **problem** activity.type "Strength & Stability Workout" shown in grey in legend and graph, not as pink as the color palette ```r! # Pair legend item and color x <- c( Badminton=colors[1] ,`Bike Fitting`=colors[2] ,Ride=colors[3] ,Run=colors[4] ,`Strength & Stability workout`=colors[5] ,Swim=colors[6] ,`Table Tennis`=colors[7] ,Walk=colors[8]) ggplot2::ggplot(activities.2023 ,aes(x=start.date.local ,y=moving.time.hour ,fill = activity.type))+ ggplot2::geom_bar(position = "stack", stat = "identity") + ggplot2::scale_x_date( limits = as.Date(c('2023-01-01','2023-12-31')) ,expand = c(0, 0) # expand = c(0,0) to remove margins ,date_breaks = "1 month" ,date_minor_breaks = "1 week" ,date_labels = "%b" # Abbreviated month name in current locale (Aug) # Add a secondary x axis showing week number ,sec.axis = ggplot2::sec_axis( trans= ~ . ,breaks= c(seq.Date(as.Date("2023-01-01"), by="month", length.out = 12) ,"2023-12-25") ,labels= scales::date_format("%W")) )+ ggplot2::scale_y_continuous(limits = c(0,8) ,breaks = seq(from=0, to=8, by=1) ,expand = c(0, 0) # expand = c(0,0) to remove margins )+ ggplot2::labs(title="Daily hours spent on physical exercise activity in 2023" ,subtitle = "GPS-tracked activities: Ride, Run, Walk" ,x = "" ,y = "Moving time (h)" ,fill="Activity type")+ # Remove default theme background color ggplot2::theme_bw()+ ggplot2::theme_minimal()+ # Change bar colors ## Use ggplot2::scale_color_manual() to modify aesthetics = "colour" ## Use ggplot2::scale_fill_manual() to modify aesthetics = "fill" ggplot2::scale_fill_manual( values=x # Reorder legend items from most frequent to least frequent # legend text longer than the cutoff is wrapped to multiple lines ,limits=stringr::str_wrap(plot.legend.ordered,width=20) ) ``` ![new color gray not as specified as violet](https://hackmd.io/_uploads/SkTfXpcH6.png) --- ## Color background of a ggplot2 bar plot **Problems** * How to remove these white spaces/margins * Margin on the left of Jan 2023 * Margin above y=8 * Margin on the right of Jan 2024 * X axis ends at Dec 2023 * Margin below y=0 **plot generated** ![Activity-moving-time-hours-barplot-timezone-2023](https://hackmd.io/_uploads/H16vTnbBp.png) **data file shared on Google drive** https://drive.google.com/file/d/14vfxeGN5xjaBA4tCbORzzse26hj0cupZ/view?usp=drive_link **Code to produce the plot** ```r! # Read tsv into R activities.2023 <- read.table( file = file.path(dir.Strava.output,"Strava-activities-2023.tsv") ,sep = "\t" ,header = TRUE) |> # Change character Dates to date dplyr::mutate(start.date.local=as.Date(start.date.local))# dim(activities.2023) 341 3 # Number of colors needed for activity.type plot.legend.ordered <- sort(unique(activities.2023$activity.type)) length(plot.legend.ordered) # 8 # Use a palette with >=20 distinct colors pals::pal.bands(kelly, glasbey, polychrome) # Only 22 colors are available with 'kelly'. # Pick first n colors from kelly(), glasbey() pals::pal.bands(kelly(n=length(plot.legend.ordered))) # Manually create start dates and end dates to different time time.zones <- data.frame( date.start=as.Date(c("2023-01-01","2023-01-16","2023-02-06","2023-09-02","2023-10-03")) ,date.end=as.Date(c("2023-01-15","2023-02-05","2023-09-01","2023-10-02","2023-12-31")) ,time.zone=c("Australia/Brisbane","Asia/Kuala_Lumpur","Australia/Brisbane","Asia/Taipei","Australia/Brisbane")) # dim(time.zones) 5 3 # Pick color red for "Asia/Kuala_Lumpur", darkgreen for "Asia/Taipei", blue for "Australia/Brisbane". Change the color order to meet the legend text order sorted time.zones.color <- c("darkred","darkgreen","darkblue") moving.time.activity.2023 <- ggplot2::ggplot(activities.2023 ,aes(x=start.date.local ,y=moving.time.hour ,fill = activity.type))+ ggplot2::geom_bar(position = "stack", stat = "identity") + ggplot2::scale_x_date( limits = as.Date(c('2023-01-01','2023-12-31')) ,expand = c(0, 0) # expand = c(0,0) to remove margins ,date_breaks = "1 month" ,date_minor_breaks = "1 week" ,date_labels = "%b" # Abbreviated month name in current locale (Aug) # Add a secondary x axis showing week number ,sec.axis = dup_axis(name = "",labels = scales::date_format("%W")) )+ ggplot2::scale_y_continuous(limits = c(0,8) ,breaks = seq(from=0, to=8, by=1) ,expand = c(0, 0) # expand = c(0,0) to remove margins )+ ggplot2::labs(title="Daily hours spent on physical exercise activity in 2023" ,subtitle = "GPS-tracked activities: Ride, Run, Walk" ,x = "" ,y = "Moving time (h)" ,fill="Activity type")+ # Remove default theme background color ggplot2::theme_bw()+ ggplot2::theme_minimal()+ # Change bar colors ## Use ggplot2::scale_color_manual() to modify aesthetics = "colour" ## Use ggplot2::scale_fill_manual() to modify aesthetics = "fill" ggplot2::scale_fill_manual( # Use palette from pals package. as.vector() needed to remove color name values = as.vector(pals::kelly(n=8)) # glasbey, polychrome not good looking as kelly # Reorder legend items from most frequent to least frequent # legend text longer than the cutoff is wrapped to multiple lines ,limits=stringr::str_wrap(plot.legend.ordered,width=20) )+ # Create a second fill scale and modify its color ggnewscale::new_scale_fill()+ ggplot2::geom_rect(data=time.zones ,inherit.aes = FALSE ,aes( xmin=date.start ,xmax=date.end ,ymin=0 #ymin=-Inf ,ymax=8 #ymax=Inf ,fill=time.zone) ,alpha=0.15)+ # Change legend title. Modify color for the fill= aesthetic above ggplot2::scale_fill_manual(name="Time Zones",values = time.zones.color) # Export plot to png # Create a github-style calender heat-map using ggplot size.base <- 300 png(file=file.path(dir.Strava.output,"Activity-moving-time-hours-stacked-barplot-background-colored-timezone-2023.png") ,width=size.base*8,height=size.base*4.5,res=300) moving.time.activity.2023 dev.off() ``` --- ## Highlight a range of X axis in a line plot data file on Google drive https://drive.google.com/file/d/13B_AfldCOwWXzj_XqUv9249NQHvbsuw2/view?usp=drive_link ```r! B2GC2023 <- readr::read_csv(file = file.path(dir.Strava,"GOTOES_FIT-CSV_4284285070831056.csv")) |> dplyr::select(timestamp, position_lat, position_long, altitude, heart_rate, distance, speed, grade, gps_accuracy, calories) |> dplyr::mutate(distance.km=distance/1000 # Set grade >= 15 to NA ,grade.edited=dplyr::case_when(grade < 15 ~ grade, TRUE ~ NA_real_) # distance between 8.89 and 108.82km as B2GC, else as non-B2GC ,segment=dplyr::case_when(distance.km>=8.89 & distance.km <=108.82 ~ "B2GC", TRUE~ "non-B2GC") ) |> # Remove speed > max speed in Strava dplyr::filter(speed <= 64.2) # dim(B2GC2023) 32883 13 # Create plot titles plot.title.heartrate <- paste0( "Brisbane to Gold Coast 2023 \nSegment B2GC highlighted\n" ,"Heart rate (bpm)\n" ,"Max rate: ", max(B2GC2023$heart_rate, na.rm = T)) # Heartrate over distance highlighting segment B2GC, distance between 8.89 and 108.82km plot.heartrate <- ggplot2::ggplot(data=B2GC2023, aes(x=distance.km)) + ggplot2::geom_line(aes(y = heart_rate), color = "darkred")+ ggplot2::labs(title=plot.title.heartrate)+ # Change position downwards ggplot2::theme(plot.title = element_text(vjust = - 7.5, hjust=0.05, size = 12.5) ,plot.margin = unit(c(0,0.2,0,1), "lines"))+ gghighlight::gghighlight(distance.km>=8.89 & distance.km<=108.82)+ ggplot2::ylim(60,200) ``` ![](https://hackmd.io/_uploads/S182gdFRh.png) --- ## Highlight a range of X axis in an area plot ```r! B2GC2023.heartrate <- B2GC2023 |> dplyr::filter(heart_rate>=60 & heart_rate<=200) # Area plots with 2 colored areas. Not what I want as there the two groups overlapped ggplot2::ggplot(data=B2GC2023.heartrate ,mapping= aes(x=distance.km, y=heart_rate, group=segment))+ ggplot2::geom_area(data=subset(B2GC2023.heartrate, segment=="B2GC") ,aes(color=segment, fill=segment) ,alpha=0.5 ,position = "identity")+ ggplot2::geom_area(data=subset(B2GC2023.heartrate, segment=="non-B2GC") ,aes(color=segment, fill=segment) ,alpha=0.2 ,position = "identity")+ scale_color_manual(values=c("#FF0000","#000000")) + scale_fill_manual(values=c("#FF0000","#000000")) # Area plots with 2 colored areas. Not what I want as there the two groups overlapped ggplot2::ggplot(data=B2GC2023.heartrate ,aes(x=distance.km, y=heart_rate, fill=segment, alpha=segment))+ geom_area()+ scale_fill_manual(values=c("#FF0000", "#000000"))+ scale_alpha_manual(values= c(1,0.3)) ``` ![](https://hackmd.io/_uploads/r1ANe_tAn.png) ```r! B2GC2023.heartrate <- B2GC2023 |> dplyr::filter(heart_rate>=60 & heart_rate<=200) # Area plots with 2 colored areas. Not what I want as there the two groups overlapped ggplot2::ggplot(data=B2GC2023.heartrate ,aes(x=distance.km, y=heart_rate, fill=segment, alpha=segment))+ geom_area()+ scale_fill_manual(values=c("#FF0000", "#000000"))+ scale_alpha_manual(values= c(1,0.3)) ``` ![](https://hackmd.io/_uploads/SJOggdKAh.png) --- ## Show number on bottom X axis and dates on top x axis ```r! ``` ![](https://hackmd.io/_uploads/HyoRcWCc3.png) --- ## Arrange multiple ggplot2 plots on one page ### Example with 3 subjects ```r! # Create sample data with 3 subjects for plotting adverse events subject.017_306 <- data.frame(AESEQ=1 ,AETERM="fractured ankle (bilateral)" ,AESTDTC="2021-08-24" ,AESTDY=70 ,AEENDTC="2022-11-01" ,AEENDY=504 ,SUBJID="017-306") # dim(subject.017_306) 1 7 subject.017_313 <- data.frame(AESEQ=1 ,AETERM="blurred vision" ,AESTDTC="2021-09-14" ,AESTDY=29 ,AEENDTC="2021-10-05" ,AEENDY=50 ,SUBJID="017-313") # dim(subject.017_313) 1 7 subject.023_302 <- data.frame(AESEQ=c(4:1) ,AETERM=c("Tearing", "Grittiness", "eyelid itchiness","Eyelid margin crusting") ,AESTDTC=c("2022-02-06","2022-02-06","2022-02-06","2022-01-16") ,AESTDY=c(25, 25, 25, 4) ,AEENDTC=c("2022-02-12","2022-03-12","2022-02-12","2022-01-22") ,AEENDY=c(31,59,31,10) ,SUBJID=rep("023-302", 4) ) # dim(subject.023_302) 4 7 # Combine three subjects to one dataframe sample.data <- rbind(subject.017_306, subject.017_313, subject.023_302) |> dplyr::mutate(AETERM=factor(AETERM) ,AESTDTC=as.Date(AESTDTC) ,AEENDTC=as.Date(AEENDTC)) # dim(sample.data) 6 7 ae.subjects <- unique(sample.data$SUBJID) # length(ae.subjects) 3 # Create an empty list for holding results from the for loop ggplot.list <- list() # class(ggplot.list) "list" # length(ggplot.list) 0 # Create one plot object per subject for(i in 1:length(ae.subjects)){ # Get data from 1 subject plotdata <- sample.data |> dplyr::filter(SUBJID==ae.subjects[i]) # dim(plotdata) 1 7 # Create plot object name plot.object.name <- paste0("plot.", gsub(pattern="-", replacement="_", x=ae.subjects[i])) # Create 1 plot object per subject # Append plot object to the list ggplot.list[[i]] <- plotdata %>% ggplot2::ggplot(aes(xmin = AESTDTC, xmax = AEENDTC, y = SUBJID)) + ggplot2::geom_linerange(aes(color=AETERM) ,linewidth = 5 , position = position_dodge(width = 0.5)) + ggplot2::scale_x_date(name = "" ,breaks = unique(c(plotdata$AESTDTC, plotdata$AEENDTC)) ,sec.axis = dup_axis( labels = unique(c(plotdata$AESTDY, plotdata$AEENDY)) ,name = "") )+ ggplot2::labs(title = paste0("Subject ", ae.subjects[i]) ,y=NULL) + ggplot2::theme_minimal()+ # Rotate and space bottom x axis label text ggplot2::theme(axis.text.x.top = element_text(color = "red") ,axis.text.x.bottom = element_text(angle = 90) ,legend.position= c(0.5, 0.85) ,legend.direction = "horizontal" # Remove y axis label ,axis.text.y=element_blank() ) #The end of the loop } # Arrange plots on one page gridExtra::grid.arrange(ggplot.list, ncol=3, nrow=1) ``` The code above produces this plot ![](https://hackmd.io/_uploads/H1s90rHD2.png) ### Example with all subjects References [ggplot2 - Easy way to mix multiple graphs on the same page](http://www.sthda.com/english/wiki/wiki.php?id_contents=7930) [Multiple ggplot2 charts on a single page](https://r-graph-gallery.com/261-multiple-graphs-on-same-page.html) [Lay out multiple ggplot graphs on a page](https://stackoverflow.com/questions/58124284/lay-out-multiple-ggplot-graphs-on-a-page) ```r! # Check the list items length(ggplot.list) # 27 # Arrange plots on one page using the list gridExtra::grid.arrange(grobs= ggplot.list[1:9], ncol=3, nrow=3) ``` which produces ![](https://hackmd.io/_uploads/BkcAG2HP3.png) ```r! gridExtra::grid.arrange(grobs= ggplot.list[10:18], ncol=3, nrow=3) ``` which produces ![](https://hackmd.io/_uploads/HkNgmhrvn.png) ```r! gridExtra::grid.arrange(grobs= ggplot.list[19:27], ncol=3, nrow=3) ``` which produces ![](https://hackmd.io/_uploads/HJTWm2rv3.png) --- ## Plot a subject's adverse events as a gantt chart ```r! ## Sample data sample.data <- data.frame(AESEQ=c(4:1) ,AETERM=c("Tearing", "Grittiness", "eyelid itchiness","Eyelid margin crusting") ,AESTDTC=c("2022-02-06","2022-02-06","2022-02-06","2022-01-16") ,AESTDY=c(25, 25, 25, 4) ,AEENDTC=c("2022-02-12","2022-03-12","2022-02-12","2022-01-22") ,AEENDY=c(31,59,31,10) ,SUBJID=rep("023-302", 4) ) SUBJID <- unique(sample.data$SUBJID) sample.data %>% ggplot2::ggplot(aes(xmin = AESTDTC, xmax = AEENDTC, y = SUBJID, color = AETERM) ,show.legend = TRUE) + ggplot2::geom_linerange(linewidth = 5, position = position_dodge(width = 0.5) ,show.legend = TRUE) + ggplot2::labs(title = paste0("Adverse events of subject ", SUBJID) ,x = "Start and end dates" ,y="") + ggplot2::theme_minimal(base_size = 16)+ # Rotate and space bottom x axis label text ggplot2::theme(axis.text.x = element_text(angle = 0, vjust = 0.5, hjust=1) # Remove y axis label ,axis.text.y=element_blank() # Legend position: top right inside plot ## The coordinates for legend.position are x- and y- offsets from the bottom-left of the plot, ranging from 0 - 1. ,legend.position = c(0.75,0.8)) ``` --- The produced figure: ![](https://hackmd.io/_uploads/ry8ezMD82.png) --- The desired figure: ![](https://hackmd.io/_uploads/ry2HQfD8n.jpg) ---