【问题标题】:Replace multiple strings/values based on separate list根据单独的列表替换多个字符串/值
【发布时间】:2018-05-30 03:20:58
【问题描述】:

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

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()

【问题讨论】:

    标签: r replace dplyr gsub


    【解决方案1】:

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

      在每一轮中,这个循环都会:

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

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

      • 因此,第一次迭代将执行以下操作: 1) 搜索名称“John Smith”出现在数据框中的每个位置。 2) 将所有“# John Smith”替换为“John Smith”。

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

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

      【讨论】:

        【解决方案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
        

        【讨论】:

        • 谢谢,这正是我所追求的,它将ID值与框架中的正确数字相匹配;也许在我的原始示例中不清楚,我应该显示 2-3 行,但 '1 John Smith' 是确切的字符串,它不是滚动计数,John smith 总是将 '1' 与他相关联作为他的ID - 此方法以快速简单的方式正确识别和匹配配对 - 适用于大数据量。
        猜你喜欢
        • 2019-08-04
        • 2020-10-16
        • 2015-06-27
        • 1970-01-01
        • 2019-11-08
        • 2021-03-28
        • 1970-01-01
        • 2019-06-22
        • 1970-01-01
        相关资源
        最近更新 更多