【问题标题】:Extract row indices of a dataframe whose entries correspond to rows of another dataframe提取数据帧的行索引,其条目对应于另一个数据帧的行
【发布时间】:2017-11-08 12:33:11
【问题描述】:

我已经挣扎了一段时间了,我找不到出路。这是我的问题。

我有 2 个数据框:

    df1 <- data.frame(replicate(3,sample(1:10,20,rep=TRUE)))
    df1
      X1 X2 X3
   1  10  1  9
   2   3  4  2
   3   7  6  8
   4   8 10  7
   5   5  7  5
   6   8  5  9
   7   9  8  4
   8   6  2  7
   9   2  9  6
   10  5  2  9

  df2 <- data.frame(df1[sample(nrow(df1),4), ])
  df2
     X1 X2 X3
  8   6  2  7
  3   7  6  8
  10  5  2  9
  7   9  8  4

我想创建一个 length(x) = length(df1) 的向量 x,每行包含 df1df2 中对应行的行索引(即,df1df2 之间的每一列的精确值相同)。

考虑一下:

    dim(df1)
    [1] 1096188  3 

    dim(df2)
    [1] 256  3

并且 df1 有几行具有相同的值(即相应的行索引将相同),并且原则上 df1 中的所有行都应该找到与 df2 中的行匹配。

预期的输出是:

    x
   [1] 0 0 2 0 0 0 4 1 0 3

希望这已经足够清楚了......

你能帮忙吗?

谢谢,

皮耶拉

【问题讨论】:

  • 不。我需要一个与 df1 长度相同的新向量,其中包含 df2 的行索引,其条目与 df1 完全相同。因此,最后,对于 df1 的每一行,我将拥有 df2 中相应行的行索引。当然,df1 会有几行具有相同的值,因此关联的行索引将是相同的。
  • 是的,抱歉,我认为该示例可能会有所改进。我的 df1 有几行具有相同的值。我应该澄清一下。
  • 您能从给出的示例中发布您的预期输出吗?我假设它是[1] 8 3 10 7
  • 完成! :) 根据需要编辑。
  • @CathG 完成。现在清楚了吗?

标签: r


【解决方案1】:

这是data.table的选项:

require(data.table)

# first set the original orders (data.frame will be sorted when doing setkey)
setDT(df1)[, ori := .I]
setDT(df2)[, ind_df2 := .I]

# define keys
setkey(df1, X1, X2, X3)
setkey(df2, X1, X2, X3)

# compute the indices of the df1 line in df2
x <- df2[df1, ind_df2]
# put the nomatch to 0
x[is.na(x)] <- 0

# Finally, put the original orders back and delete the variable ori
x <- x[order(df1$ori)]
df2 <- df2[order(df2$ind_df2)]
df1[, ori:=NULL]
df2[, ind_df2:=NULL]

结果 x(使用您的数据):

x
#[1] 0 0 2 0 0 0 4 1 0 3

@Frank 建议的另一个更简单有效的选择:

setkeyv(setDT(df2)[,ii:=.I],setdiff(names(df2),"ii"))
x <- df2[df1]$ii
x[is.na(x)] <- 0

@nicola 答案、@Frank 建议和我的答案之间的一些基准,在 100000 行 df1 和 200 行 df2 上稍微修改了 nicola 的得到所需输出的答案(两个函数给出相同的结果,除了 nicola 需要 as.numeric):

所以:

set.seed(17)
df1 <- data.frame(replicate(3,sample(1:100,100000,rep=TRUE)))
df2 <- data.frame(df1[sample(nrow(df1),200), ])

nicola <- function(){x<-match(do.call(paste,df1),do.call(paste,df2), nomatch=0)}

cath <- function(){
          dt1 <-data.table(df1); dt1[, ori:=.I]
          dt2 <- data.table(df2); dt2[, ind_df2:=.I]
          setkey(dt1, X1, X2, X3)
          setkey(dt2, X1, X2, X3)
          x <- dt2[dt1, ind_df2]
          x[is.na(x)] <- 0
          x <- x[order(dt1$ori)]
          x
        }

