【问题标题】:Cross-check for duplicate values across dataframes交叉检查跨数据框的重复值
【发布时间】:2018-03-06 08:55:16
【问题描述】:

我正在尝试编写一个函数,该函数将数据帧列表和条件列表作为其参数,然后返回这些数据帧的列表,其中的列指示这些值在另一个数据帧中重复的行。

例如,我有三个数据框:

df1:

Name1 | Zip_code | Data
----- | -------- | ----
George|  123     |  abc
----- | -------- | ----
Marge |  456     |  def
----- | -------- | ----
Mike  |  789     |  foo

df2:

Name  |  data    | zip_code
----- | -------- | --------
Mike  | klm      | 789
----- | -------- | --------
George| xxx      | 123
----- | -------- | --------
Marge | yyy      | 456
----- | -------- | --------
Bob   | zzz      | 678

df3:

Data  |  Name    | zip_code
----- | -------- | --------
zzz   |  Bob     | 678
----- | -------- | --------
ggg   | Mike     | 789

假设我只关心重复的名称和邮政编码,我希望输出如下所示:

df1:

Name1 |  Zip_code | Data | row_df2 | row_df3
----- | --------- | ---- | ------- | -------
George| 123       | abc  | 2       | NA
----- | --------- | ---- | ------- | -------
Marge | 456       | def  | 3       | NA
----- | --------- | ---- | ------- | -------
Mike  | 789       | foo  | 1       | 2

df2:

Name  | data  | zip_code  | row_df3
----- | ----- | --------- | -------
Mike  | klm   | 789       | 2
----- | ----- | --------- | -------
George| xxx   | 123       | NA
----- | ----- | --------- | -------
Marge | yyy   | 456       | NA
----- | ----- | --------- | -------
Bob   | zzz   | 678       | 1

每个数据框之间的列名并不总是相同,例如我们可以在一个数据框中使用“Name”,在另一个数据框中使用“NameWhole”。此外,每个数据帧中可能有不同数量的列。我意识到每个数据帧要比较的数据顺序需要从左到右相同,否则列之间的内容无关紧要。因此,

df1 有:

名称 |邮编 |数据

df2 有:

数据 |姓名 |邮编

df3 有:

名称 |数据 |邮编

我目前的解决方案如下:

首先,初始化作为函数第一个参数的数据框列表:

dflist[[1]] <- df1
dflist[[2]] <- df2
dflist[[3]] <- df3

然后我们初始化标准列表,它是函数的第二个参数。由于我们对数据帧中常见的名称和邮政编码感兴趣,因此:

criterialist[[1]] <- c(1,2)
criterialist[[2]] <- c(1,3)
criterialist[[3]] <- c(2,3)

现在函数是:

