【问题标题】:Unexpected behaviour of data.tabledata.table 的意外行为
【发布时间】:2021-11-16 09:22:47
【问题描述】:

我对 R 的大部分经验都与数据有关。框架的世界。然而,由于我经常不得不与大型 data.frames 作斗争,我决定探索高效的 data.table 世界,因此,我要为学习曲线的后果付出代价。这是我的假设情况:

> sessionInfo()
R version 4.1.1 (2021-08-10)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19043)

> sessioninfo::package_info()
package     * version date       lib source        
data.table  * 1.14.0  2021-02-21 [1] CRAN (R 4.1.1)

我有一张长桌,例如它的名字是创始人

> library(data.table)
nfounder <- 1000 
founders <- data.table(ID= seq(1:nfounder), 
                           mum= rep(0, nfounder), 
                           dad= rep(0, nfounder))

另一方面,我有一个短表 (ped.g),其中变量 mum 和 dad 来自于创始人表中的变量 ID,即

ped.g <- data.table(ID= seq(1001, 1003), 
                      mum= c(19,23,23), 
                      dad= c(13,13,19))

因此,谱系表是根据创始人和 ped.g 表构建的

  pedigree <- rbind(founders, ped.g)
  > pedigree
    ID mum dad
   1:    1   0   0
   2:    2   0   0
   3:    3   0   0
   4:    4   0   0
   5:    5   0   0
   ---             
   999:  999   0   0
  1000: 1000   0   0
  1001: 1001  19  13
  1002: 1002  23  13
  1003: 1003  23  19

因此,

  ID <- pedigree[, ID] 
  mum <- match(pedigree[, mum], pedigree[, ID], nomatch=NA)
  dad <- match(pedigree[, dad], pedigree[, ID], nomatch=NA)

这样

  > mum
  [1] NA NA NA ... NA NA 19 23 23
  > dad
  [1] NA NA NA ... NA NA 13 13 19

接下来的步骤包括函数生成(“Generation”,用图解继承的家谱的遗传表示),不管这个函数,关键是谱系表

  generation <- function(pedigree) {
      ID <- pedigree[, ID] 
      mum <- match(pedigree[, mum], pedigree[, ID], nomatch=NA)
      dad <- match(pedigree[, dad], pedigree[, ID], nomatch=NA)
      n <- dim(pedigree)[1]
      generOld <- gen <- rep(n + 100, n) 
      gen[is.na(mum) & is.na(dad)] <- 0
      i <- 0
      while(!all(generOld == gen)) {
         generOld <- gen
         gen[mum %in% ID[gen==i]] <- i + 1
         gen[dad %in% ID[gen==i]] <- i + 1
         i <- i + 1
         if(i > n + 10) break
      }
     pedigree[, gen := gen]
    return(pedigree)
   } 

因此,变量 gen 被添加到谱系表中,因此创始人个人的 gen=0 和第一代的 gen=1

pedigree[, gen := gen]
> pedigree
        ID mum dad gen
   1:    1   0   0   0
   2:    2   0   0   0
   3:    3   0   0   0
   4:    4   0   0   0
   5:    5   0   0   0
   ---                 
   999:  999   0   0   0
  1000: 1000   0   0   0
  1001: 1001  19  13   1
  1002: 1002  23  13   1
  1003: 1003  23  19   1

显然,一切似乎都很好......!然而,这个谱系包括许多未被选为“第一代”作为父母(妈妈、爸爸)的个体(ID)。也就是说,它必须只包括父母 13、19 和 23(属于“第 1 代”)。

因此,为了尝试解决之前的问题(仅保留从创始人 data.table 中选择的父母),我使用了以下方法(代码)

回想一下 ped.g 表是

> ped.g
 ID mum dad
 1: 1001  19  13
 2: 1002  23  13
 3: 1003  23  19

ped.tmp <- rbind(founders, ped.g)
parents.G0 <- unique(unlist(ped.g[, c(2, 3)]))
parents.G0 <- as.data.table(parents.G0)
setnames(parents.G0, 'parents.G0', 'parents')
setkey(parents.G0, parents) 
setkey(ped.tmp, ID) 
founders.new <- ped.tmp[parents.G0]

> founders.new
 TreeID mum dad
1:  13   0   0
2:  19   0   0
3:  23   0   0

pedigree.new <- as.data.table(rbind(founders.new, ped.g))
> pedigree.new
ID mum dad
1: 13   0   0
2: 19   0   0
3: 23   0   0
4:  1  19  13
5:  2  23  13
6:  3  23  19

到这里,一切顺利……现在,我将尝试利用前面概述的代码构建“Generation”字段

