检查具有数字和字符数据的 2 个数据帧之间差异的最有效方法?

Posted

技术标签:

【中文标题】检查具有数字和字符数据的 2 个数据帧之间差异的最有效方法?【英文标题】:Most efficient way to check difference between 2 dataframes with numeric & character data? 【发布时间】:2017-10-20 09:51:28 【问题描述】:

我在 R 中有两个数据框,每个数据框具有相同的列和数据类型。有些列是基于文本的,有些是数字,有些是日期。但是,相同的列在两个数据框中具有相同类型的数据。两者的唯一标识符也相同,即主键匹配。

现在,我想创建第三个数据框,它基本上为每个主键捕获,相应列的 DF1 和 DF2 中的值有什么区别。当要检查的列是字符时,我们可以简单地说 1 或 0 表示差异。当它是数字时,我们可以捕获差异量,或者可能只是 1 或 0。

在 R 中最有效的方法是什么?我不想逐行比较,因为它很慢。逐列比较会很好,但这似乎也需要太多的手动监督。理想情况下,寻找一些可以帮助我做到这一点的数据框级函数。

可重现和可编辑的示例:

Dataframe1:
ID    val1     date1     chrval1    val3
A1    400      3/4/2017  DR9912YS   -43
A2    230      3/4/2017  ER9F4YS    -43
A3    500      31/2/2015  FFR99S     -49

Dataframe2:
ID    val1     date1     chrval1    val3
A1    400      3/4/2017  DR9912YS   -43
A2    400      3/4/2017  DR9912YS   -43
A3    400      31/4/2017  DR9912YS   -43

Ideally this is what I am looking for:
Difference Dataframe:
ID    val1     date1     chrval1    val3
A1    0        0         True        0
A2    170      0         False       0
A3    -100     0/2/2     False       5

【问题讨论】:

有趣的问题。据我所知,通常很难对数据进行测试。函数identical 可能会有所帮助。看看here @DJJ 我看到了,但这基本上告诉我它们是否完全匹配。我需要更多细节。还有什么有用的吗?我愿意根据数据类型将我的数据帧分成两个或三个拆分.. 你想对因子变量做什么?此外,日期差异对我来说没有意义。 2017 年 1 月 1 日和 2017 年 1 月 2 日之间的差异是 0/1/0。 dfference1/2/2017 和 1/3/2017 也是 0/1/0。显然,这两个时期的长度是不同的。 【参考方案1】:

刚刚煮熟了一些东西。它不处理日期大小写。

我正在使用库 gtools 中的宏。我不确定这是否有必要,但我对此有所了解。

library(gtools)

桌子。 read.table 非常适合轻松复制数据。

