【问题标题】:matching data from one data frame to another将数据从一个数据帧匹配到另一个数据帧
【发布时间】:2014-12-09 12:06:28
【问题描述】:

我正在尝试执行以下操作,我最初发布了一个更简单的版本,认为它会泛化但现在意识到它不会,所以我在这里重新发布问题

原始问题(和解决方案)可以在这里找到:Matching data from one data frame to another

我有两个数据框,dfa 和 dfb

IDa <- c(1,2,3)
score1a <- c(5,10,1)
score2a <- c(NA,8,NA)
score3a <- c(NA,NA,13)
score1b <- c(NA,4,9)
score2b <- c(2,3,NA)
score2c <- c(1,5,1)
score3c <- c(6,NA,1)

dfa <- data.frame(IDa,score1a,score2a,score3a,score1b,score2b,score2c,score3c)

IDb <- c(1,1,1,2,2,3)
timeb <- c(1,2,3,2,3,3)

dfb <- data.frame(IDb,timeb)

在 score1a 中,“1”代表 dfb 中的 timeb = 1,“a”代表第一种测试类型(因此有 3 种测试类型,a、b、c 和 3 个时间点 1、2、3)

我想从 dfa 获取数据并将其添加到 dfb 以在下面创建类似 dfc 的内容(注意 dfc 的前两列与 dfb 相同)

IDc <- c(1,1,1,2,2,3)
timec <- c(1,2,3,2,3,3)
scorea <- c(5,NA,NA,8,NA,13)
scoreb <- c(NA,2,NA,3,NA,NA)
scorec <- c(NA,1,6,5,NA,1)

dfc <- data.frame(IDc, timec, scorea, scoreb, scorec)

希望这是有道理的,非常感谢您对此的任何帮助!