ID <- pedigree.new[, ID]
mum <- match(pedigree.new[, mum], pedigree.new[, ID], nomatch=NA)
dad <- match(pedigree.new[, dad], pedigree.new[, ID], nomatch=NA)

第一个惊喜(或与早期输出的差异)。现在变量 mum 和 dad 由它们在表 pedigree.new 中的变量 ID 对应的索引位置来表示,而不是像前面的情况那样,用它自己的值(13、19 和 23)来表示,即

> mum
[1] NA NA NA  2  3  3
> dad
[1] NA NA NA  1  1  2

因此,在应用代码创建变量“Generation”时,我得到了错误的结果。也就是说,对于第一代,变量 gen=106 而不是 gen=1。

pedigree.new[, gen := gen]
> pedigree.new
ID mum dad gen
1: 13   0   0   0
2: 19   0   0   0
3: 23   0   0   0
4:  1  19  13 106
5:  2  23  13 106
6:  3  23  19 106

当然,我也尝试过不同的方法来创建表谱系(过滤版)。例如,请参阅下一次尝试。

parents.G0 <- unique(unlist(ped.g[, c(2, 3)]))
parents.G0 <- as.data.table(parents.G0)
setnames(parents.G0, 'parents.G0', 'parents')
founders.new2 <- founders[parents.G0, on=.(ID=parents)]
pedigree.new2 <- rbind(founders.new2, ped.g)

但是,我得到了相同的结果..

 > gen
 [1]   0   0   0 106 106 106

此外,我比较了两个表的属性和结构(谱系与 pedigre.new),但是,它们看起来相同。过滤时似乎在原始谱系表中丢失了一些东西,我真的想更好地理解为什么 data.table 对象会以这种方式表现。所以任何帮助将不胜感激。

问题的重新表述

  1. 输入:表谱系

    pedigree <- rbind(founders, ped.g)
    > pedigree
          ID   mum dad
    1:    1     0   0
    2:    2     0   0
    3:    3     0   0
    ---   
    13:   13    0   0
    ---   
    19:   19    0   0
    ---   
    23:   23    0   0
    ---              
    999:  999   0   0
    1000: 1000  0   0
    1001: 1001  19  13
    1002: 1002  23  13
    1003: 1003  23  19
    

2) 处理,此步骤由过滤谱系表组成,以便“保留”:

a) 在谱系表(ID=13,19,23)上等于mum 或dad 字段不同的0 的ID 记录,即

       ID mum dad
   1:  13   0   0
   2:  19   0   0
   3:  23   0   0

和 b) 与那些不为零的妈妈和爸爸有关的 ID 记录(ID=1001、1002、1003),即

          ID   mum dad
   1001: 1001  19  13
   1002: 1002  23  13
   1003: 1003  23  19

3 ) 预期输出:从上一步来看,过滤器谱系表如下所示

  > pedigree.new
    ID  mum dad
  1: 13   0   0
  2: 19   0   0
  3: 23   0   0
  4:  1  19  13
  5:  2  23  13
  6:  3  23  19

这样

  ID <- pedigree.new[, ID] 
  mum <- match(pedigree.new[, mum], pedigree.new[, ID], nomatch=NA)
  dad <- match(pedigree.new[, dad], pedigree.new[, ID], nomatch=NA)

  > mum
  [1] NA NA NA ... NA NA 19 23 23
  > dad
  [1] NA NA NA ... NA NA 13 13 19

因此,在应用函数生成时,新字段 gen“必须看起来像”

  pedigree.new <- generation(pedigree.new)

  > pedigree.new
     ID  mum dad gen
  1: 13   0   0   0
  2: 19   0   0   0
  3: 23   0   0   0
  4:  1  19  13   1
  5:  2  23  13   1
  6:  3  23  19   1
  1. 一些尝试的示例(审查表创始人和 ped.g 早期概述)

    ped.tmp <- rbind(founders, ped.g)
    parents.G0 <- unique(unlist(ped.g[, c(2, 3)]))
    parents.G0 <- as.data.table(parents.G0)
    setnames(parents.G0, 'parents.G0', 'parents')
    setkey(parents.G0, parents) 
    setkey(ped.tmp, ID) 
    founders.new <- ped.tmp[parents.G0]
    
    > founders.new
       ID mum dad
    1: 13    0   0
    2: 19    0   0
    3: 23    0   0
    
    pedigree.new2 <- as.data.table(rbind(founders.new, ped.g))
    
    > pedigree.new2
      ID mum dad
      1: 13   0   0
      2: 19   0   0
      3: 23   0   0
      4:  1  19  13
      5:  2  23  13
      6:  3  23  19
    
     ID <- pedigree.new2[, ID] 
     mum <- match(pedigree.new2[, mum], pedigree.new2[, ID], nomatch=NA)
     dad <- match(pedigree.new2[, dad], pedigree.new2[, ID], nomatch=NA)
    
      > mum
     [1] NA NA NA  2  3  3
      > dad
     [1] NA NA NA  1  1  2
    
     pedigree.new2 <- generation(pedigree.new2)
     > pedigree.new2 
          ID   mum dad gen
     1:   13   0   0   0
     2:   19   0   0   0
     3:   23   0   0   0
     4: 1001  19  13 106
     5: 1002  23  13 106
     6: 1003  23  19 106
    

