处理多个 grepl 语句中的关系

Handling ties in multiple grepl statements

我正在做一项作业,需要清除大量混乱的字符串数据。
我已经解决了大多数问题,但遇到了两个问题:

  1. 使用多个 grepl 语句时的关系
  2. 我觉得很多代码可以简化,但我不知道如何简化

让我们考虑这个最小的例子:

names 是一个字符向量,存储 3 个不同的人的名字,以各种方式书写

names 应该简化(重新编码),以便多次出现的人名以相同的方式存储

让我们假设 Johnatan 是第一约翰,
约翰尼和约翰尼都是约翰二世,
John, John D., John Doe 是第三约翰。

凭借我有限的 R 知识,我得出了这个解决方案:

names <- c("John", "Johnatan", "Johnnie", "John D.", "John Doe", "johnnie")

names[grepl("johna", names, ignore.case = TRUE)] <- "First John"
names[grepl("johnn", names, ignore.case = TRUE)] <- "Second John"
names[grepl("john d*", names, ignore.case = TRUE)] <- "Third John"

此时 john 我不知道如何将 Third John 重新编码为

names[grepl("john", names, ignore.case = TRUE)]

会接走 names 的所有约翰。

问题:

我该如何处理这种关系,希望在某种程度上比我目前所写的更优雅?

感谢您的任何提示和建议。

temp = c(Johnatan = "First John", johnnie = "Second John", John = "Third John")
temp[apply(X = sapply(names(temp),
                 function(x) grepl(pattern = x,
                                   x = names,
                                   ignore.case = TRUE)),
      MARGIN = 1,
      FUN = function(x) head(which(x), 1))]
#         John      Johnatan       johnnie          John          John       johnnie 
# "Third John"  "First John" "Second John"  "Third John"  "Third John" "Second John" 

您可以为 "john":

使用单词边界 (\b)
names <- c("John", "Johnatan", "Johnnie", "John D.", "John Doe", "johnnie")
names2 = names

names2[grepl("johna", names, ignore.case = TRUE)] <- "First John"
names2[grepl("johnn", names, ignore.case = TRUE)] <- "Second John"
names2[grepl("john(\b|\sd.*)", names, ignore.case = TRUE)] <- "Third John"

case_when 来自 dplyr:

library(dplyr)
names = case_when(grepl("johna", names, ignore.case = TRUE) ~ "First Join",
                  grepl("johnn", names, ignore.case = TRUE) ~ "Second Join",
                  grepl("john(\b|\sd.*)", names, ignore.case = TRUE) ~ "Third Join")

注:

  • \b 匹配单词边界,可以是 space 或标点符号。例如 johnatan 不会匹配,因为 john 跟在另一个字母 a 之后,而不是单词边界。

  • \s 匹配 space.

  • d.* 匹配 d 后跟任何内容 (.) 零次。

  • ( | ) 是匹配 |.

  • 左侧或右侧的捕获组
  • john(\b|\sd.*) 匹配 john 后跟单词边界或 space 后跟 d 和任何零次或多次。因此匹配 "john""john d.""john doe"ignore.case = TRUE 会处理这些情况)。

结果:

> names2
[1] "Third John"  "First John"  "Second John" "Third John"  "Third John"  "Second John"