cross_checker <- function(dflist, criterialist){

# Insert an index column indicating the row number to be returned:
for (i in 2:length(dflist)){
dflist[[i]]$index <- 1:nrow(dflist[[i]])
}

# Next we loop over the dataframes with two for-loops:
for (i in 1:length(dflist)-1){
  for (j in 2:length(dflist)){
  dflist[[i]][,ncol(dflist[[i]])+1] <- merge(dflist[[i]], dflist[[j]], by.x=criterialist[[i]], by.y=criterialist[[j]], all.x=TRUE)$index
}
}

因此,我在 df1 中只有一个新的索引列,有时我的 RStudio 只是打开了一个调试窗口。我不确定“合并”是否是解决这个问题的方法,但我还没有弄清楚“匹配”是如何工作的。

我想一种方法是用 for 循环蛮力它,但我认为这会很慢。

最终的想法是创建一个函数,该函数采用任意标准的任意数量的数据帧来检查重复记录并返回这些数据帧,并带有一个新列,该列指示记录在哪一行和哪个数据帧中重复.

编辑:抱歉,我的第一个问题。以下是表格的可重现代码:

name1 <- c("George","Marge","Mike")
zip1 <- c(123,456,789)
data1 <- c("abc","def","foo")
df1 <- data.frame(name1,zip1,data1,stringsAsFactors = F)

name2 <- c("Mike","George","Marge","Bob")
data2 <- c("klm","xxx","yyy","zzz")
zip2 <- c(789,123,456,678)
df2 <- data.frame(name2,data2,zip2,stringsAsFactors = F)

data3 <- c("zzz", "ggg")
name3 <- c("Bob","Mike")
zip3 <- c(678,789)
df3 <- data.frame(data3,name3,zip3,stringsAsFactors = F)

编辑 2:

我决定添加一个额外的数据框(所以现在有 4 个):

    name1 <- c("George","Marge","Mike")
    zip1 <- c(123,456,789)
    data1 <- c("abc","def","foo")
    df1 <- data.frame(name1,zip1,data1,stringsAsFactors = F)

    name2 <- c("Mike","George","Marge","Bob")
    data2 <- c("klm","xxx","yyy","zzz")
    zip2 <- c(789,123,456,678)
    df2 <- data.frame(name2,data2,zip2,stringsAsFactors = F)

    data3 <- c("zzz", "ggg")
    name3 <- c("Bob","Mike")
    zip3 <- c(678,789)
    df3 <- data.frame(data3,name3,zip3,stringsAsFactors = F)

    name4<-c("Marge", "George","Bob")
    zip4<-c(234,123,678)
    data4<-c("ask","bff","hhh")
    df4 <- data.frame(name4,zip4,data4,stringsAsFactors = F)

然后我决定尝试以下代码:

cross_checker2 <- function(dflist,criterialist){
  returnlist<-list()
looplen1 <- length(dflist)-1

 for(i in 1:looplen1){

    temp_df1 <- dflist[[i]]
    temp_crit1 <- criterialist[[i]]
    for(j in (i+1):length(dflist)){
     temp_df2 <- dflist[[j]]
 temp_crit2 <- criterialist[[j]]
   temp_df1 <- merge(temp_df1,temp_df2,by.x=temp_crit1,by.y=temp_crit2,all.x=TRUE)

    }

    returnlist[[length(returnlist)+1]]<-temp_df1
  }

我创建以下列表作为参数传递给函数:

deflista<-list()
deflista[[1]]<-df1
deflista[[2]]<-df2
deflista[[3]]<-df3
deflista[[4]]<-df4

crit1<-c(1,2)
crit2<-c(1,3)
crit3<-c(2,3)
crit4<-c(1,2)

critlist<-list()
critlist[[1]]<-crit1
critlist[[2]]<-crit2
critlist[[3]]<-crit3
critlist[[4]]<-crit4

并将其称为:

test <- cross_checker2(deflista,critlist)

除了第二个数据框之外,其他所有内容的输出都是正确的。 第一个数据框是正确的:

name1  |  zip1  | data1  | data2  | data3  | data4
-------|  ----- | -------|--------| -------|  -------
George |  123   | abc    | xxx    | <NA>   | bff
-------|  ------| -------| -------| -------| --------
Marge  | 456    | def    | yyy    | <NA>   | <NA>
------ | ------ | ------ | ------ | ------ | ------
Mike   | 789    | foo    | klm    | ggg    | <NA>

现在是第二个:

name2  | data2  | zip2   | data3  |  data4
------ | ------ | ------ | ------ | ------
Bob    | zzz    | 678    | zzz    | <NA>
------ | ------ | ------ | ------ | -------
George | xxx    | 123    | <NA>   | <NA>
-----  | ------ | ------ | ------ | ------
Marge  | yyy    | 456    | <NA>   | <NA>
-----  | ------ | ------ | ------ | ------
Mike   | klm    | 789    | ggg    | <NA>

这是不正确的,因为最后一个数据帧 (deflista[[4]]) 中的 George 和 Bob 都在那里,但由于某种原因,合并没有返回那些。

第三个数据框:

name3  |  zip3  |  data3  |  data4
------ | ------ | ------- | ------
Bob    | 678    | zzz     | hhh
-----  | ------ | ------- | --------
Mike   | 789    | ggg     | <NA>

这是正确的,因为在最后一个数据帧中找到了 Bob (deflista[[4]])

我无法弄清楚 for 循环有什么问题,因为在比较该组中的第二个数据帧时必须存在一些索引问题。有什么想法吗?

出于这些目的,我没有返回找到的条目的行索引,但我可以在弄清楚它有什么问题后立即添加它。此外,更喜欢基础库中的任何解决方案。

【问题讨论】:

  • 你能发布可重现的例子吗?没有人愿意复制和编辑您发布的那些表格
  • 请重新格式化您的代码,尤其是 Edit2。由于缩进错误和空间不足,难以阅读。 RStudio 将通过Ctrl+Shift+A 为您执行此操作。谢谢。

标签: r dataframe duplicates


【解决方案1】:

我相信,到目前为止,我已经修复了原始问题中的循环,它们返回了预期的结果:

# create lists
dflist <- list(df1, df2, df3)
criterialist <- list(c(1,2), c(1,3), c(2,3))

# add index columns
dflist <- lapply(dflist, function(x) {x[["index"]] <- seq_len(nrow(x)); x})

# find combinations of dataframes to check
combi <- combn(seq_along(dflist), 2)
combi
     [,1] [,2] [,3]
[1,]    1    1    2
[2,]    2    3    3
# check for matching rows
for (k in seq_len(ncol(combi))) {
  i <- combi[1, k]
  j <- combi[2, k]
  tmp <- merge(dflist[[i]], dflist[[j]], 
               by.x=criterialist[[i]], by.y=criterialist[[j]], all.x=TRUE)
  dflist[[i]][[paste0("row_df", j)]] <- tmp[order(tmp$index.x), "index.y"]
}
dflist
[[1]]
   name1 zip1 data1 index row_df2 row_df3
1 George  123   abc     1       2      NA
2  Marge  456   def     2       3      NA
3   Mike  789   foo     3       1       2

[[2]]
   name2 data2 zip2 index row_df3
1   Mike   klm  789     1       2
2 George   xxx  123     2      NA
3  Marge   yyy  456     3      NA
4    Bob   zzz  678     4       1

[[3]]
  data3 name3 zip3 index
1   zzz   Bob  678     1
2   ggg  Mike  789     2

请注意,这是检查 3 个数据框的预期结果(在问题的 Edit2 之前)。

有几个缺陷导致原始代码崩溃:

  1. 第二个for 循环中的循环限制定义不正确:for (i in 1:length(dflist)-1){。在这里,: 运算符优先,因此索引从 0 开始,这会导致错误。这可以通过额外的一对括号for (i in 1:(length(dflist)-1)){ 来解决,甚至可以通过使用seq_len() 函数for (i in seq_len(length(dflist)-1)) { 来解决。
  2. merge() 返回两列 index.xindex.y。在与df1 合并的情况下,它只返回一个index 列,其中OP 已经不遗余力地添加索引列。
  3. merge() 的结果需要在追加前按index.x 排序。
  4. for 循环导致数据帧与其自身进行比较。相反,combn() 函数用于查找所有唯一组合。

【讨论】:

    【解决方案2】:

    感谢您的意见!

    抱歉,我认为当我编辑我的原始帖子时,它删除了我从你们那里收到的一些意见。我不知道它会那样做。

    但是,我为此管理了一个解决方案,其中一个重要的解决方案是合并,因为我没有意识到它会改变列和行的顺序。

    无论如何,这是可行的:

    cross_checker4 <- function(dflist,criterialist) {
      # Initialize the output list
    
      returnlist <- list()
    
    
      # Initialize the outer loop length, 
      # this can be omitted in the for-loop below but let's 
      # keep it for historical reasons
    
      looplen1 <- length(dflist) - 1
    
      # Loop through all dataframes in dflist, 
      # this could just as well be for (i in 1:length(dflist)-1){}
    
      for (i in 1:looplen1) {
        # Initialize a temporary dataframe 
        # since we can't copy the data within dflist
        # Rearrange the columns for the output. 
        # Merge will mix them up otherwise
    
        temp_df1_drop <- dflist[[i]][-c(critlist[[i]])]
        temp_df1_keep <- dflist[[i]][c(critlist[[i]])]
        temp_df1 <- cbind(temp_df1_keep,temp_df1_drop)
    
        # Initialize the temporary criteria from criterialist
    
        temp_crit1 <- c(1:length(critlist[[i]]))
    
        # Loop through all remaining dataframes in dflist 
        # --> This is where we compare
    
        for (j in (i + 1):length(dflist)) {
          temp_df2 <- dflist[[j]]
    
    
          temp_df2_drop <- temp_df2[-c(critlist[[j]])]
          temp_df2_keep <- temp_df2[c(critlist[[j]])]
          temp_df2 <- cbind(temp_df2_keep,temp_df2_drop)
    
          # Add index column into dataframe to indicate 
          # which row the duplicate entry is on
    
          temp_df2$index <- 1:nrow(temp_df2)
    
          # Rename the index column
    
          indexer <- paste(c("index", j),collapse = " ")
          colnames(temp_df2)[colnames(temp_df2) == 'index'] <- indexer
    
          temp_crit2 <- c(1:length(critlist[[j]]))
    
          # Do the merge
    
          temp_df1 <-
            merge(
              temp_df1,temp_df2,by.x = temp_crit1,by.y = temp_crit2,all.x = TRUE
            )
        }
    
        # Insert merged dataframe into the returnlist
    
        returnlist[[length(returnlist) + 1]] <- temp_df1
    
      }
      # Since merge shoves in all columns in the comparison dataframe, 
      # we remove those columns and only leave index x
    
      for (k in 1:length(returnlist)) {
        for (o in (ncol(dflist[[k]]) + 1):(ncol(returnlist[[k]]))) {
          if (!grepl("index",names(returnlist[[k]])[o])) {
            returnlist[[k]] <- returnlist[[k]][,-o]
          }
    
        }
      }
    
      # Exit the loops and return the output list
      return(returnlist)
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-06-13
      • 2018-10-18
      • 2016-03-13
      • 1970-01-01
      • 2018-02-04
      • 2018-11-25
      • 1970-01-01
      • 2020-01-10
      相关资源
      最近更新 更多