因此,函数 generation() 无法定义字段 gen(ej。ID=1001、1002 和 1003 的 gen 字段等于 106 而不是 gen=1)。

【问题讨论】:

  • 你能用 1. 你的输入来安排你的问题吗? 2.你想要的输出; 3.(不太重要,但因为你已经拥有它)你尝试了什么。很难遵循您想要发生的事情,因此更难提供帮助。
  • Dean,非常感谢您的回答,我重新定义了我的问题,定义了一个名为“生成”的函数,以便维持秩序。然而,关键是过滤后产生的谱系表。也就是说,如果谱系表没问题,则“生成”功能起作用,否则,它不起作用......!

标签: r data.table


【解决方案1】:

我认为问题的根本原因是对match()的返回值的误解。

根据help("match"),如果有匹配,match() 返回一个整数向量,给出第一个匹配的table 中的位置,否则nomatch

因此,为了修复generation() 函数,我们需要子集 ID 与调用match() 返回的索引:

generation <- function(pedigree) {
  ID <- pedigree[, ID]
  # subset ID with indices returned by match()
  mum <- ID[match(pedigree[, mum], pedigree[, ID], nomatch = NA)]
  dad <- ID[match(pedigree[, dad], pedigree[, ID], nomatch = NA)]
  n <- dim(pedigree)[1]
  generOld <- gen <- rep(n + 100, n)
  gen[is.na(mum) & is.na(dad)] <- 0
  i <- 0
  while (!all(generOld == gen)) {
    generOld <- gen
    gen[mum %in% ID[gen == i]] <- i + 1
    gen[dad %in% ID[gen == i]] <- i + 1
    i <- i + 1
    if (i > n + 10)
      break
  }
  pedigree[, gen := gen]
  return(pedigree)
}

现在,我们得到

generation(pedigree.new)
pedigree.new
     ID mum dad gen
1:   13   0   0   0
2:   19   0   0   0
3:   23   0   0   0
4: 1001  19  13   1
5: 1002  23  13   1
6: 1003  23  19   1

编辑:data.table 替换为generation()

如果我正确理解了 OP 的要求,下面的代码是 OP 的 generation() 函数的替代品,这对于 data.table 来说更为惯用:

generation_dt <- function(pdg) {
  pdg[, gen := 0L]
  for (i in 0:nrow(pdg)) {
    pdg[mum %in% ID[gen == i] | dad %in% ID[gen == i], gen := gen + 1L]
    if (.Last.updated == 0L) break
  }
  pdg[]
}

使用 2 代用例进行测试:

pedigree2 <- fread("
  ID  mum  dad
  13    0    0
  19    0    0
  23    0    0
  24    0    0
1001   19   13
1002   23   13
1003   23   19
1004 1003   24
")

generation_dt(pedigree2)
     ID  mum dad gen
1:   13    0   0   0
2:   19    0   0   0
3:   23    0   0   0
4:   24    0   0   0
5: 1001   19  13   1
6: 1002   23  13   1
7: 1003   23  19   1
8: 1004 1003  24   2

.Last.updated 是一个变量,其中包含受最近:=set() 影响的行数。

【讨论】:

  • Uwe,非常感谢您的回答,问题已解决...!但是,我仍然不清楚 data.table 对象的行为,也许还有 match() 函数。我的意思是,根据我的理解,匹配函数返回索引位置。但是,如果您考虑原始谱系表 (dim(pedigree)=1003x3),然后使用 match 函数但没有按照您的建议包含 ID,它会返回妈妈/爸爸的值,而不是其在谱系中的位置。即 ID mum [1] NA ... NA 19 23 23
  • 在原始谱系表有 1003 行的情况下,match(...) 返回与ID[match(...) 相同的值只是巧合,因为ID 与行号/位置相同。
  • 干得好,乌维... !!!你的回答超出了我的预期。我的意思是,除了澄清有关 match() 函数可能不一致的疑问之外,您还改进了函数生成,即 generation_dt。再次感谢......!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-20
  • 1970-01-01
相关资源
最近更新 更多