将字符向量列子集化为多列

Subsetting a character vector column into multiple columns

我有以下问题:

colours = tribble(
  ~all,
  c('blue','green', 'red', 'pink', 'yellow', 'gold', 'orange', 'ivory', 'brown', 'beige'),
  c('green', 'red', 'pink', 'orange', 'ivory', 'beige')
)

我想根据颜色系列将颜色分成多列:CoolWarmNeutral,每个系列一列。

我可以使用 mutatemapstr_subset:

colours %>%
  mutate(
    'Cool' = map(all, ~str_subset(., '^(blue|green)$')), 
    'Warm' = map(all, ~str_subset(., '^(red|pink|yellow|gold|orange)$')),
    'Neutral' = map(all, ~str_subset(., '^(ivory|brown|beige)$'))
  )

# A tibble: 2 x 4
  all        Cool      Warm      Neutral  
  <list>     <list>    <list>    <list>   
1 <chr [10]> <chr [2]> <chr [5]> <chr [3]>
2 <chr [6]>  <chr [1]> <chr [3]> <chr [2]>

但我想知道是否有更简洁的方法来实现相同的结果?我试过 tidyr::extract() 但似乎无法获得正则表达式右:

colours %>% 
  mutate(all = map(all, ~paste(., collapse = ' '))) %>% 
  extract(all, into = c('Cool', 'Warm', 'Neutral'), 
          regex = '(blue|green)|(red|pink|yellow|gold|orange)|(ivory|brown|beige)')

我猜这是不正确的,因为 OR 语句匹配每个组中的单个单词,而不是将字符串分成三个子字符串,每个子字符串包含每个组的所有匹配单词? Here is the demo

我非常确信 extract 行不通,但它使用了正确的正则表达式。它实际上并不比您的第一个解决方案 "succinct" 多多少,但我认为它可能已经尽可能简洁了。 (如果你想缩短事情,考虑将你的颜色折叠成一个双元素字符向量,而不是一个带有列表列的数据框。)

您的正则表达式模式的问题是您对 | 的使用。您想要定位单词集合,而不是 "x OR y OR z",这是您的模式所做的,也是您每行只获得一个匹配项的原因。要创建可能匹配项的集合,请使用 []。为 "zero or more" 个匹配项包含 *。使用上面的示例数据:

library(tidyverse)

colours %>% 
    mutate(all = map(all, str_c, collapse = " ")) %>% 
    extract(all, c("cool", "warm", "neutral"),
            "([blue green]*) ([red pink yellow gold orange]*) ([ivory brown beige]*)",
            remove = F # Include the `all` column.
    )

#### OUTPUT ####

# A tibble: 2 x 4
  all       cool       warm                        neutral          
  <list>    <chr>      <chr>                       <chr>            
1 <chr [1]> blue green red pink yellow gold orange ivory brown beige
2 <chr [1]> green      red pink orange             ivory beige      

主要注意事项是 颜色类别 需要按正确的顺序排列,即字符串必须包含顺序为 cool 的颜色词组 → warmneutral。如果它们是随机的,它将不起作用。事实上,如果颜色词是随机的,我认为 extract 将不再有效,因为无法提取单个词然后将它们连接起来。您还会丢失列表中的列 - 如果这对您很重要。

如果无法保证顺序,或者可能缺少某些类别词,那么您可以执行以下操作。使用类别词的随机样本(请注意,我删除了列表列,以便您可以看到发生了什么):

col_rand <- tribble(
    ~all,
    sample(c('blue','green', 'red', 'pink', 'yellow', 'gold', 'orange', 'ivory', 'brown', 'beige'), 5),
    sample(c('green', 'red', 'pink', 'orange', 'ivory', 'beige'), 4)
) %>% 
    mutate(all = map(all, str_c, collapse = " ") %>% unlist())

#### OUTPUT ####

# A tibble: 2 x 1
  all                       
  <chr>                     
1 blue yellow red beige pink
2 ivory pink beige orange   

并具有以下模式:

patts <- c(cool = "blue|green",
           warm = "red|pink|yellow|gold|orange",
           neutral = "ivory|brown|beige"
           )

您可以执行类似以下操作,提取匹配项并将它们连接起来,或者 returns NA 如果没有匹配项:

library(magrittr)

unlist(col_rand$all) %>% 
    map_dfr(function(x) {str_extract_all(x, patts) %>%
            map(function(x) ifelse(length(x) == 0,
                                   NA,
                                   str_c(x, collapse = " ")
                                   )
                ) %>% 
            bind_cols()}) %>% 
    set_colnames(names(patts)) %>% bind_cols(col_rand, .)

#### OUTPUT ####

# A tibble: 2 x 4
  all                        cool  warm            neutral    
  <chr>                      <chr> <chr>           <chr>      
1 blue yellow red beige pink blue  yellow red pink beige      
2 ivory pink beige orange    NA    pink orange     ivory beige

Note that the magrittr library is loaded for the set_colnames. If you load magrittr after tidyverse/tidyr you'll need to use tidyr::extract() above because both libraries have an extract function.