【问题标题】:R: Find matching string then copying rowR:查找匹配的字符串然后复制行
【发布时间】:2016-03-04 03:12:44
【问题描述】:

我有一个多步骤问题。第一步:将一个字符串 (df1) 中的文本从一列匹配到另一列范围 (df2)。没有匹配的列的顺序,并且匹配可以发生在范围内的任何位置。找到匹配后,将 df2 行匹配复制到 df1。最后,对整列重复。

df1= structure(list(Assay = c("ATG_AR_trans_up","NVS_PXR_cis","BSK_VCAM1_up"), p.value = c(0.01,0.05,0.0001)), .Names = c("Assay", "p.value"),row.names = c(NA, 3L), class = "data.frame") 

df1
Assay               p.value
ATG_AR_trans_up       0.01
NVS_hPXR              0.065
BSK_VCAM1_up          0.001

df2=structure(list(GeneID = c("AR", "VACM1", "TR", "ER", "PXR"), Assay1= c("ATG_ARE_cis", "BSK_hEDG_VCAM1", "NVS_TR_tran", "ATG_ER_UP", "NVS_PXRE_UP"), Assay2= c("ATG_AR_trans_up", "BSK_BE3K_VCAM1", "NA", "ATG_ERE_cis", "ATG_PXRE_cis"), Assay3= c("NVS_AR_trans", "BSK_VCAM1_UP", "NA", "NVS_ERa_CIS", "NVS_PXR_cis"), Assay4= c("Tox21_AR_ARE","NA", "NA", "Tox21_ERaERb_lig", "NA")),  .Names = c("GeneID", "Assay1", "Assay2", "Assay3", "Assay4"),row.names = c(NA, 5L), class = "data.frame")

df2  
    GeneID             Assay1            Assay 2           Assay3
    AR                 ATG_ARE_cis       NVS_hAR          ATG_AR_trans_up
    VACM1              BSK_hEGF_CAM1     BSK_VCAM1_up      BSK_VCAM1_down
    TR                 NVS_TR_tran       NA                  NA
    ER                 ATG_ER_UP         ATG_ERE_cis     NVS_ERa_CIS     
    PXR                ATG_PXR_down      ATG_PXRE_cis      NVS_hPXR

基本上变成

df
Assay           p.value   GeneID    Assay1       Assay2      Assay3
ATG_AR_trans_up  0.01      AR      ATG_ARE_cis  NVS_hAR      ATG_AR_trans_up
NVS_hPXR         0.065    PXR    ATG_PXR_down   ATG_PXRE_cis NVS_hPXR
BSK_VCAM1_up     0.001    VCAM1  BSK_hEGF_CAM1  BSK_VCAM1_up BSK_VCAM1_down

为简洁起见,我大大缩短了 df,但它大约需要 88 个 Assays 和 4,000 行才能进行一场比赛(大约有 30 行)。所以我最初的直觉是循环,但有人告诉我grep 可能是一个有用的包(即使它不适用于 R 3.2.2)。任何帮助都将不胜感激。

【问题讨论】:

  • 你的例子太小了。尝试添加更多匹配项以显示所需的输出。
  • 好的。我会尽力。让我知道是否可以接受修改
  • 您可以编辑问题。
  • 你也应该使用dput来存放你的例子,这样人们就不必手动复制你的dfs了
  • 我添加了数据框。希望这会让它更容易理解......虽然我认为我让它更令人困惑。

标签: r formatting match data-management


【解决方案1】:

由于 OP 对 grep 解决方案感兴趣,另一种方法是,

asDF2 <- apply(df2, 1, function(r) do.call(paste, as.list(r)))

do.call(rbind, lapply(1:nrow(df1), 
                  function(i){
                    matchIX <- grepl(df1$Assay[i], asDF2, ignore.case=T)
                    if(any(matchIX))
                      cbind(df1[i, ], df2[matchIX, ])
                  }))

第一行创建一个字符向量,其连接的行分析名称为df2。第二行循环通过df1 并使用greplasDF2 中找到匹配项

或者等价的,

do.call(rbind, lapply(1:nrow(df1), 
                  function(i){
                    matchIX <- grepl(df1$Assay[i], 
                                     data.frame(t(df2), stringsAsFactors=F), 
                                     ignore.case=T)
                    if(any(matchIX))
                      cbind(df1[i, ], df2[matchIX, ])
                    } ))

请注意,上述变体,可以匹配df2df1 中的多行。

注意 为了测试,我将新行添加到原始数据帧中

df1 <- rbind(df1, data.frame(Assay="NoMatch", p.value=.2))
df2 <- rbind(df2,
         data.frame(GeneID="My", Assay1="NVS_PXR_cis", Assay2="NA", Assay3="NA", Assay4="NA"))

