在dplyr和mutate中使用strsplit和subset

chu*_*271 9 r strsplit dplyr

我有一个包含一个字符串列的数据表.我想使用strsplit创建另一列作为此列的子集.

dat <- data.table(labels=c('a_1','b_2','c_3','d_4'))
Run Code Online (Sandbox Code Playgroud)

我想要的输出是

label  sub_label
a_1    a
b_2    b
c_3    c
d_4    d 
Run Code Online (Sandbox Code Playgroud)

我尝试过以下但似乎都没有效果.

dat %>%
    mutate(
        sub_labels=strsplit(as.character(labels), "_")[[1]][1]
    ) 
# gives a column whose values are all "a"
Run Code Online (Sandbox Code Playgroud)

这个对我来说合乎逻辑,

dat %>%
    mutate(
        sub_labels=sapply(strsplit(as.character(labels), "_"), function(x) x[[1]][1])
    )
Run Code Online (Sandbox Code Playgroud)

给出错误

错误:不知道如何处理类型的pairlist

我看到另一个文章,其中strsplit的输出上的粘贴崩溃工作,所以我不明白为什么匿名函数中的子集正在给出问题.感谢您对此的任何澄清.

Rom*_*ois 20

tidyr::separate 可以帮助:

> dat %>% separate(labels, c("first", "second") )
   first second
1:     a      1
2:     b      2
3:     c      3
4:     d      4    
Run Code Online (Sandbox Code Playgroud)

  • @thelatemail 我认为你可以指定 `remove = FALSE` 来处理这个问题。 (3认同)

Gen*_*Rus 10

另一种方法使用purrr's map_chr,我发现它对于我不想打扰分离和合并的应用程序很有用(例如,将结果sprintf与其他字符串一起使用):

tibble(labels=c('a_1','b_2','c_3','d_4')) %>% 
  mutate(sub_label = stringr::str_split(labels, "_") %>% map_chr(., 1))
Run Code Online (Sandbox Code Playgroud)

这种方法可以比separate我的经验快得多,尤其是当您有更长的数据集时。separate当我使用 100 个字符串时几乎没有击败 map,但在大多数情况下当我使用 1000 个时会落后(不确定这个最大值是怎么回事)。

    > microbenchmark::microbenchmark(
+   d.filtered_reads %>% head(1000) %>% 
+     mutate(name = stringr::str_split(Header, " ") %>% map_chr(., 1)) %>% 
+     select(-Header),
+   d.filtered_reads %>% head(1000) %>% 
+     separate(Header, into = c("name","index"), sep = " ") %>% 
+     select(-"index")
+ )
Unit: milliseconds
                                                                                                                          expr
 d.filtered_reads %>% head(1000) %>% mutate(name = stringr::str_split(Header,      " ") %>% map_chr(., 1)) %>% select(-Header)
          d.filtered_reads %>% head(1000) %>% separate(Header, into = c("name",      "index"), sep = " ") %>% select(-"index")
      min       lq     mean   median       uq       max neval
 5.333891 5.817589 6.292954 5.935706 6.059031 41.530089   100
 7.517316 8.031325 8.399471 8.500359 8.647468  9.855612   100
Run Code Online (Sandbox Code Playgroud)

  • 值得一提的是,`strsplit` 被替换为 `stringr` 中的 `str_split` (https://github.com/tidyverse/stringr)。另外,此代码也可以作为替代方案:`dat %&gt;% mutate(sub_label = sapply(str_split(labels, "_"), function(x) x[1]))` (2认同)

Hen*_*ndy 10

我没有想出这个,我只是在寻找解决方案时偶然发现了这个 github 问题,并认为它比这里的许多答案更简单,特别是避免了额外的map_chr()tmp_chunks.

# I used data.frame since I don't have data table installed
library(dplyr)
library(stringr)
dat <- data.frame(labels=c('a_1','b_2','c_3','d_4'))
dat %>% mutate(sub_label = str_split(labels, "_", simplify = T)[, 1])
  labels sub_label
1    a_1         a
2    b_2         b
3    c_3         c
4    d_4         d
Run Code Online (Sandbox Code Playgroud)


Dom*_*omQ 5

如果我们想一次提取多个列(当然,无需再次运行分割),我们可以将GenesRus方法与一个临时列结合起来,我们将其与负数select()一起放在管道中:

library(purrr)
library(dplyr)
library(tibble)
library(stringr)

tibble(labels=c('a_1','b_2','c_3','d_4')) %>% 
  mutate(tmp_chunks = stringr::str_split(labels, stringr::fixed("_"),  n = 2)) %>%
  mutate(sub_label = map_chr(tmp_chunks, 1),
         sub_value = map_chr(tmp_chunks, 2)) %>%
  select(-tmp_chunks)
Run Code Online (Sandbox Code Playgroud)

截至 2020 年,性能separate().

为了完整起见,值得一提的是

  • map_chr可以接受一个.default参数(以防某些行中缺少分隔符),
  • 如果需要的话,也可以labels用 negative去掉select()