基于正则表达式创建新变量

16

我的问题涉及如何基于正则表达式的结果在R中创建一个数据框中的新变量。以下是最小化的数据示例:

df <- data.frame(model=c("Legacy 2.0  BG5 B4 AUTO","Legacy 2.0 BH5 AT","Legacy 2.0i CVT Non Leather","Legacy 2.0i CVT","Legacy 2.0 BL5 AUTO B4",
                 "Legacy 2.0 BP5 AUTO","Legacy 2.0 BM5 AUTO CVT"), CRSP=c(3450000,3365000,4950000,5250000,4787526,3550000,5235000))

df
                        model    CRSP
1     Legacy 2.0  BG5 B4 AUTO 3450000
2           Legacy 2.0 BH5 AT 3365000
3 Legacy 2.0i CVT Non Leather 4950000
4             Legacy 2.0i CVT 5250000
5      Legacy 2.0 BL5 AUTO B4 4787526
6         Legacy 2.0 BP5 AUTO 3550000
7     Legacy 2.0 BM5 AUTO CVT 5235000

我想创建一个新变量'chassis',其值为相应'model'变量字符串的第三个元素,最终得到:

df
                        model    CRSP chassis
1     Legacy 2.0  BG5 B4 AUTO 3450000     BG5
2           Legacy 2.0 BH5 AT 3365000     BH5
3 Legacy 2.0i CVT Non Leather 4950000     CVT
4             Legacy 2.0i CVT 5250000     CVT
5      Legacy 2.0 BL5 AUTO B4 4787526     BL5
6         Legacy 2.0 BP5 AUTO 3550000     BP5
7     Legacy 2.0 BM5 AUTO CVT 5235000     BM5

我需要找到一种方法来提取每行中的适当元素并将它们放入新变量中。任何帮助都将不胜感激。

7个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
10

这里是使用 stringi 的一种可能解决方案。

library(stringi)
df$chassis <- stri_extract_all_words(df$model, simplify = TRUE)[, 3]
df
#                         model    CRSP chassis
# 1     Legacy 2.0  BG5 B4 AUTO 3450000     BG5
# 2           Legacy 2.0 BH5 AT 3365000     BH5
# 3 Legacy 2.0i CVT Non Leather 4950000     CVT
# 4             Legacy 2.0i CVT 5250000     CVT
# 5      Legacy 2.0 BL5 AUTO B4 4787526     BL5
# 6         Legacy 2.0 BP5 AUTO 3550000     BP5
# 7     Legacy 2.0 BM5 AUTO CVT 5235000     BM5
同样地,

Or similarly

df$chassis <- sapply(stri_extract_all_words(df$model), `[`, 3)

6

对于这种任务和将所有部分提取到单独的列中,我非常喜欢tidyr

if (!require("pacman")) install.packages("pacman")
pacman::p_load(dplyr, tidyr)

regx <- "(^[A-Za-z]+\\s+[0-9.a-z]+)\\s+([A-Z0-9]+)\\s*(.*)"

df %>%
    extract(model, c("a", "chassis", "b"), regx, remove=FALSE)

##                         model           a chassis           b    CRSP
## 1     Legacy 2.0  BG5 B4 AUTO  Legacy 2.0     BG5     B4 AUTO 3450000
## 2           Legacy 2.0 BH5 AT  Legacy 2.0     BH5          AT 3365000
## 3 Legacy 2.0i CVT Non Leather Legacy 2.0i     CVT Non Leather 4950000
## 4             Legacy 2.0i CVT Legacy 2.0i     CVT             5250000
## 5      Legacy 2.0 BL5 AUTO B4  Legacy 2.0     BL5     AUTO B4 4787526
## 6         Legacy 2.0 BP5 AUTO  Legacy 2.0     BP5        AUTO 3550000
## 7     Legacy 2.0 BM5 AUTO CVT  Legacy 2.0     BM5    AUTO CVT 5235000
你可以使用这个正则表达式来更通用一些:

