【问题标题】:Compare every 2 rows and show mismatches in R比较每 2 行并显示 R 中的不匹配
【发布时间】:2014-09-05 06:29:28
【问题描述】:

我进行了很多搜索并自己尝试过,但找不到针对此特定问题的解决方案。

对于每 2 行('key' 很常见),我必须在每一列中找到不匹配的地方,并以如下所示的有条理的方式突出显示它们。

输出应采用以下格式:

COLUMN_NAME is not matching for records below:
PRINT COMPLETE RECORDS
...
COLUMN_NAME is not matching for records below:
PRINT COMPLETE RECORDS
...
COLUMN_NAME is not matching for records below:
PRINT COMPLETE RECORDS
...

输入数据(它是一个数据框):

key V1  V2  V3  V4  V5
a1  1   2   3   4   5
a1  1   3   9   4   5
a5  2   1   4   7   5
a5  2   1   4   7   6
a6  7   6   8   9   6
a6  7   6   3   9   6
a9  7   6   8   9   4
a9  7   6   8   9   3

输出

V2 is not matching for records below:
key V1  V2  V3  V4  V5
a1  1   2   3   4   5
a1  1   3   9   4   5


V3 is not matching for records below:
key V1  V2  V3  V4  V5
a1  1   2   3   4   5
a1  1   3   9   4   5
a6  7   6   8   9   6
a6  7   6   3   9   6


V5 is not matching for records below:
key V1  V2  V3  V4  V5
a5  2   1   4   7   5
a5  2   1   4   7   6
a9  7   6   8   9   4
a9  7   6   8   9   3

我是 R 的初学者,所以请善待 :)

