在R数据框中插入值的简单查找

15

这是一个看似简单的R语言问题,但我在这里没有看到确切的答案。我有一个数据框(alldata),它看起来像这样:

Case     zip     market
1        44485   NA
2        44488   NA
3        43210   NA

现在有超过350万条记录。

然后,我有第二个数据框,“zipcodes”。

market    zip
1         44485
1         44486
1         44488
...       ... (100 zips in market 1)
2         43210
2         43211
...       ... (100 zips in market 2, etc.)

我想基于alldata$zip与邮政编码数据框中相应的值匹配,找到每个案例中alldata$market的正确值。我只是在寻找正确的语法,并且像往常一样非常感谢您的帮助。


1
你正在寻找merge。在SO中搜索[r] merge即可找到它。 - Ricardo Saporta
@Ricardo Saporta 在3.5版本中,merge可能会有点慢。 - Tyler Rinker
@TylerRinker,我经常在4亿行上使用merge.data.table,速度非常快。不过你使用qdap也很好。 - Ricardo Saporta
哇,从未处理过如此大的数据。感谢您提供经验丰富的回复。 - Tyler Rinker
5个回答

14

既然你不在意 alldata 中的 market 列,你可以首先使用 drop 去掉它,并且根据 zip 列使用 mergealldatazipcodes 的列合并:

merge(alldata[, c("Case", "zip")], zipcodes, by="zip")

by参数指定了关键标准,如果您有一个复合键(compound key),可以这样做:by=c("zip", "otherfield")


这样做是否会在alldata中不存在的邮政编码处引入NA值? - Rodrigo
默认情况下,您将仅获得两个数据框中都有匹配项的行。这可以通过allall.xall.y选项进行控制。 - Dan Garant

9

我有一个很简单的方法可以解决这个问题:

alldata$market<-with(zipcodes, market[match(alldata$zip, zip)])

如此简单,却如此伟大! - vashts85
2
如果您能解释一下那里发生了什么,那就太好了。 - Rodrigo
从R文档中可以得知:"match返回一个向量,其中包含第一个参数在第二个参数中的(第一个)匹配项的位置"。因此,基本上match返回一个向量,其中包含正确的索引,以访问zipcodes$market,以获取alldata$market的正确市场值。 - Aelian

4

对于如此大的数据集,您可能需要使用环境查找的速度。 您可以按照以下方式使用qdapTools包中的lookup函数:

library(qdapTools)
alldata$market <- lookup(alldata$zip, zipcodes[, 2:1])

或者

alldata$zip %l% zipcodes[, 2:1]

Tyler,我在更大的数据上进行了测试,发现lookup(.)步骤非常耗时(我在一段时间后停止了它)。我将其作为一个gist。我是否做得正确?通过快速调试,发现recoder函数内部的sapply(.)是最耗时的部分。 - Arun
@Arun 感谢你的要点。我在dev版本中加入了一些改进,基于你的发现,这些改进可以显著提高速度。 - Tyler Rinker
只是出于好奇,你有没有测试过data.table来看它与其他的比较。我听说data.table在查找方面做了一些很好的改进,包括字符查找。 - Tyler Rinker
1
我已经用两种方法编辑了这个要点。1)使用连接(虽然在这里真的不需要),2)使用match:=,不过最好在大数据上进行基准测试。希望对你有所帮助。 - Arun
1
是的,从1.9.0+版本开始有了很多令人欣喜的变化。这应该是有说服力的:require(data.table); set.seed(1L); x = sample(paste0("V", 1:1e6)); system.time(ans1 <- data.table:::forderv(x)); system.time(ans2 <- order(x)); identical(ans1, ans2) - Arun

4
这是使用dplyr的方法:
library(tidyverse)
alldata %>%
  select(-market) %>%
  left_join(zipcodes, by="zip")

在我的机器上,它的性能与lookup大致相同。


0

match 的语法有点笨拙。你可能会发现使用 lookup 包更容易。

alldata <- data.frame(Case=1:3, zip=c(44485,44488,43210), market=c(NA,NA,NA))
zipcodes <- data.frame(market=c(1,1,1,2,2), zip=c(44485,44486,44488,43210,43211))
alldata$market <- lookup(alldata$zip, zipcodes$zip, zipcodes$market)
alldata
##   Case   zip market
## 1    1 44485      1
## 2    2 44488      1
## 3    3 43210      2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接