aa <- read.table(header=TRUE,text="
ID    val1     date1     chrval1    val3
A1    400      3/4/2017  DR9912YS   -43
A2    230      3/4/2017  ER9F4YS    -43
A3    500      31/2/2015  FFR99S     -49")

bb <- read.table(header=TRUE,text="
ID    val1     date1     chrval1    val3
A1    400      3/4/2017  DR9912YS   -43
A2    400      3/4/2017  DR9912YS   -43
A3    400      31/4/2017  DR9912YS   -43")

这是一个执行以下操作的简单宏。如果fn1 产生错误,它将捕获并使用fn2。可能不是 最合适的方法。随时改进。

expect_error <- defmacro(fn1,fn2,expr=
    tryCatch(fn1(x,y),
             error=function(e) mytc(fn2(x,y))
))

它也有一个 this 函数。根据暴击使用fn1fn2。 例如crit=is.numeric。如果暴击为真,则使用fn1,否则使用fn2

condlapply <- function(lst, crit, fn1, fn2)
       lapply(lst, function(x) if(crit(x)) 
                                   fn1(x) else fn2(x))

       

一些简单的功能

myequal <-  function(x,y=1) 
    `==`(x,y)
    

mydiff <-  function(x,y)
    `-`(x,y)
    


res <- data.frame(sapply(Map(function(x,y) expect_error(mydiff,myequal),aa,bb),c))

这里懒得用 ID 了。

res$ID <- aa$ID


## res                         
##   ID val1 date1 chrval1 val3
## 1 A1    0     1       1    0
## 2 A2 -170     1       0    0
## 3 A3  100     0       0   -6

我们可以在一个函数中结束

check_df <- function(df1,df2)
### DD
    ## df1, df2 . data.frames
    res <- data.frame(sapply(Map(function(x,y) expect_error(mydiff,myequal),df1,df2),c))
    res$ID <- aa$ID
    res
    

【讨论】:

【参考方案2】:

在基础 R 中:

# merge the two dataframes
dfm <- merge(df1, df2, by = 'ID')

# create numeric vectors for the column-names ending with '.x' and '.y'
xvec <- grep('.x', names(dfm), fixed = TRUE)
yvec <- grep('.y', names(dfm), fixed = TRUE)
# determine which columns are not of the character class
non_char <- which(sapply(dfm, class) != 'character')

# create a new dataframe by binding the 'ID' column
# with the difference of the '.x' & '.y' columns
dfnew <- cbind.data.frame(ID = dfm$ID, 
                          dfm[, intersect(yvec, non_char)] - dfm[, intersect(xvec, non_char)], 
                          chrval1 = dfm$chrval1.x == dfm$chrval1.y)

# remove the '.y' from the column-names of the new dataframe
names(dfnew) <- gsub('.y','',names(dfnew),fixed=TRUE)

给出:

> dfnew
  ID val1    date1 val3 chrval1
1 A1    0   0 days    0    TRUE
2 A2  170   0 days    0   FALSE
3 A3 -100 733 days    6   FALSE

就内存效率和速度而言,data.table-package 可能是最佳选择。然后你可以这样做:

library(data.table)
setDT(df1)
setDT(df2)

df1[df2, on = 'ID', `:=` (val1 = i.val1 - x.val1, 
                          dat1 = as.numeric(i.date1) - as.numeric(x.date1), 
                          chrval1 = i.chrval1 == x.chrval1, 
                          val3 = i.val3 - x.val3)][, date1:= NULL][]

给出:

> df1
   ID val1      date1 chrval1 val3
1: A1    0 1970-01-01    TRUE    0
2: A2  170 1970-01-01   FALSE    0
3: A3 -100 1972-01-04   FALSE    6

使用的数据:

df1 <- structure(list(ID = c("A1", "A2", "A3"), 
                      val1 = c(400L, 230L, 500L), 
                      date1 = structure(c(17290, 17290, 16557), class = "Date"), 
                      chrval1 = c("DR9912YS", "ER9F4YS", "FFR99S"), 
                      val3 = c(-43L, -43L, -49L)), 
                 .Names = c("ID", "val1", "date1", "chrval1", "val3"), row.names = c(NA, -3L), class = "data.frame")
df2 <- structure(list(ID = c("A1", "A2", "A3"), 
                      val1 = c(400L, 400L, 400L), 
                      date1 = structure(c(17290, 17290, 17290), class = "Date"), 
                      chrval1 = c("DR9912YS", "DR9912YS", "DR9912YS"), 
                      val3 = c(-43L, -43L, -43L)), 
                 .Names = c("ID", "val1", "date1", "chrval1", "val3"), row.names = c(NA, -3L), class = "data.frame")

【讨论】:

以上是关于检查具有数字和字符数据的 2 个数据帧之间差异的最有效方法?的主要内容,如果未能解决你的问题,请参考以下文章

如何计算不同数据框的列之间的数值差异?

具有数百万条记录的 2 个数据帧之间的 Pyspark 交叉连接

值列表与数据集的as.list之间的差异

根据2个数据帧之间的部分字符串匹配删除元素:

通过在两个 Pandas 数据帧之间迭代来识别相似的值。

无法在 wav 文件中找到 2 个声音帧之间的差异