I am having issues plotting data using calendR package in R. I am using calendR to create a calendar plot of the air quality index (AQI) category for each day a community had data onto a 2024 calendar. I'm able to successfully plot one community, but not the other. For the community I cannot plot I keep getting the error "Error in if (special.days != "weekend") { : the condition has length > 1". I'm unable to find any information on the web relating to this error code or issue.
See below code for reproducible data and how I coded to generate the calendR plots.
library(calendR)
library(tidyverse)
#create data for community 1
julian_community1 <- c(299:366) #julian days for October 25 - December 31
#create list of PM2.5 readings in community 1 from October 25 - December 31
PM25_community1 <- c(3.4,1.3,1.2,1.2,0.4,3.4,1.0, #october data
0.8,0.3,6.0,0.9,5.3,4.4,3.4,1.2,0.7,0.6,0.8,0.3,0.9,0.9,4.1,0.7,0.3,0.4,2.1,1.4,5.2,4.2,3.9,1.4,0.8,0.7,0.8,0.3,1.9,0.8, #november data
0.7,1.2,1.7,1.8,3.8,6.1,5.9,1.2,0.7,0.3,0.6,2.9,2.5,1.1,0.5,0.9,1.5,1.1,0.8,1.5,0.8,2.2,4.6,1.2,1.0,3.3,0.9,0.9,4.6,1.2,2.8#december data
)
site_community1 <- rep("community 1", 68) #repeat community 1 site name 68 times (68 days between October 25 - December 31)
#create data for community 2
julian_community2 <- c(69:366) #julian days for March 9 - December 31
#create list of PM2.5 readings in community 2 from March 9 - December 31
PM25_community2 <- c(1.6,1.5,3.4,5.8,5.1,2.6,5.4,2.8,2.5,3.7,6.2,4.8,rep(NA,286))
site_community2 <- rep("community 2", 298) #repeat community 2 site name 298 times (298 days between March 9 - December 31)
#create data frame with both communities
data <- as.data.frame(c(julian_community1,julian_community2))
colnames(data) <- "julian"
data$PM25 <- c(PM25_community1, PM25_community2)
data$site_name <- c(site_community1,site_community2)
#create AQI category for each PM25 value
one <- data$PM25 <= 9.0
two <- data$PM25 >= 9.1 & data$PM25 <= 35.4
three <- data$PM25 >= 35.5 & data$PM25 <= 55.4
four <- data$PM25 >= 55.5 & data$PM25 <= 125.4
five <- data$PM25 >= 125.5 & data$PM25 <= 225.4
six <- data$PM25 >= 225.5
data$AQI <- NA
data <- data %>% mutate(AQI = case_when(
one ~ "1",
two ~ "2",
three ~ "3",
four ~ "4",
five ~ "5",
six ~ "6"
))
Then I plotted community 1 successfully
community <- "community 1"
year <- 2024
#create objects with days in each AQI category
good <- data %>% filter(site_name == community & AQI == 1)
moderate <- data %>% filter(site_name == community & AQI == 2)
unhealthy_sg <- data %>% filter(site_name == community & AQI == 3)
unhealthy <- data %>% filter(site_name == community & AQI == 4)
veryunhealthy <- data %>% filter(site_name == community & AQI == 5)
hazardous <- data %>% filter(site_name == community & AQI == 6)
#create event list with days in each category
events <- rep(NA, 365)
events[good$julian] <- "Good"
events[moderate$julian] <- "Moderate"
events[unhealthy_sg$julian] <- "Unhealthy for sensitive groups"
events[unhealthy$julian] <- "Unhealhty"
events[veryunhealthy$julian] <- "Very unhealthy"
events[hazardous$julian] <- "Hazardous"
levels(factor(events))
#create color list
communitydata <- data %>% filter(site_name == community) #filter down to just data from given community
uniqueAQI <- unique(communitydata$AQI) #what AQI categories exist in the data from given community
#create a list of colors mutated by case when, for unique AQI groups for each community to feed into calendR
color <- data.frame(uniqueAQI)
color$color <- NA
green <- color$uniqueAQI == 1
yellow <- color$uniqueAQI == 2
orange <- color$uniqueAQI == 3
red <- color$uniqueAQI == 4
purple <- color$uniqueAQI == 5
maroon <- color$uniqueAQI == 6
color <- color %>% mutate(color = case_when(
green ~ "green",
yellow ~ "yellow",
orange ~ "orange",
red ~ "red",
purple ~ "purple",
maroon ~ "maroon"
))
palette <- as.vector(subset(color$color, !(is.na(color$color)))) #create palette vector to feed into calendR
desired_order <- c("green","yellow","orange","red","purple","maroon")
palette <- palette[order(match(palette, desired_order))]
#create plot
calendR(year = year,
start = "M",
special.days = events,
special.col = palette, #adjust colors to the unique(data$AQI)
low.col = "white",
legend.pos = "right",
legend.title = "Air Quality Index",
title = paste(community,"AQI in 2024" ),
mbg.col = "lightgray", #background color behind month title
months.col = "white", #color of month text
weeknames = c("M","T","W","T","F","S","S")
)
But when I try to plot community 2 I get the error "Error in if (special.days != "weekend") { : the condition has length > 1"
community <- "community 2"
year <- 2024
#create objects with days in each AQI category
good <- data %>% filter(site_name == community & AQI == 1)
moderate <- data %>% filter(site_name == community & AQI == 2)
unhealthy_sg <- data %>% filter(site_name == community & AQI == 3)
unhealthy <- data %>% filter(site_name == community & AQI == 4)
veryunhealthy <- data %>% filter(site_name == community & AQI == 5)
hazardous <- data %>% filter(site_name == community & AQI == 6)
#create event list with days in each category
events <- rep(NA, 365)
events[good$julian] <- "Good"
events[moderate$julian] <- "Moderate"
events[unhealthy_sg$julian] <- "Unhealthy for sensitive groups"
events[unhealthy$julian] <- "Unhealhty"
events[veryunhealthy$julian] <- "Very unhealthy"
events[hazardous$julian] <- "Hazardous"
levels(factor(events))
#create color list
communitydata <- data %>% filter(site_name == community) #filter down to just data from given community
uniqueAQI <- unique(communitydata$AQI) #what AQI categories exist in the data from given community
#create a list of colors mutated by case when, for unique AQI groups for each community to feed into calendR
color <- data.frame(uniqueAQI)
color$color <- NA
green <- color$uniqueAQI == 1
yellow <- color$uniqueAQI == 2
orange <- color$uniqueAQI == 3
red <- color$uniqueAQI == 4
purple <- color$uniqueAQI == 5
maroon <- color$uniqueAQI == 6
color <- color %>% mutate(color = case_when(
green ~ "green",
yellow ~ "yellow",
orange ~ "orange",
red ~ "red",
purple ~ "purple",
maroon ~ "maroon"
))
palette <- as.vector(subset(color$color, !(is.na(color$color)))) #create palette vector to feed into calendR
desired_order <- c("green","yellow","orange","red","purple","maroon")
palette <- palette[order(match(palette, desired_order))]
#create plot
calendR(year = year,
start = "M",
special.days = events,
special.col = palette, #adjust colors to the unique(data$AQI)
low.col = "white",
legend.pos = "right",
legend.title = "Air Quality Index",
title = paste(community,"AQI in 2024" ),
mbg.col = "lightgray", #background color behind month title
months.col = "white", #color of month text
weeknames = c("M","T","W","T","F","S","S")
)
2024 had 366 days, since it's a leap year. So change events <- rep(NA, 365)
to events <- rep(NA, 366)
.
I would change your code a little, to make it more robust. Notably, I make the dataframe definitions a little more concise and use as.numeric(format(as.Date(paste(year, "12", "31", sep="-")), "%j"))
to figure out, how many days the year
had.
library(calendR); library(tidyverse)
# Community 1 data (Oct 25 - Dec 31)
community1_data <- data.frame(
julian = 299:366,
PM25 = c(3.4,1.3,1.2,1.2,0.4,3.4,1.0, #october data
0.8,0.3,6.0,0.9,5.3,4.4,3.4,1.2,0.7,0.6,0.8,0.3,0.9,0.9,4.1,0.7,0.3,0.4,2.1,1.4,5.2,4.2,3.9,1.4,0.8,0.7,0.8,0.3,1.9,0.8, #november data
0.7,1.2,1.7,1.8,3.8,6.1,5.9,1.2,0.7,0.3,0.6,2.9,2.5,1.1,0.5,0.9,1.5,1.1,0.8,1.5,0.8,2.2,4.6,1.2,1.0,3.3,0.9,0.9,4.6,1.2,2.8 #december data
),
site_name = "community 1"
)
# Community 2 data (Mar 9 - Dec 31)
community2_data <- data.frame(
julian = 69:366,
PM25 = c(1.6,1.5,3.4,5.8,5.1,2.6,5.4,2.8,2.5,3.7,6.2,4.8,rep(NA,length(69:366)-12)),
site_name = "community 2"
)
# Combine both communities
data <- rbind(community1_data, community2_data)
#create AQI category for each PM25 value
data$AQI <- case_when(
data$PM25 <= 9.0 ~ "1",
data$PM25 >= 9.1 & data$PM25 <= 35.4 ~ "2",
data$PM25 >= 35.5 & data$PM25 <= 55.4 ~ "3",
data$PM25 >= 55.5 & data$PM25 <= 125.4 ~ "4",
data$PM25 >= 125.5 & data$PM25 <= 225.4 ~ "5",
data$PM25 >= 225.5 ~ "6",
TRUE ~ NA_character_
)
plot_calendar <- function(df, community, year){ # make use of a function
#create objects with days in each AQI category
filtered_data <- data %>% filter(site_name == community)
#create event list for all days of the year
days_in_year <- as.numeric(format(as.Date(paste(year, "12", "31", sep="-")), "%j"))
events <- rep(NA, days_in_year)
events[filtered_data$julian] <- case_when(
filtered_data$AQI == "1" ~ "Good",
filtered_data$AQI == "2" ~ "Moderate",
filtered_data$AQI == "3" ~ "Unhealthy for sensitive groups",
filtered_data$AQI == "4" ~ "Unhealthy",
filtered_data$AQI == "5" ~ "Very unhealthy",
filtered_data$AQI == "6" ~ "Hazardous",
TRUE ~ NA_character_
)
#create color palette
all_categories <- c("1", "2", "3", "4", "5", "6")
colors <- c("green", "yellow", "orange", "red", "purple", "maroon")
palette <- colors[match(unique(filtered_data$AQI[!is.na(filtered_data$AQI)]), all_categories)]
#create plot
calendR(year = year,
start = "M",
special.days = events,
special.col = palette,
low.col = "white",
legend.pos = "right",
legend.title = "Air Quality Index",
title = paste(community, "AQI in", year),
mbg.col = "lightgray",
months.col = "white",
weeknames = c("M","T","W","T","F","S","S")) # this returns the calendar plot
}
# calling functions with filters shows the plot
plot_calendar(data, "community 1", 2024)
plot_calendar(data, "community 2", 2024)