代码之家  ›  专栏  ›  技术社区  ›  kittygirl

如何使用“merge”代替“plyr::join(x,y,type='full')”[[副本]

r
  •  0
  • kittygirl  · 技术社区  · 6 年前

    我想合并两个数据帧,保持其中一个的原始行顺序( df.2

    以下是一些示例数据(所有值都来自 class

    df.1 <- data.frame(class = c(1, 2, 3), prob = c(0.5, 0.7, 0.3))
    df.2 <- data.frame(object = c('A', 'B', 'D', 'F', 'C'), class = c(2, 1, 2, 3, 1))
    

    如果我这样做了:

    merge(df.2, df.1)
    

    输出为:

      class object prob
    1     1      B  0.5
    2     1      C  0.5
    3     2      A  0.7
    4     2      D  0.7
    5     3      F  0.3
    

    如果我加上 sort = FALSE

    merge(df.2, df.1, sort = F)                                                        
    

      class object prob
    1     2      A  0.7
    2     2      D  0.7
    3     1      B  0.5
    4     1      C  0.5
    5     3      F  0.3
    

    但我想要的是:

      class object prob
    1     2      A  0.7
    2     1      B  0.5
    3     2      D  0.7
    4     3      F  0.3    
    5     1      C  0.5
    
    0 回复  |  直到 6 年前
        1
  •  42
  •   joran    11 年前
        2
  •  56
  •   PAC    12 年前

    您只需要创建一个变量,它给出df.2中的行号。然后,一旦合并了数据,就可以根据这个变量对新数据集进行排序。举个例子:

    df.1<-data.frame(class=c(1,2,3), prob=c(0.5,0.7,0.3))
    df.2<-data.frame(object=c('A','B','D','F','C'), class=c(2,1,2,3,1))
    df.2$id  <- 1:nrow(df.2)
    out  <- merge(df.2,df.1, by = "class")
    out[order(out$id), ]
    
        3
  •  11
  •   alex23lemm    11 年前

    您也可以查看 inner_join 哈德利函数 dplyr plyr ). 它保留第一个数据集的行顺序。与所需解决方案的细微差别在于,它还保留了第一个数据集的原始列顺序。所以它不一定把我们用来合并的列放在第一个位置。

    inner_join(df.2,df.1)
    Joining by: "class"
      object class prob
    1      A     2  0.7
    2      B     1  0.5
    3      D     2  0.7
    4      F     3  0.3
    5      C     1  0.5
    
        4
  •  11
  •   Uwe    7 年前

    data.table v1.9.5+ ,您可以执行以下操作:

    require(data.table) # v1.9.5+
    setDT(df.1)[df.2, on="class"]
    

    对列执行联接 class df.1 中的每一行 df.2 并提取相应的列。

        5
  •  5
  •   Uwe    7 年前

    在联接中更新 同时保留原始行顺序。这可能是一个替代方案 Arun's data.table answer 如果只有几列要追加:

    library(data.table)
    setDT(df.2)[df.1, on = "class", prob := i.prob][]
    
       object class prob
    1:      A     2  0.7
    2:      B     1  0.5
    3:      D     2  0.7
    4:      F     3  0.3
    5:      C     1  0.5
    

    df.2 是正确地加入到 df.1 并获得一个新的专栏 prob 它是从 .

        6
  •  3
  •   Community CDub    8 年前

    accepted answer 提出了一种使用时保持秩序的手动方法 merge ,大多数情况下可以工作,但需要不必要的手动操作。这个解决方案来自于 How to ddply() without sorting? ,它处理的是维持秩序的问题,但在拆分应用组合上下文中:

    #Peter's version used a function gensym to
    # create the col name, but I couldn't track down
    # what package it was in.
    keeping.order <- function(data, fn, ...) { 
      col <- ".sortColumn"
      data[,col] <- 1:nrow(data) 
      out <- fn(data, ...) 
      if (!col %in% colnames(out)) stop("Ordering column not preserved by function") 
      out <- out[order(out[,col]),] 
      out[,col] <- NULL 
      out 
    } 
    

    keeping.order 函数以保持

    df.1<-data.frame(class=c(1,2,3), prob=c(0.5,0.7,0.3))
    df.2<-data.frame(object=c('A','B','D','F','C'), class=c(2,1,2,3,1))
    keeping.order(df.2, merge, y=df.1, by = "class")
    

    根据要求,将产生:

    > keeping.order(df.2, merge, y=df.1, by = "class")
      class object id prob
    3     2      A  1  0.7
    1     1      B  2  0.5
    4     2      D  3  0.7
    5     3      F  4  0.3
    2     1      C  5  0.5
    

    维持秩序 有效地自动化已接受答案中的方法。

        7
  •  1
  •   qed    12 年前

    merge_sameord = function(x, y, ...) {
        UseMethod('merge_sameord')
    }
    
    merge_sameord.data.frame = function(x, y, ...) {
        rstr = paste(sample(c(0:9, letters, LETTERS), 12, replace=TRUE), collapse='')
        x[, rstr] = 1:nrow(x)
        res = merge(x, y, all.x=TRUE, sort=FALSE, ...)
        res = res[order(res[, rstr]), ]
        res[, rstr] = NULL
        res
    }
    

        8
  •  1
  •   moodymudskipper    7 年前

    在这种情况下,你可以告诉我们 factor 对于紧凑的基本解决方案:

    df.2$prob = factor(df.2$class,labels=df.1$prob)
    
    df.2
    #   object class prob
    # 1      A     2  0.7
    # 2      B     1  0.5
    # 3      D     2  0.7
    # 4      F     3  0.3
    # 5      C     1  0.5
    

    但不是一般解决方案,如果:

    1. 您想更新一个表,而不是创建一个新表
    2. 查找表按合并列排序
    3. 查找表没有额外的级别
    4. 你想要一个 left_join

    1是没有商量余地的,剩下的我们可以做:

    df.3  <- df.2 # deal with 2.
    df.1b <- df.1[order(df.1$class),] # deal with 3
    df.1b <- df.1b[df.1$class %in% df.2$class,] # deal with 4.
    df.3$prob = factor(df.3$class,labels=df.1b$prob)
    df.3 <- df3[!is.na(df.3$prob),] # deal with 5. if you want an `inner join`
    df.3$prob <- as.numeric(as.character(df.3$prob)) # deal with 6.
    
        9
  •  0
  •   BurninLeo    6 年前

    # Use the key variable as row.names
    row.names(df.1) = df.1$key
    
    # Sort df.1 so that it's rows match df.2
    df.3 = df.1[df.2$key, ]
    
    # Create a data.frame with cariables from df.1 and (the sorted) df.2
    df.4 = cbind(df.1, df.3)
    

    此代码将保留df.2及其顺序,并仅添加df.1中的匹配数据

    如果只添加一个变量,则 cbind()

    row.names(df.1) = df.1$key
    df.2$data = df.1[df.2$key, "data"]
    
        10
  •  0
  •   MS Berends    5 年前

    对于包开发人员

    作为软件包开发人员,您希望尽可能少地依赖其他软件包。尤其是tidyverse函数,对于包开发人员来说,这种变化太频繁了。

    能够利用 dplyr 打包而不导入 dplyr公司 ,下面是一个快速实现。它保持了原来的排序(按OP的要求),并且没有将连接列移到前面(这是另一件令人讨厌的事情) merge()

    left_join <- function(x, y, ...) {
      merge_exec(x = x, y = y, all.x = TRUE, ...)
    }
    right_join <- function(x, y, ...) {
      merge_exec(x = x, y = y, all.y = TRUE, ...)
    }
    inner_join <- function(x, y, ...) {
      merge_exec(x = x, y = y, all = TRUE, ...)
    }
    full_join <- function(x, y, ...) {
      merge_exec(x = x, y = y, ...)
    }
    
    # workhorse:
    merge_exec <- function(x, y, ...) {
      # set index
      x$join_id_ <- 1:nrow(x)
      # do the join
      joined <- merge(x = x, y = y, sort = FALSE, ...)
      # get suffices (yes, I prefer this over suffixes)
      if ("suffixes" %in% names(list(...))) {
        suffixes <- list(...)$suffixes
      } else {
        suffixes <- c("", "")
      }
      # get columns names in right order, so the 'by' column won't be forced first
      cols <- unique(c(colnames(x), 
                       paste0(colnames(x), suffixes[1]), 
                       colnames(y), 
                       paste0(colnames(y), suffixes[2])))
      # get the original row and column index
      joined[order(joined$join_id),
             cols[cols %in% colnames(joined) & cols != "join_id_"]]
    }
    
        11
  •  0
  •   ah bon    4 年前

    评分最高的答案不会产生原始海报想要的内容,即第1栏中的“类”。如果OP允许在df.2中切换列顺序,那么下面是一个可能的base R non merge单行答案:

    df.1 <- data.frame(class = c(1, 2, 3), prob = c(0.5, 0.7, 0.3))  
    df.2 <- data.frame(class = c(2, 1, 2, 3, 1), object = c('A', 'B', 'D', 'F', 'C'))  
    cbind(df.2, df.1[match(df.2$class, df.1$class), -1, drop = FALSE])
    

    我碰巧喜欢列名中描述的信息。一个完全复制OP期望结果的一行代码是

    data.frame(cbind(df.2, df.1[match(df.2$class, df.1$class), -1, drop = FALSE]),
               row.names = NULL)
    

    我同意你的看法 https://stackoverflow.com/users/4575331/ms-berends 一个包开发人员对另一个包(或“verse”)的依赖越少越好,因为开发路径常常随着时间的推移而发生分歧。

    df.1$class . 这可以通过sans merge来克服 'outer'

        12
  •  -1
  •   James Holland    10 年前

    在基地可能有更有效的方法。这将是一个相当简单的函数。

    varorder <- names(mydata)  # --- Merge 
    mydata <- merge(mydata, otherData, by="commonVar")
    restOfvars <- names(mydata[!(names(mydata) %in% varorder)])
    
    mydata[c(varorder,restOfvars)]