missing column in my final output causing an error, how to: retain the column or skip the error using lapply?

Viewed 31

My lapply function cannot continue because of an error. What I noticed is because the column with which.max are gone in the column when there is no data for it, therefore lapply do not continue. If I am not explaining it well, here's the data and script:

data:

store fruit color start end
Nina banana yellow 1 9
Lana apple red 2 3
Nina grapes green 4 2
Nina banana green 4 7
Lana banana yellow 3 6
Lana apple green 2 5
Lana grapes purple 3 4
Nina grapes purple 2 3

and the script:

library(readr)
library(dplyr)
library(tidyverse)

list = c("apple", "banana", "mango", "grapes")
data=read_tsv("/home/polen/Desktop/data.tsv", col_names = TRUE)

group_func <- function(list) {
  df <- data %>% filter(fruit == {{list}}) %>%
    group_by(fruit) %>%
    summarise(color = names(which.max(table(color))), min_start = min(start), max_end = max(end)) %>% select(fruit,color,min_start)
  assign(list, df, envir = .GlobalEnv)
}

lapply(list, group_func)

this will return two files: apple and banana and an error "Column color doesn't exist." However, if I remove the select function:

list = c("apple", "banana", "mango", "grapes")
data=read_tsv("/home/polen/Desktop/data.tsv", col_names = TRUE)

group_func <- function(list) {
  df <- data %>% filter(fruit == {{list}}) %>%
    group_by(fruit) %>%
    summarise(color = names(which.max(table(color))), min_start = min(start), max_end = max(end))
  assign(list, df, envir = .GlobalEnv)
}

lapply(list, group_func)

it will return 4 files: apple, banana, mango and grapes. Mango having only 3 variables (which causes the error in lapply).

What I want is to either:

  1. skip the error so it will continue to the next loop.
  2. retain the column that was removed in summarise so the loop will continue.

I tried tryCatch but I am not able to make it work, especially since (although not included in my above script), I am exporting those files to my computer. Although maybe it will work, I'm just really noob about it.

I will really appreciate help for this. Thank you!

1 Answers

Issue

First of all, the reason a try catch doesn't work is as follows:

  • When it gets to mango, there are no rows, so color = character(0) (empty character vector). If you run your code names(which.max(table(color))) where color = character(0), the output would be NULL.
  • Setting the column color NULL would essentially drop the column, hence color not existing when you do the select (try running data.frame(color = NULL) for example and you will see that the result has no columns)

Direct solution

To solve for this directly, we could check if your code is NULL and replace with NA so that the column isn't dropped. For example, we can define a function as follows to do this check:

get_max_val <- function(x){
  result = names(which.max(table(x)))
  if (is.null(result)){
    return (NA)
  } else{
    return (result) 
  }
}

Now, if you re-define your grouping function as such, it will work:

group_func <- function(list) {
  df <- data %>% filter(fruit == {{list}}) %>%
    group_by(fruit) %>%
    summarise(color = get_max_val(color), min_start = min(start), max_end = max(end))
  assign(list, df, envir = .GlobalEnv)
}

Alternate proposal

Seeing the output of the above function, you can re-think the problem as such:

  1. Read in your data (using the sample you provided; note that your second column was not called fruit in your table so I renamed manually)

    library(dplyr)
    
    data = read.table(text = "store  fruit   color   start   end
    Nina banana  yellow  1   9
    Lana apple   red 2   3
    Nina grapes  green   4   2
    Nina banana  green   4   7
    Lana banana  yellow  3   6
    Lana apple   green   2   5
    Lana grapes  purple  3   4
    Nina grapes  purple  2   3", header = T)
    
  2. Group by fruit and color. Then count the number of colors, the minimum start and the maximum end

    data %>%
      group_by(fruit, color) %>%
      summarise(color_count = n(), min_start = min(start), max_end = max(end))
    

    Output is

      fruit  color  color_count min_start max_end
      <chr>  <chr>        <int>     <int>   <int>
    1 apple  green            1         2       5
    2 apple  red              1         2       3
    3 banana green            1         4       7
    4 banana yellow           2         1       9
    5 grapes green            1         4       2
    6 grapes purple           2         2       4
    
  3. Notice that for each fruit, we have counts of color so if we arrange in descending order by color_count and take the first row, we will keep the fruit with the highest color_count. Let's do that now and also compute also the min start and max end

    result = data %>%
      group_by(fruit, color) %>%
      summarise(color_count = n(), min_start = min(start), max_end = max(end)) %>%
      arrange(fruit, desc(color_count)) %>%
       group_by(fruit) %>%
       summarise(top_color = first(color), min_start = min(min_start), max_end = max(max_end))
    
    result
    

Resulting output is:

  fruit  top_color min_start max_end
  <chr>  <chr>         <int>   <int>
1 apple  green             2       5
2 banana yellow            1       9
3 grapes purple            2       4

If you did want to assign to the global env once done, you can do:

list_of_fruits = c("apple", "banana", "mango", "grapes")

lapply(list_of_fruits, function(fruit_name){
    filtered_result = result %>% filter(names == {{fruit_name}})
    assign(fruit_name, filtered_result, envir = .GlobalEnv)
})
Related