【问题标题】:Replace a character string based on a separate list/dataframe R根据单独的列表/数据框 R 替换字符串
【发布时间】:2019-08-04 01:16:16
【问题描述】:

我正在尝试做一些我认为很简单但让我难过的事情。

假设我有以下数据框:

id <- c("bob_geldof", "billy_bragg", "melvin_smith")
code <- c("blah", "di", "blink")
df <- as.data.frame(cbind(id,code))

> df
             id  code
1    bob_geldof  blah
2   billy_bragg    di
3  melvin_smith blink

还有一个这样的:

ID1 <- c("bob_geldof", "melvin_smith")
ID2 <- c("the_builder", "kelvin")
alternates <- as.data.frame(cbind(ID1, ID2))

> alternates
            ID1         ID2
1    bob_geldof the_builder
2  melvin_smith      kelvin

如果df$id 中的字符串匹配alternates$ID1,我想用alternates$ID2 替换它。如果不匹配,我想保持原样。

最终的 df 应该是这样的

> df
               id   code
1 bob_the_builder   blah
2     billy_bragg     di
3   melvin_kelvin  blink

这显然是一个愚蠢的例子,我的真实数据集需要大量替换。

我添加了“代码”列来证明我正在使用数据框,而不仅仅是字符向量。

我一直在使用 gsub 单独替换它们,但这很耗时,而且列表不断变化。

我查看了 str_replace,但您似乎只能指定一个替换值。

任何帮助将不胜感激。

干杯!

编辑:并非所有 id 都包含下划线,我需要保留匹配的位。例如。 bob_geldolf 成为 bob_the_builder。

编辑2(!):感谢大家的建议。我通过合并数据框(以便有不需要进行更改的 NA)并使用 ifelse 语句创建新 ID 来解决这个问题。这有点笨拙,但很有效!

【问题讨论】:

    标签: r replace character tidyverse gsub


    【解决方案1】:

    创建dataframes 时使用stringsAsFactors = FALSE 以免处理因素。然后,如果行是有序的,只需应用:

    df <- as.data.frame(cbind(id,code),stringsAsFactors = FALSE)
    alternates <- as.data.frame(cbind(ID1, ID2),stringsAsFactors = FALSE)
    
    df$id[c(TRUE,FALSE)]=paste(gsub("(.*)(_.*)","\\1",df$id[c(TRUE,FALSE)]),
                             alternates$ID2,sep="_")
    
    > df
                   id  code
    1 bob_the_builder  blah
    2     billy_bragg    di
    3   melvin_kelvin blink
    

    如果它们是无序的,我们可以使用dlyr

    df%>%rowwise()%>%mutate(id=if_else(length(which(alternates$ID1==id))>0,
                                      paste(gsub("(.*)(_.*)","\\1",id),
                                            alternates$ID2[which(alternates$ID1==id)],sep="_"),
                                      id))
    # A tibble: 3 x 2
      id              code 
      <chr>           <chr>
    1 bob_the_builder blah 
    2 billy_bragg     di   
    3 melvin_kelvin   blink
    

    我们使用与以前相同的逻辑。在这里,我们逐行检查df。如果它的id 匹配任何alternatives$ID1(由length() 检查),我们会更新它。

    【讨论】:

    • 谢谢!我只是想弄清楚 gsub 部分发生了什么。这部分是做什么的?: "(.*)(_.*)","\\1" 另外,您提到了要排序的行。我的真实数据框没有任何特定的顺序并且包含数千行,而“备用”数据框仅包含大约 100 行。这些应该如何排序?
    • 您好,在gsub 中,我们告诉函数寻找两种模式:(.*) 抓取每个字符,(_.*) 抓取_ 之后的所有字符。然后将其替换为\\1,这意味着我们匹配的第一个模式(这样我们丢弃了第二部分)。然后通过_ 粘贴ID2 值。希望现在更清楚了!
    • @SDes 让我知道新解决方案是否适合您 :)
    • 不清楚带下划线的 ID 值的格式是否在所有数据中保持一致?这可能是一个可靠的假设,也可能不是一个可靠的假设?
    • @Soren,你是对的,有些 ID 没有下划线!
    【解决方案2】:

    以下解决方案使用 base-R 并进行了一些简化。第 1 步:使用左连接将主“df”和“备用”df 合并在一起。第 2 步:检查 ID2 值在哪里没有丢失 (NA),然后将这些值分配给“id”。这将保留您的原始 ID(如果可用);并在匹配 ID 可用的情况下将其替换为 ID2

    解决办法:

    combined <- merge(x=df,y=alternates,by.x="id",by.y="ID1",all.x=T)
    combined$id[!is.na(combined$ID2)] <- combined$ID2[!is.na(combined$ID2)]
    

    使用完整的原始数据框定义(使用 stringsAsFactors=F):

    id <- c("bob_geldof", "billy_bragg", "melvin_smith")
    code <- c("blah", "di", "blink")
    df <- as.data.frame(cbind(id,code),stringsAsFactors = F)
    
    ID1 <- c("bob_geldof", "melvin_smith")
    ID2 <- c("the_builder", "kelvin")
    alternates <- as.data.frame(cbind(ID1, ID2),stringsAsFactors = F)
    
    combined <- merge(x=df,y=alternates,by.x="id",by.y="ID1",all.x=T)
    combined$id[!is.na(combined$ID2)] <- combined$ID2[!is.na(combined$ID2)]
    

    结果:(下面的完整合并,您也可以为精简结果做combined[,c("id","code")])。这里,不匹配的“billy_bragg”被保留;其他的被替换为匹配的ID

    > combined
               id  code         ID2
    1 billy_bragg    di        <NA>
    2 the_builder  blah the_builder
    3      kelvin blink      kelvin
    

    【讨论】:

    • 您好,感谢您的关注!我只需要更换不匹配的位。所以 bob_geldof 变成了 bob_the_builder,而不仅仅是“the_builder”。这在普通的旧 gsub 中可以正常工作,但是我必须单独指定每个替换值。
    猜你喜欢
    • 2018-05-30
    • 2023-01-30
    • 2019-11-08
    • 2014-02-06
    • 2020-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-23
    相关资源
    最近更新 更多