【讨论】:

  • 你太棒了。它工作了 50%。对于两组不同的数据帧,我基本上必须运行此代码两次。第一个终于起作用了,但是当我运行第二个时,我得到了错误:“data.frame 中的错误(...,check.names = FALSE):参数意味着不同的行数:1, 0 7 stop(gettextf( "参数暗示不同的行数:%s", paste(unique(nrows), collapse = ", ")), domain = NA)
  • 6 data.frame(..., check.names = FALSE) 5 cbind(deparse.level, ...) 4 cbind(p.MDRAtT[i, ], AmigoDEV[grepl(p .MDRatT$Assay[i], data.frame(t(AmigoDEV), stringsAsFactors = F), ignore.case = T), ]) 3 FUN(X[[i]], ...) 2 lapply(1: nrow(p.MDRatT), function(i) cbind(p.MDRatT[i, ], AmigoDEV[grepl(p.MDRatT$Assay[i], data.frame(t(AmigoDEV), stringsAsFactors = F), 忽略。 case = T), ])) 1 do.call(rbind, lapply(1:nrow(p.MDRatT), function(i) cbind(p.MDRatT[i, ], AmigoDEV[grepl(p.MDRatT$Assay[ i], data.frame(t(AmigoDEV), stringsAsFactors = F), ignore.case = T), ]))) " 有什么想法吗?
  • 我猜是因为df1 中有一些Assaydf2 中没有匹配项。
  • 我更新了我的帖子以应对不匹配的情况。我认为这就是错误消息对 0 (... differing number of rows: 1, 0 ) 所说的内容。这也可能是由于grep 由于某种原因不匹配(输入错误?)。你能举一个更好的例子来测试吗?
  • 成功了。太感谢了!我已经和这个怪物斗争了好几个星期了。非常感谢!
【解决方案2】:

这可以通过重塑轻松完成。我把所有的化验都写成大写,因为那会弄乱匹配。

library(dplyr)
library(tidyr)
library(stringi)

df2_ID = df %>% mutate(new_ID = 1:n() )

result = 
  df2_ID %>%
  select(new_ID, Assay1:Assay85) %>%
  gather(assay_number, Assay, Assay1:Assay85) %>%
  mutate(Assay = 
           Assay %>% 
           iconv(to = "ASCII") %>%
           stri_trans_toupper) %>%
  inner_join(df1 %>%
               mutate(Assay = 
                        Assay %>% 
                        iconv(to = "ASCII") %>%
                        stri_trans_toupper)) %>%
  inner_join(df2_ID)

【讨论】:

  • 太棒了。我如何/在哪里保存它?
  • 我进行了编辑,以便将答案保存在名为 result 的数据框中。
  • 谢谢!不过还是有问题。结果中我只得到了 46 个变量中的 19 个。另外,我收到此警告消息:“警告消息:度量变量的属性不相同;它们将被删除”有什么想法吗?
  • 这里发生的事情(如我的回答)是应该匹配的元素有一些微妙的地方导致它们不匹配。它可能是大小写问题、您不知道存在的前导或尾随空格或不同的数据类型(例如,一些存储为字符,另一些存储为因子)。但由于某种原因,您认为应该匹配的单元格实际上并不匹配。要检查这一点,请查找未正确提取的匹配项,并检查 df1 和 df2 中的相关条目是否与 df1[i,j]==df2[k,l] 真正相同
  • 同意前面的。其他可能导致问题的事情是“隐形”字符。转换为 ascii 有时会有所帮助。
【解决方案3】:

由于您是 R 新手,我认为您是对的,最直观的方法是使用 for 循环。这不是最简洁或最有效的方法,但应该清楚发生了什么。

# Creating example data
df1 <- as.data.frame(matrix(data=c("aa", "bb", "ee", .9, .5, .7), nrow=3))
names(df1) <- c("assay", "p")

df2 <- as.data.frame(matrix(data=c("G1", "G2", "aa", "dd", "bb", "ee", "cc", "ff"), nrow=2))
names(df2) <- c("GeneID", "assay1", "assay2", "assay3")

# Building a dataframe to store output

df3 <- as.data.frame(matrix(data=NA, nrow=dim(df1)[1], ncol=dim(df2)[2]))
names(df3) <- names(df2)

# Populating dataframe with output
for(i in 1:dim(df1)[1]){
  index <- which(df2==as.character(df1$assay[i]), arr.ind = TRUE)[1]
  for(j in 1:dim(df3)[2]){
  df3[i,j] <- as.character(df2[index,j])
  }
}

df <- cbind(df1, df3)

