Search code examples
r

calendR - "Error in if (special.days != "weekend" { : the condition has length > 1" for some datasets but not others


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")
)

Screenshot of generated plot from above code depicting the air quality index category for "community 1" in 2024, there is only data from October 25 on and all days are in the "good" air quality index category.

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")
)

Solution

  • Fix

    2024 had 366 days, since it's a leap year. So change events <- rep(NA, 365) to events <- rep(NA, 366).

    Add

    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)