根据单独的列表替换多个字符串/值

问题描述 投票:1回答:3

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

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中的值经常更改。在X.1:Y.3中出现的ID和ID具有数值,然后名称ID,即“1 John Smith”或“20 Sam Smith”将是字符串。

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

我有一个受这种不匹配影响的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()
r replace dplyr gsub
3个回答
3
投票

有时,暂时重塑数据会有所帮助。这样我们就可以对所有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

3
投票

不确定你是否设置了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   

2
投票

我认为实现这一目标的最有效方法是建立一个循环。原因是您必须重复该功能才能替换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]
}

每轮,这个循环将:

  • 确定名称“i”出现的列X.1:Y.3(问题中的第5到10列)中的位置。
  • 然后,它将所有这些值更改为ID向量的“i”位置中的值。
  • 因此,第一次迭代将执行:1)搜索名称“John Smith”出现在数据框中的每个位置。 2)用“John Smith”替换所有那些“#John Smith”。

注意:如果您只想删除数字,可以使用gsub替换它们。考虑到你可能想要删除数字和名称之间的第一个空格。一种方法是使用gsub和正则表达式:

DF[, 5:10] <- gsub("[0-9]+ ", "", DF[, 5:10])
© www.soinside.com 2019 - 2024. All rights reserved.