【讨论】:

  • 谢谢!抱歉,我花了一点时间来运行代码,但我明白了!
  • 等待。在我看来它正在工作,但它没有找到所有的匹配项。任何想法可能是什么?
  • 这取决于数据框的完整结构。您可以尝试将 for 循环中的 which 语句替换为 which((as.character(df2)==as.character(df1$assay[i]), arr.ind = TRUE)[1]
  • 我收到语法错误,无法弄清楚如何解决它:“错误:意外','在:”for(i in 1:dim(df1)[1]) { index
【解决方案4】:

在用户澄清后编辑

我刚刚创建了一个三重 for 循环来检查您的值。基本上它所做的就是寻找匹配项。它通过循环遍历所有列和该列中的所有值来做到这一点。

但是我的代码还不完美(也是 R 的初学者),我只是想发布它,以便我们可以一起解决问题:)。

所以我首先将您的数据转换为 data.frame。之后,我创建了一个空输出,稍后我会根据找到的匹配项进行填充。

此方法的改进在于,使用此解决方案,函数 append 还将附加列名,这将导致多个无用的列名。

df3 <- as.data.frame(df1)
df4 <- as.data.frame(df2)
output <- data.frame()



for(j in 1:nrow(df3)) {
    match <- FALSE
    for(i in 2:(ncol(df4))) {
        for(p in 1:nrow(df4)) {
            if((df3[j, 1] == df4[p, i]) && (match == FALSE)) {
                output <- append(output, c(df3[j, ], df4[j, ]))
                match <- TRUE
            }
        }
    }
}

【讨论】:

  • 谢谢,但我也需要添加匹配条件。可能来自我问这个问题的糟糕方式。我尝试添加数据框以使其更容易。这个问题的框架仍然很差吗?我想我让事情变得更糟了。
  • @ephackett,我认为您的编辑很清楚;绝对没有让事情变得更糟。
  • 感谢您添加信息!问题中不清楚的部分是行找到匹配项的条件是什么。在您的前两个输出行中,我注意到“Assay”和“Assay3”中的匹配项。但是在您的第三行中,我注意到“Assay”和“Assay2”中的匹配项。那么它是否应该在第二个数据帧的三个“分析”中的任何一个中找到匹配项?
  • 是的。没有特定的列匹配顺序。我只需要在任何分析列中找到匹配项(实际上有 88 个,但为了便于举例,我认为 3/4 就足够了)。
  • 那是你应该提到的定义哈哈。我正在考虑制作一个 for 循环,但如果你有 88 列,那么制作 88 个“if else”语句将是一种非常糟糕的编码方式。
【解决方案5】:

假设,您没有与 df1 中的条目对应的任何重复条目。以下是您的问题的解决方案:

assay <-as.matrix(df1[,1])
m1 <- as.numeric(sapply(assay, function(x){grep(x,df2[,2], ignore.case = T)}, simplify = FALSE))
m2 <- as.numeric(sapply(assay, function(x){grep(x,df2[,3], ignore.case = T)}, simplify = FALSE))
m3 <- as.numeric(sapply(assay, function(x){grep(x,df2[,4], ignore.case = T)}, simplify = FALSE))
m4 <- as.numeric(sapply(assay, function(x){grep(x,df2[,5], ignore.case = T)}, simplify = FALSE))

m1[is.na(m1)] <- 0
m2[is.na(m2)] <- 0
m3[is.na(m3)] <- 0
m4[is.na(m4)] <- 0

m0 <- (m1+m2+m3+m4)
df <- NULL
for(i in 1:nrow(df1){
  df3 = cbind(df1[i,],df2[m0[i],])
  df = rbind(df,df3)
}

编辑:概括

由于您有超过 80 行,您可以将其概括为:

assay <-as.matrix(df1[,1])

# Storing Assay column in a list
m <- vector('list',ncol(df2[, 2:ncol(df2)]))
for(i in 1:length(m)){
  m[[i]] <- as.numeric(sapply(assay, function(x){grep(x,df2[,(i+1)], ignore.case = T)}, simplify = FALSE))

}
# Getting row subscript for df2
m1 <- as.data.frame(m)
m1[is.na(m1)] <- 0
m2 <- rowSums(m1)

df <- NULL
for(i in 1:nrow(df1)){
  df3 = cbind(df1[i,],df2[m2[i],])
  df = rbind(df,df3)
}

【讨论】:

  • 非常感谢您的回答!不幸的是,我得到了错误:
  • " data.frame(..., check.names = FALSE) 中的错误:参数暗示不同的行数:1, 0 4 stop(gettextf("参数暗示不同的行数:% s", paste(unique(nrows), collapse = ", ")), domain = NA) 3 data.frame(..., check.names = FALSE) 2 cbind(deparse.level, ...) 1 cbind (p.MDRabT[i, ], AmigoDEV[m0[i], ])" df1 在工作表中被标记为 p.MDRabT 而 df2 是 AmigoDEV。
  • 它在您的示例数据集上运行良好。让我概括一下。
  • 谢谢!感谢您的额外帮助!
  • 新的工作没有错误,但它没有找到所有的化验(数据框中缺少一些)。我在列出的其他一些代码答案中遇到了这个问题。唯一成功解决这个问题的是 user2282381 的回答
猜你喜欢
  • 1970-01-01
  • 2016-04-28
  • 2014-10-04
  • 1970-01-01
  • 1970-01-01
  • 2015-05-20
  • 2013-10-23
  • 2021-02-22
  • 1970-01-01
相关资源
最近更新 更多