why doesn't my code work within a function?

Viewed 35

I have created two list with my data set, I am my code helps to match the two list if there are elements in one list that are not in the other using attributes that have been assigned to the two list.

library(lubridate)
library(tidyverse)
library(purrr)

date <- rep_len(seq(dmy("01-01-2011"), dmy("01-01-2013"), by = "days"), 300)
ID <- rep(c("A","B", "C"), 300)
df <- data.frame(date = date,
                 x = runif(length(date), min = 60000, max = 80000),
                 y = runif(length(date), min = 800000, max = 900000),
                 ID)

df$month <- month(df$date)
df$year <- year(df$date)

# Create first list
int1 <- df %>%
  mutate(new = floor_date(date, '10 day')) %>%
  mutate(new = if_else(day(new) == 31, new - days(10), new)) %>% 
  group_by(ID, new) %>%
  filter(month == "1") %>% 
  group_split()

# Create second list
int2 <- df %>%
  mutate(new = floor_date(date, '10 day')) %>%
  mutate(new = if_else(day(new) == 31, new - days(10), new)) %>% 
  group_by(ID, new) %>%
  filter(month == "2") %>% 
  group_split()

int1 <- int1[-c(3,6)]


# Convenience function to grab the attributes for you
check <- function(list, attribute_name) {
  return(attr(list, attribute_name))
}

# Add an attribute to hold the attributes of each list element
attr(int1, "match") <- data.frame(id = sapply(int1, function(x) paste(x$ID[1])),
                                  interval_start_date = sapply(int1, function(x) paste(x$new[1]))
)

# Check the attributes
check(int1)

# Add an attribute "tab" to hold the attributes of each list element
attr(int2, "match") <- data.frame(id = sapply(int2, function(x) paste(x$ID[1])),
                                  interval_start_date = sapply(int2, function(x) paste(x$new[1]))
) 

# Check the attributes
check(int2)

# Creates an index for the two list based on the attributes, 
i1 <-  with(check(int2, "match"), paste(id, format(as.Date(interval_start_date),
                                                               "%Y-%d"))) %in%  
  with(check(int1, "match"), paste(id, format(as.Date(interval_start_date), 
                                                          "%Y-%d")))

out <- int2[i1]

The code works, and it correctly provides the output, but when I put the code into a function, it ends up giving an error and ultimately does not work.

match_intervals <- function(attribute_name, list1, list2, id_col, interval_col){
  
  # Creates an index for the two list based on the attributes, 
  i1 <-  with(check(list2, attribute_name), paste(id_col, format(as.Date(interval_col),
                                                                 "%Y-%d"))) %in%  
    with(check(list1, attribute_name), paste(id_col, format(as.Date(interval_col), 
                                                            "%Y-%d")))
  
  out <- list2[i1]
  return(out)
}

int2 <- match_intervals(attribute_name = "match", int1, int2, id, 
                        interval_col = interval_start_date)

I get this error: Error in as.Date(interval_col) : object 'interval_start_date' not found and/or Error in as.Date.default(interval_col) : do not know how to convert 'interval_col' to class “Date”

Any thoughts on what might be happening and how I can fix this?

Error: enter image description here

1 Answers

We may pass the input column names as string and then use [[ instead of with

match_intervals <- function(attribute_name, list1, list2, id_col, interval_col){
  
  # Creates an index for the two list based on the attributes, 
  dat2 <- check(list2, attribute_name)
  dat1 <- check(list1, attribute_name)
  i1 <-   paste(dat2[[id_col]], format(as.Date(dat2[[interval_col]]),
                                                                 "%Y-%d")) %in%  
    paste(dat1[[id_col]], format(as.Date(dat1[[interval_col]]), 
                                                            "%Y-%d"))
  
  out <- list2[i1]
  return(out)
}

-testing

int2 <- match_intervals(attribute_name = "match", int1, int2, "id", 
                        interval_col = "interval_start_date")
> length(int2)
[1] 7

According to ?with

For interactive use this is very effective and nice to read. For programming however, i.e., in one's functions, more care is needed, and typically one should refrain from using with(), as, e.g., variables in data may accidentally override local variables, see the reference.

Related