Frank <- function(){dt1 <-data.table(df1);dt2 <- data.table(df2); setkey(setDT(dt2)[,ii:=.I],X1,X2,X3); x <- dt2[dt1]$ii;x[is.na(x)] <- 0}

require(microbenchmark)
microbenchmark(cath(), Frank(), nicola(), unit="relative", times=100)
    #Unit: relative
    # expr       min        lq     mean    median       uq      max neval cld
  #Frank()  1.000000  1.000000 1.000000  1.000000 1.000000 1.000000   100 a  
  # cath()  3.238195  3.099896 2.438342  2.767165 2.177365 1.447397   100  b 
 #nicola() 13.127820 12.476996 8.761549 10.899191 7.292086 2.783436   100   c

【讨论】:

  • 这个有效!!非常感谢!当我尝试以原始形式重新排序时,我只是得到了一个错误;它告诉我:顺序错误(df1$ori):参数 1 不是向量
  • 现在可以使用了!而且速度也很快!!问题是我第一次运行它并且几乎没有错误,因为我需要更改 df 的名称,然后我显然没有重新运行所有内容,所以 ori 已经消失了。极好的!再次感谢!
  • 这就是 data.table 的伟大之处,它使用二进制搜索,比“常规”搜索快一千倍! :-)。我很高兴它对你有用。如果您经常使用这样的大 data.frame,您可能需要深入研究 data.table ;-)
  • 伟大的基准。无论如何,如果您认为1:nrow(df2) 是正确的而不是rownames(df2),那么我的答案的子集部分是无用的,只需match(..., nomatch=0) 就足够了。在这个改变之后,我猜这两个选项在效率上会是相似的。
  • 1:nrow()代替.I
【解决方案2】:

我只是试试:

 x <- rownames(df2)[match(do.call(paste, df1), do.call(paste, df2))]
 x[is.na(x)] <- 0

关于期望的输出有很多讨论;在@CathG 解释中,这一行产生了它:

 match(do.call(paste, df1), do.call(paste, df2),nomatch=0)

【讨论】:

  • 这个也可以! :) 谢谢,我以前没见过!而且这也很容易......再次感谢。
  • acutally,我正在尝试做一些基准测试,但没有通过您的函数获得预期的输出。我认为您应该将 rownames(df2) 替换为 (1:nrow(df2)) 它似乎不那么“危险”...
  • 如果他想要行索引或行名,我真的不能确定阅读 OP。事实上他的df2 确实有行名,我选择了这个解决方案。如果 OP 介入并明确说明他想要什么,我会在需要时进行更改。
  • @Piera 您需要 df2 中存在的 df1 行的索引还是 df2 行的索引? nicola 的回答给了你第一个(但只是因为 df2 是基于 df1 构建的),我给你第二个
  • @Piera。很可能是。我更喜欢使用索引而不是行名。行名可以是任何东西...
【解决方案3】:

在您的示例中,df1 中的行在 df2 中并非都匹配(例如第 1 行)。但是,假设他们这样做了(也许 df1 中有多次出现的行),您可以:

x <- rep(0, length(df1[, 1]) #initialise
for(r1 in 1:length(df1[, 1])){
  for(r2 in 1:length(df2[, 1])){
    if(identical(df1[r1,], df2[r2,])){
      x[r1] <- r2
      break
    }
  }
}

df1 中实际上不在 df2 中的任何行都将保留值为 0。也许不是最快的解决方案 - 您需要重复该过程多少次?

【讨论】:

  • 不知道为什么它不起作用:我得到了全 0,即使我检查过并且我有匹配的行,所以它不应该。
  • 它对我有用,但奇怪的是,每次我重新生成 df2 并重新运行时,我得到的 x 的结果相似但不相同。可能是sample 的问题?
  • 不知道。我正在使用我的原始数据框,结果全为 0……不明白为什么,抱歉。
【解决方案4】:

我意识到这是对一个非常老问题的新答案,但有什么问题

match(data.frame(t(df1)), data.frame(t(df2))) 

??

输出是

[1] NA NA  2 NA NA NA  4  1 NA  3

所以有NAs 而不是0s 但除此之外我认为它是单行的,不需要do.call() 或任何东西。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多