使用dplyr的mutate函数和现有列的子字符串创建新列

31

我有一个包含字符串列的数据帧,并想将它们的子串提取到一个新列中。

以下是一些示例代码和数据,显示我想要获取 id 列最后一个下划线字符后面的字符串,以创建一个 new_id 列。该 id 列条目始终具有 2 个下划线字符,并且我总是想要最后的子串。

df = data.frame( id = I(c("abcd_123_ABC","abc_5234_NHYK")), x = c(1.0,2.0) )

require(dplyr)

df = df %>% dplyr::mutate(new_id = strsplit(id, split="_")[[1]][3])

我原本希望strsplit依次作用于每一行。

然而,new_id列在每一行中只包含ABC,我想在第1行中有ABC,在第2行中有NHYK。您知道为什么会出现问题,并且如何实现我想要的结果吗?


这是因为你调用了 strsplit[[1]] 总是获取列表的第一个元素。 - Lloyd Christmas
在基本的R语言中,只需要一点正则表达式的魔法:df$newVar <- sub(".*_([A-Z]+)$", "\\1", df$id) - lmo
5个回答

35
你可以使用 stringr::str_extract 函数:
library(stringr)

 df %>%
   dplyr::mutate(new_id = str_extract(id, "[^_]+$"))


#>              id x new_id
#> 1  abcd_123_ABC 1    ABC
#> 2 abc_5234_NHYK 2   NHYK

这个正则表达式表示,匹配一个或多个(+)不是_的字符(否定类 [^ ]),后面跟着字符串结尾符$


32

不使用正则表达式,且保持 tidyverse 风格的另一种选择是使用 tidyr::separate()。请注意,默认情况下此方法会删除输入列(使用 remove=FALSE 可以防止删除)。

## using your example data
df = data.frame( id = I(c("abcd_123_ABC","abc_5234_NHYK")), x = c(1.0,2.0) )

## separate knowing you will have three components
df %>% separate(id, c("first", "second", "new_id"), sep = "_") %>% select(-first, -second)
## returns
  new_id x
1    ABC 1
2   NHYK 2

12

2
请注意,这种方法比典型的dplyr慢,因为它无法从矢量化操作中受益。不过,这个技巧还是值得一试的。 - vincentmajor

6

可以使用 str_split 函数并指定 simplify 参数来完成此操作。

simplify 可以将分割后的字符串展开,并允许通过索引进行元素选择。在这种情况下,由于每个字符串中都有 2 个“_”,我们总是可以选择第三个元素。

# Create df
df = data.frame( id = I(c("abcd_123_ABC","abc_5234_NHYK")), x = c(1.0,2.0) )

# Create new_id using dplyr only
df <- df %>% 
  mutate(new_id = str_split(id, "_", simplify = TRUE)[ , 3])

请查看https://github.com/tidyverse/stringr/issues/265


1
这里有一种使用 strsplit 的通用方法来实现你所需的功能。
library(dplyr)
df = data.frame( id = I(c("abcd_123_ABC","abc_5234_NHYK")), x = c(1.0,2.0) )

temp <- seq(from=3, by=3, length.out = length(df))
dfn <- df %>% dplyr::mutate(new_id = unlist(strsplit(id, split="_"))[temp])

> dfn
             id x new_id
1  abcd_123_ABC 1    ABC
2 abc_5234_NHYK 2   NHYK

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接