根据单独的列表替换多个 strings/values

Replace multiple strings/values based on separate list

我有一个类似于此的数据框:

EVENT  ID            GROUP   YEAR    X.1         X.2           X.3            Y.1           Y.2           Y.3
1      1 John Smith  GROUP1  2015  1 John Smith  5 Adam Smith  12 Mike Smith  20 Sam Smith  7 Luke Smith  3 George Smith

新日志的每一行都会重复,但 X.1 : Y.3 中的值经常变化。 ID 和 X.1 中存在的 ID:Y.3 有一个数值,然后是名称 ID,即“1 John Smith”或“20 Sam Smith”将是字符串。

我有一个问题,在某些情况下,ID 将保持为“1 John Smith”,但在 X.1 : Y.3 中,编号可能会在 "John Smith" 之前发生变化,例如它可能是“14 约翰·史密斯”。名字总是正确的,只是数字有时会混淆。

我有一个受此不匹配影响的 200 多个 ID 的列表 - 替换 X.1 中的值的最有效方法是什么:Y.3 以便它们与列 ID 中的正确 ID 匹配?

我不知道“14 John Smith”出现在哪一列,它可能是 X.1、Y.2 或 Y.3,具体取决于行。

我可以在 dplyr 代码行中使用替换函数,或者对每个 200 多个 ID 和每个受影响的列使用 gsub,但这似乎效率很低。有没有比重复以下 x 次更快的方法?

df%>%mutate(X.1=replace(X.1, grepl('John Smith', X.1), "1 John Smith"))%>%as.data.frame()

不确定您是否设置了 dplyr 和管道,但我认为这是一个可以满足您需要的 plyr 解决方案。鉴于此示例数据集:

> df
  EVENT           ID  GROUP YEAR            X.1           X.2           X.3            Y.1           Y.2           Y.3
1     1 1 John Smith GROUP1 2015  19 John Smith 11 Adam Smith   9 Sam Smith 5 George Smith 13 Mike Smith 12 Luke Smith
2     2 2 John Smith GROUP1 2015 1 George Smith  9 Luke Smith 19 Adam Smith    7 Sam Smith 17 Mike Smith 11 John Smith
3     3 3 John Smith GROUP1 2015 5 George Smith 18 John Smith  12 Sam Smith   6 Luke Smith  2 Mike Smith  4 Adam Smith

adply 函数逐行执行,并将任何匹配的 X:Y 列值替换为 ID 列中的值:

library(plyr)

adply(df, .margins = 1, function(x) {
  idcol <- as.character(x$ID)
  searchname <- trimws(gsub('[[:digit:]]+', "", idcol))
  sapply(x[5:10], function(y) {
    ifelse(grepl(searchname, y), idcol, as.character(y))
  })
})

输出:

  EVENT           ID  GROUP YEAR            X.1           X.2           X.3            Y.1           Y.2           Y.3
1     1 1 John Smith GROUP1 2015   1 John Smith 11 Adam Smith   9 Sam Smith 5 George Smith 13 Mike Smith 12 Luke Smith
2     2 2 John Smith GROUP1 2015 1 George Smith  9 Luke Smith 19 Adam Smith    7 Sam Smith 17 Mike Smith  2 John Smith
3     3 3 John Smith GROUP1 2015 5 George Smith  3 John Smith  12 Sam Smith   6 Luke Smith  2 Mike Smith  4 Adam Smith

数据:

names <- c("EVENT","ID",'GROUP','YEAR', paste(rep(c("X.", "Y."), each = 3), 1:3, sep = ""))
first <- c("John", "Sam", "Adam", "Mike", "Luke", "George")
set.seed(2017)
randvals <- t(sapply(1:3, function(x) paste(sample(1:20, size = 6), 
      paste(sample(first, replace = FALSE, size = 6), "Smith"))))
df <- cbind(data.frame(1:3, paste(1:3, "John Smith"), "GROUP1", 2015), randvals)
names(df) <- names   

我认为最有效的方法是构建一个循环。原因是您将不得不重复该功能来替换 ID 列表中每个名称的名称。使用循环,您可以自动执行此操作。

我先做一些假设:

  1. ID列表可以读作字符向量
  2. 您的 ID 列表或 data.frame 中没有任何拼写错误,包括 名称中的不同大小写字母。
  3. 您的 ID 列表中没有号码。如果它确实包含数字,则必须使用 gsub 将其删除。
  4. 该示例可以与具有相同结构的 data.frame (DF) 一起使用 你提出你的问题。

>

ID <- c("John Smith", "Adam Smith", "George Smith")

for(i in 1:length(ID)) { 
    DF[, 5:10][grep(ID[i], DF[, 5:10])] <- ID[i]
}

每一轮这个循环将:

  • 确定 X.1:Y.3 列(您问题中的第 5 至 10 列)中名称 "i" 的位置出现。

  • 然后,它会将所有这些值更改为 ID 向量的 "i" 位置中的值。

  • 因此,第一次迭代将执行:1) 搜索名称 "John Smith" 在数据框中出现的每个位置。 2) 用 "John Smith".

  • 替换所有那些“#John Smith”

注意:如果只是想删除数字,可以使用gsub来代替。考虑到您可能还想删除数字和名称之间的第一个 space。一种方法是使用 gsub 和正则表达式:

DF[, 5:10] <- gsub("[0-9]+ ", "", DF[, 5:10])

有时它有助于临时重塑数据。这样我们就可以对所有 X 和 Y 值进行操作而无需对其进行迭代。

library(stringr)
library(tidyr)

## some data to work with
exd <- read.csv(text = "EVENT,ID,GROUP,YEAR,X.1,X.2,X.3,Y.1,Y.2,Y.3
1,1 John Smith,GROUP1,2015,19 John Smith,11 Adam Smith,9 Sam Smith,5 George Smith,13 Mike Smith,12 Luke Smith
2,2 John Smith,GROUP1,2015,1 George Smith,9 Luke Smith,19 Adam Smith,7 Sam Smith,17 Mike Smith,11 John Smith
3,3 John Smith,GROUP1,2015,5 George Smith,18 John Smith,12 Sam Smith,6 Luke Smith,2 Mike Smith,4 Adam Smith",
stringsAsFactors = FALSE)

## re-arrange to put X and Y columns into a single column
exd <- gather(exd, key = "var", value = "value", X.1, X.2, X.3, Y.1, Y.2, Y.3)

## find the X and Y values that contain the ID name
matches <- str_detect(exd$value, str_replace_all(exd$ID, "^\d+ *", ""))

## replace X and Y values with the matching ID
exd[matches, "value"] <- exd$ID[matches]

## put it back in the original shape
exd <- spread(exd, key = "var", value = value)

exd
##   EVENT           ID  GROUP YEAR            X.1           X.2           X.3            Y.1           Y.2           Y.3
## 1     1 1 John Smith GROUP1 2015   1 John Smith 11 Adam Smith   9 Sam Smith 5 George Smith 13 Mike Smith 12 Luke Smith
## 2     2 2 John Smith GROUP1 2015 1 George Smith  9 Luke Smith 19 Adam Smith    7 Sam Smith 17 Mike Smith  2 John Smith
## 3     3 3 John Smith GROUP1 2015 5 George Smith  3 John Smith  12 Sam Smith   6 Luke Smith  2 Mike Smith  4 Adam Smith