我有一个类似于此的数据框:
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()
答案 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列表中每个名称的名称。使用循环,您可以自动执行此操作。
我先做一些假设:
&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])