Home > Net >  extracting items from list - how to account for character(0)
extracting items from list - how to account for character(0)

Time:03-15

I am trying to extract the last element from the list nuts. In one row, however, the content is character(0). Hence, the extraction of the last element fails. I am struggling to control for the presence of character(0). Any help? Many thanks.

library(tidyverse)

my_df <- tibble(
  txt=c("chestnut, pear, kiwi, peanut",
        "grapes, banana"))

#Extract all nuts
my_df <- my_df %>% 
  mutate(nuts=str_extract_all(txt, regex("\\w*nut\\w*"))) 

#there were no nuts in the second row; hence character(0)
my_df$nuts
#> [[1]]
#> [1] "chestnut" "peanut"  
#> 
#> [[2]]
#> character(0)

#now i want to extract the last element from the list; doesn't work
my_df %>% 
  mutate(last_item=map_chr(nuts, ~tail(.x, 1)))
#> Error in `mutate_cols()`:
#> ! Problem with `mutate()` column `last_item`.
#> i `last_item = map_chr(nuts, ~tail(.x, 1))`.
#> x Result 2 must be a single string, not a character vector of length 0
#> Caused by error in `stop_bad_type()`:
#> ! Result 2 must be a single string, not a character vector of length 0

#the reason for the failure is the second row with character(0), the other row works,
my_df %>% 
  slice(., 1) %>% 
  mutate(last_item=map_chr(nuts, ~tail(.x, 1)))
#> # A tibble: 1 x 3
#>   txt                          nuts      last_item
#>   <chr>                        <list>    <chr>    
#> 1 chestnut, pear, kiwi, peanut <chr [2]> peanut

#how to make analysis account for the presence of character(0); 

#Attempt 1: purrr::possibly doesn't work either
my_df %>% 
  slice(., 1) %>% 
  mutate(last_item=map_chr(nuts, ~purrr::possibly(tail(.x, 1),
                                           otherwise="NA")))
#> Error in `mutate_cols()`:
#> ! Problem with `mutate()` column `last_item`.
#> i `last_item = map_chr(nuts, ~purrr::possibly(tail(.x, 1), otherwise = "NA"))`.
#> x Can't coerce element 1 from a closure to a character
#> Caused by error:
#> ! Can't coerce element 1 from a closure to a character

#Attempt 2: Circumvent the issue by taking the length of the list into consideration;
#but my map - command doesn't work now.
my_df %>% 
  mutate(list_length=map_dbl(nuts, length)) %>% 
  mutate(last_item=case_when(
         list_length>0 ~ ~map_chr(nuts, ~tail(.x, 1)),
         list_length==0 ~ NA_character_))
#> Error in `mutate_cols()`:
#> ! Problem with `mutate()` column `last_item`.
#> i `last_item = case_when(...)`.
#> x must have class `call`, not class `formula`.
#> Caused by error in `glubort()`:
#> ! must have class `call`, not class `formula`.

Created on 2022-03-15 by the reprex package (v2.0.1)

CodePudding user response:

You can do:

my_df |> 
  rowwise() |> 
  mutate(last_item = ifelse(length(nuts) == 0L, unlist(nuts), nuts[[length(nuts)]])) |> 
  ungroup()

# A tibble: 2 x 3
  txt                          nuts      last_item
  <chr>                        <list>    <chr>    
1 chestnut, pear, kiwi, peanut <chr [2]> peanut   
2 grapes, banana               <chr [0]> NA       

CodePudding user response:

library(tidyverse)

my_df <- tibble(
  txt = c(
    "chestnut, pear, kiwi, peanut",
    "grapes, banana"
  )
) %>%
  mutate(nuts = str_extract_all(txt, regex("\\w*nut\\w*")))

my_df %>%
  mutate(
    last_item = nuts %>% map_chr(last)
  )
#> # A tibble: 2 × 3
#>   txt                          nuts      last_item
#>   <chr>                        <list>    <chr>    
#> 1 chestnut, pear, kiwi, peanut <chr [2]> peanut   
#> 2 grapes, banana               <chr [0]> <NA>


my_df %>%
  mutate(
    # can not use map_chr becasue NA is not of class character
    last_item = nuts %>% map(possibly(~tail(.x, 1),  NA))
  )
#> # A tibble: 2 × 3
#>   txt                          nuts      last_item
#>   <chr>                        <list>    <list>   
#> 1 chestnut, pear, kiwi, peanut <chr [2]> <chr [1]>
#> 2 grapes, banana               <chr [0]> <chr [0]>

Created on 2022-03-15 by the reprex package (v2.0.0)

  • Related