【问题讨论】:

    标签: r reshape


    【解决方案1】:

    这是一个使用 dplyr 和 tidyr 的选项:

    require(dplyr)
    require(tidyr)
    
    gather(dfa, xx, timea, -IDa) %>%
      mutate(xx = as.character(xx),
             x = gsub("[0-9]", "", xx)) %>%
      spread(x, timea) %>%
      mutate(xx = as.numeric(gsub("[a-zA-Z]", "", xx))) %>%
      group_by(IDa, xx) %>%
      summarise_each(funs(first(.[!is.na(.)]))) %>%
      left_join(dfb, ., by = c("IDb" = "IDa", "timeb" = "xx"))
    
    #  IDb timeb scorea scoreb scorec
    #1   1     1      5     NA     NA
    #2   1     2     NA      2      1
    #3   1     3     NA     NA      6
    #4   2     2      8      3      5
    #5   2     3     NA     NA     NA
    #6   3     3     13     NA      1
    

    执行以下步骤(每行代码):

    1. gather:将数据从宽格式重新整形(收集)为长格式,新列将命名为“xx”和“timea”
    2. 变异:将“xx”列变成字符列(之前是一个因子)
    3. Still Mutate:根据列“xx”创建一个新列“x”,但删除 xx 中的所有数字
    4. 传播:根据新的“x”列将数据从长到宽重新整形
    5. 变异:从“xx”中删除所有字符并将其余字符(字符格式的数字)存储为数字数字
    6. group_by:按“IDa”和“xx”对数据进行分组
    7. summarise_each:在每个组内(按 IDa 和 xx),对于除分组变量 IDa 和 xx 之外的每一列:取 first 元素,即 not NA。更明确地说:.[!is.na(.)] 从数据中删除所有 NA 条目,然后将 first() 函数包裹在它周围,获取没有 NA 的数据的第一个元素。通常,summarise 和 summarise_each 会将每个组的数据分解为 1 行(在这种情况下,它将保存第一个非 NA 条目)。
    8. left_join:分别按 ID 列和 timeb 和 xx 对 dfb 与先前计算的数据(注意 left_join 内部的顺序,这在此处很重要)执行左连接。

    编辑 2

    这里有一些例子可以更好地理解first(.[!is.na(.)]) 部分的作用。请记住,在代码中,. 表示传递给函数的分组数据(相当于我在下面的示例中称为 x)。

    set.seed(99)
    x <- sample(10)  #create a vector with random numbers
    x
    #[1]  6  2 10  7  4  5  3  1  8  9
    
    x[sample(10, 4, replace = F)] <- NA  # add some NAs
    x
    #[1]  6 NA 10  7 NA NA  3  1 NA  9 
    
    is.na(x)  # is the value in each in index/place of x equal to NA?
    #[1] FALSE  TRUE FALSE FALSE  TRUE  TRUE FALSE FALSE  TRUE FALSE
    
    x[is.na(x)]   # show me the values of x which are NA (of course, they are NA)
    #[1] NA NA NA NA
    
    x[!is.na(x)]  # show me the values of x which are not NA (== remove NAs)
    #[1]  6 10  7  3  1  9
    
    dplyr::first(x[!is.na(x)])  # of all the values in x which are not NA, return the first one
    #[1] 6
    
    x[!is.na(x)][1]  # this is equivalent to the previous line but using [1] instead of first()
    #[1] 6
    
    head(x[!is.na(x)], 1)  # this is also equivalent of the two previous lines but using head(..., 1)
    #[1] 6
    

    希望对您有所帮助。

    【讨论】:

    • 谢谢!你能扩展一下这条线在做什么summarise_each(funs(first(.[!is.na(.)])))
    • @Impossible9,我在答案中添加了解释。
    • 谢谢,这是有道理的。我在哪里可以找到有关.[!is.na(.)] 语法的更多信息?我以前从未见过!is.na() 以这种方式使用过(我也不是很了解子集是如何工作的)并且环顾四周但找不到任何东西
    • 我真的不知道有什么好读的,除了可能检查帮助(“is.na”),但我添加了一些关于这个主题的更多解释。
    【解决方案2】:

    这是使用我的“splitstackshape”包中的merged.stack 和merge 的替代方法。

    通常,R 中与 reshape 相关的函数似乎喜欢名称为“类型”+“时间”的形式(您的变量当前为“时间”+“类型”的形式)。我们可以使用“data.table”中的setnames(与“splitstackshape”一起加载)轻松地将列重命名为所需的形式。

    library(splitstackshape)
    setnames(dfa, gsub("(score)(\\d)([a-z])", "\\3_\\2", names(dfa)))
    

    一旦名称正确,我们就会堆叠相关列并将结果与​​您的第二个数据集合并。需要转换为数字才能在相同类型的数据上进行合并。

    setkey(
      merged.stack(dfa, var.stubs = c("^a", "^b", "^c"), 
                   sep = "_")[, .time_1 := as.numeric(.time_1)],
      IDa, .time_1)[setkeyv(as.data.table(dfb), names(dfb))]
    #    IDa .time_1 ^a ^b ^c
    # 1:   1       1  5 NA NA
    # 2:   1       2 NA  2  1
    # 3:   1       3 NA NA  6
    # 4:   2       2  8  3  5
    # 5:   2       3 NA NA NA
    # 6:   3       3 13 NA  1
    

    【讨论】:

      【解决方案3】:

      类似于上面的@beginneR 答案,但避免使用分组/summarise_each:

      library(tidyr)
      library(dplyr)
      
      colnames(dfa)[-1] <- c("scorea1","scorea2","scorea3","scoreb1","scoreb2","scorec2","scorec3") 
      
      dfa %>%
        gather(name, score, scorea1:scorec3) %>%
        separate(variable, c("score","time"), 6) %>%
        mutate(time = as.numeric(time)) %>%
        spread(score, value) %>%
        left_join(dfb, ., by= c("IDb"="IDa", "timeb"="time"))
      

      【讨论】:

        猜你喜欢
        • 2023-03-25
        • 1970-01-01
        • 2021-05-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-24
        • 2021-04-03
        相关资源
        最近更新 更多