根据单独的列表

时间:2017-12-15 21:57:43

标签: r replace dplyr gsub

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

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”史密斯”。名字总是正确的,它只是有时混淆的数字。

我有一个受这种不匹配影响的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()

3 个答案:

答案 0 :(得分: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   

答案 1 :(得分: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

答案 2 :(得分:2)

我认为实现这一目标的最有效方法是建立一个循环。原因是您必须重复该功能才能替换ID列表中每个名称的名称。使用循环,您可以自动执行此操作。

我先做一些假设:

  1. ID列表可以读作字符向量
  2. 您在ID列表或data.frame中没有任何拼写错误,包括 名称中包含不同的小写和大写字母。
  3. 您的ID列表不包含这些数字。如果确实包含数字,则必须使用gsub擦除它们。
  4. 该示例可以使用具有相同结构的data.frame(DF) 你提出了问题。
  5. &GT;

    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列)中的位置,其中显示名称&#34; i&#34;

    • 然后,它会将所有这些值更改为&#34; i&#34; ID矢量的位置。

    • 所以,第一次迭代会做:1)搜索名称&#34; John Smith&#34;出现在数据框中。 2)替换所有那些&#34; #John Smith&#34;与约翰史密斯&#34;。

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

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