【问题讨论】:

    标签: r dataframe compare match apply


    【解决方案1】:

    首先将您的数据框按key

    dfs <- split(df, df$key)  # presuming your data frame is named `df`
    

    现在编写一个函数,获取一个数据框并比较第一行和第二行(为简单起见,我们不会检查数据框是否实际上有 2 行 - 这只是理所当然的):

    chk <- function(x) sapply(x, function(u) u[1]==u[2])
    

    现在将该函数应用于split'ed 数据:

    matches <- sapply(dfs,chk)
    ## so `foo` is a matrix showing, for each variable and each ID, whether there is 
    ## a match or not
    apply(matches, 1, function(x) colnames(matches)[which(!x)])
    ## and this one takes each row in `foo` and extracts the column name (i.e. key)
    ## for every TRUE-valued cell.  the result is a list - note that some of the
    ## elements will be empty
    

    最后一行输出每个变量的不匹配对的名称(key 列)。

    现在是最后一步:

    mm_keys <- apply(matches, 1, function(x) colnames(matches)[which(!x)])
    # mm_keys stands for mismatching keys
    lapply(mm_keys, function(x) subset(df, key %in% x))
    # this one, called `mm_lines` below, takes each element from mm_keys
    # .. and extracts (via `subset`) the corresponding lines from the original data frame
    

    好的,您已经拥有了您想要的所有信息,但没有以很好的方式格式化。您也可以轻松做到这一点。

    mm_lines <- lapply(mm_keys, function(x) subset(df, key %in% x))
    mm_lines <- mm_lines[sapply(mm_lines, nrow)>0]  
    # leave out variables where there is no mismatch
    # for understanding this, try what `sapply(mm_lines, nrow)` does
    # and add labels the way you want:
    names(mm_lines) <- paste(names(mm_lines), "IS NOT MATCHING FOR RECORDS BELOW:")
    

    现在输出:

    print(boo)
    #$`V2 IS NOT MATCHING FOR RECORDS BELOW:`
    #  key V1 V2 V3 V4 V5
    #1  a1  1  2  3  4  5
    #2  a1  1  3  9  4  5
    #
    #$`V3 IS NOT MATCHING FOR RECORDS BELOW:`
    #  key V1 V2 V3 V4 V5
    #1  a1  1  2  3  4  5
    #2  a1  1  3  9  4  5
    #5  a6  7  6  8  9  6
    #6  a6  7  6  3  9  6
    #
    #$`V5 IS NOT MATCHING FOR RECORDS BELOW:`
    #  key V1 V2 V3 V4 V5
    #3  a5  2  1  4  7  5
    #4  a5  2  1  4  7  6
    #7  a9  7  6  8  9  4
    #8  a9  7  6  8  9  3
    

    [编辑]

    既然你要求它,这里有一些东西可以在一行上完成,看起来有点像魔术师:

    boo <- (function(x) x[sapply(x, nrow)>0])(lapply(lapply(df, function(x) tapply(x, df$key, function(x) x[1]!=x[2])), function(x) subset(df, key %in% names(which(x)))))
    

    并以您想要的方式将其写入文本文件(“out.txt”):

    sink("out.txt")
    for(iii in seq_along(boo)){
      cat(names(boo)[iii], "IS NOT MATCHING FOR THE RECORDS BELOW:\n")
      print(boo[[iii]])
      cat("\n")
      }
    sink(NULL)
    

    【讨论】:

    • 这就是他们所说的魔法? :) 杰出的。非常感谢您的回答。想请求几件事,1.请组织您的代码以供其他人理解他们是否愿意 2.请告诉我如何将 boo 的输出扔到文本文件或如果可能的话到 Excel。 XLSX 包正在将其转换为数据框并弄乱所需输出的格式。
    • 感谢 Akrun,我设法将 boo 的输出打印到文件中。但它是“Word Wrapped”的排序,所以列在下面输入。希望他们能出现在一条直线上。我有 113 列要处理,每行显示 15 列。
    • 好吧,你抓住了我。编码的第一条规则是:永远不要使用愚蠢的变量名称,如“blah”和“boo”。即使你将成为唯一使用它的人——因为在 4 个月内你不会记得你今天在想什么。第二条规则:可理解的 cmets!所以我添加了一些 cmets(希望它更容易理解)并更改了 var 名称。但我无法抗拒并添加了一个真正的魔法 - 删除括号,它不会起作用。这证明它是真正的魔法。 :)
    • 再次精彩 :) 谢谢!但最后一个怪癖是它是一种文字包装。就像我提到的,我处理 113 列,每行只显示 15 列。你能做更多的魔术并在一行上显示所有列吗?非常感谢! :)
    • 参见options()$width - 通常设置为 options(width=10000) - 或者如果您的行长于 10000 行,则当前方法行不通。
    【解决方案2】:

    你可以试试by

    res <- c(with(stack(by(df[,-1], df[,1],
             FUN=function(x)names(x)[ x[1,]!=x[2,]])), 
               by(ind, values, FUN=function(x) df[df[,1] %in% x,])))
    
     names(res) <- paste(names(res), "is not matching for records below")
     res
    #$`V2 is not matching for records below`
    #  key V1 V2 V3 V4 V5
    #1  a1  1  2  3  4  5
    #2  a1  1  3  9  4  5
    
    #$`V3 is not matching for records below`
    #  key V1 V2 V3 V4 V5
    #1  a1  1  2  3  4  5
    #2  a1  1  3  9  4  5
    #5  a6  7  6  8  9  6
    #6  a6  7  6  3  9  6
    
    #$`V5 is not matching for records below`
    # key V1 V2 V3 V4 V5
    #3  a5  2  1  4  7  5
    #4  a5  2  1  4  7  6
    #7  a9  7  6  8  9  4
    #8  a9  7  6  8  9  3
    

    【讨论】:

    • 由于输出在数据框中,它与我想要的格式相混淆。你能不能把它变成我写的格式。并感谢您的回答:)
    • @Pragith 这里,输出在列表中。如果要将输出保存在 .txt 文件中,可以使用 capture.output。即capture.output(res, file="file1.txt")如果你想要的输出是一个数据框,不清楚你想如何放置这些labels
    • Akrun,输出不准确,因为我选择的最佳答案。您的代码显示所有列不匹配。我在现实中处理 113 列和 10 行,它显示所有不匹配。
    • @Pragith 没关系。在您展示的多列示例中,它可以正常工作。因此,我不确定您的原始数据集的行为是否不同。每个键都有超过 2 条记录吗?也许(10 行)?
    猜你喜欢
    • 2020-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-21
    相关资源
    最近更新 更多