regx <- "(^[^ ]+\\s+[^ ]+)\\s+([^ ]+)\\s*(.*)"

还要注意,您可以使用extract方法来获取您需要的列,只需删除第一个和最后一个组的分组括号,如下所示:

regx <- "^[A-Za-z]+\\s+[0-9.a-z]+\\s+([A-Z0-9]+)\\s*.*"

df %>% 
    extract(model, "chassis", regx, remove=FALSE)

2
我在同一个 dplyr 管道 %>% 中对相同的数据框进行其他前置和后续数据操作步骤,因此这个解决方案更加方便,因为我可以直接将其插入循环中。谢谢 @TylerRinker - amo

5
一种使用strsplit的替代方案。
# Split each of the models using space (the + accounts for multiple spaces)
# Note that model is a factor in your data frame, so it must be cast to char
model.split <- strsplit(as.character(df$model), " +")
# Now go through each element of the splitted list and get the 3rd word
df$chassis <- sapply(model.split, function(x){x[3]})

2
或者使用 data.table 的类似方法。setDT(df)[, chassis:=tstrsplit(model, ' +')[[3]]][] - akrun

4
我们可以匹配字符,直到包括数字部分的 i 和空格,使用 sub 将其替换为 '',然后使用 word 提取第一个单词。
library(stringr)
 word(sub('^\\D*[0-9.i ]*', '', df$model),1)
#[1] "BG5" "BH5" "CVT" "CVT" "BL5" "BP5" "BM5"
或者匹配空格,用一个空格替换并使用word
 word(gsub(' +', ' ', df$model),3)
 #[1] "BG5" "BH5" "CVT" "CVT" "BL5" "BP5" "BM5"

注意:不确定'model'的第一个元素中多余的空格是否为拼写错误。如果原始数据集中单词之间没有超过一个空格,则word(df$model, 3)将有效。


3
这可以在基本的R语言中轻松完成:
transform(df, chassis=sub("^(\\S+\\s+){2}(\\S+).*", "\\2", model))

生成:

                        model    CRSP chassis
1     Legacy 2.0  BG5 B4 AUTO 3450000     BG5
2           Legacy 2.0 BH5 AT 3365000     BH5
3 Legacy 2.0i CVT Non Leather 4950000     CVT
4             Legacy 2.0i CVT 5250000     CVT
5      Legacy 2.0 BL5 AUTO B4 4787526     BL5
6         Legacy 2.0 BP5 AUTO 3550000     BP5
7     Legacy 2.0 BM5 AUTO CVT 5235000     BM5     

2
您可以使用splitstackshape包中的cSplit按空格字符进行分割:
library(splitstackshape)
df$chassis <- cSplit(df, "model", sep = " ", "wide")$model_3
这可以避免使用正则表达式或apply函数的需要。

cSplit有一个选项drop=FALSE,并返回一个data.table。因此,也许这个会起作用cSplit(df, "model", sep = " ", "wide", drop=FALSE)[, c(1,5), with=FALSE] - akrun

0

使用 unglue 我们可以做到:

# install.packages("unglue")
library(unglue)

unglue_unnest(df, model, "{=.*?} {=.*?} {chassis=[^ ]+}{=.*?}", remove = FALSE)
#>                         model    CRSP chassis
#> 1     Legacy 2.0  BG5 B4 AUTO 3450000     BG5
#> 2           Legacy 2.0 BH5 AT 3365000     BH5
#> 3 Legacy 2.0i CVT Non Leather 4950000     CVT
#> 4             Legacy 2.0i CVT 5250000     CVT
#> 5      Legacy 2.0 BL5 AUTO B4 4787526     BL5
#> 6         Legacy 2.0 BP5 AUTO 3550000     BP5
#> 7     Legacy 2.0 BM5 AUTO CVT 5235000     BM5

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