我想要关联两个数据帧。
第一个数据框(LOC)在5年内研究了大约200只动物,因此大约有100000个点位置。我需要知道每只动物在研究期间的包装情况,但不幸的是,动物经常更换包装。我需要每个点位置都有一个与之关联的包名称,因为我将基于包而不是个人进行进一步的分析。
第二个数据框(PACK)包含这三百只动物的包附属关系,每行代表一个入口日和退出日。我的真实数据是一个包含约700行的表格,每行代表该动物留在包中的时间。例如,在下面的PACK数据框中,动物“W1”在一年的第一天在SunnyLake中“A”,然后在第200天离开并前往RainyLake并在那里呆到年底“B”(闰年)
由于我有这么多动物和多年的转换,我想知道一种方法告诉R哪些动物属于SunnyLake和RainyLake在LOC表中。
到目前为止,我的方法如下,但速度很慢。我对R很新,所以我认为必须有更快,更优雅的方式来做到这一点。如果您看到更好的解决方案,请告诉我(并记住我几个月前刚开始在R工作)!
我的示例数据:
位置表
LOC <- data.frame(matrix(NA, nrow = 8, ncol = 4))
colnames(LOC) <- c("ID", "Yr", "Dy", "Pack")
LOC$ID <- "W1"
LOC$Yr <- rep(c("A", "B"), each = 4)
LOC$Dy <- c(4, 200, 300, 335, 3, 100, 150, 350)
LOC
ID Yr Dy Pack
1 W1 A 4 NA
2 W1 A 200 NA
3 W1 A 300 NA
4 W1 A 335 NA
5 W1 B 3 NA
6 W1 B 100 NA
7 W1 B 150 NA
8 W1 B 350 NA
包装表
PACK <- data.frame(matrix(nrow = 3, ncol = 5))
colnames(PACK) <- c("ID", "Pack", "EnterDay", "ExitDay", "Yr")
PACK$ID <- "W1"
PACK$Pack <- c("SunnyLake", "RainyLake", "RainyLake")
PACK$EnterDay <- c(1, 201, 1)
PACK$ExitDay <- c(200, 365, 366)
PACK$Yr <- c("A", "A", "B")
PACK
ID Pack EnterDay ExitDay Yr
1 W1 SunnyLake 1 200 A
2 W1 RainyLake 201 365 A
3 W1 RainyLake 1 366 B
我在PACK的每一行中滚动并填充LOC中的“Pack”列的方式
for (i in 1:nrow(PACK)){
cat("LOC$Pack[LOC$ID == \"", as.character(PACK$ID[i]),"\" & LOC$Yr == \"", as.character(PACK$Yr[i]),"\" & LOC$Dy >= ", PACK$EnterDay[i], " & LOC$Dy <= ", PACK$ExitDay[i],"] <- \"", as.character(PACK$Pack[i]),"\"\n", sep="")
}
该命令打印以下内容,然后将其粘贴回控制台并运行。
LOC$Pack[LOC$ID == "W1" & LOC$Yr == "A" & LOC$Dy >= 1 & LOC$Dy <= 200] <- "SunnyLake"
LOC$Pack[LOC$ID == "W1" & LOC$Yr == "A" & LOC$Dy >= 201 & LOC$Dy <= 365] <- "RainyLake"
LOC$Pack[LOC$ID == "W1" & LOC$Yr == "B" & LOC$Dy >= 1 & LOC$Dy <= 366] <- "RainyLake"
生成的LOC表如下所示:
LOC
ID Yr Dy Pack
1 W1 A 4 SunnyLake
2 W1 A 200 SunnyLake
3 W1 A 300 RainyLake
4 W1 A 335 RainyLake
5 W1 B 3 RainyLake
6 W1 B 100 RainyLake
7 W1 B 150 RainyLake
8 W1 B 350 RainyLake
答案 0 :(得分:3)
1)sqldf 试试这个:
library(sqldf)
sqldf("select L.ID, L.Yr, L.Dy, P.Pack from LOC L left join PACK P
on L.Yr = P.Yr and L.ID = P.ID and L.Dy between P.EnterDay and P.ExitDay")
,并提供:
ID Yr Dy Pack
1 W1 A 4 SunnyLake
2 W1 A 200 SunnyLake
3 W1 A 300 RainyLake
4 W1 A 335 RainyLake
5 W1 B 3 RainyLake
6 W1 B 100 RainyLake
7 W1 B 150 RainyLake
8 W1 B 350 RainyLake
2)dplyr 或
library(dplyr)
left_join(LOC, PACK, by = c("ID", "Yr")) %.%
filter((Dy >= EnterDay & Dy <= ExitDay) | is.na(Pack.y)) %.%
select(ID:Dy, Pack.y)
给
ID Yr Dy Pack.y
1 W1 A 4 SunnyLake
2 W1 A 200 SunnyLake
3 W1 A 300 RainyLake
4 W1 A 335 RainyLake
5 W1 B 3 RainyLake
6 W1 B 100 RainyLake
7 W1 B 150 RainyLake
8 W1 B 350 RainyLake
增加:添加第二个解决方案并改进了两者。修复了dplyr解决方案,以便不删除LOC
行PACK
行。
答案 1 :(得分:2)
首先,不要在开头Pack
创建LOC
列;没必要。
LOC <- data.frame(matrix(NA, nrow = 8, ncol = 3))
colnames(LOC) <- c("ID", "Yr", "Dy") # NOTE: No Pack column
LOC$ID <- "W1"
LOC$Yr <- rep(c("A", "B"), each = 4)
LOC$Dy <- c(4, 200, 300, 335, 3, 100, 150, 350)
这是一种使用数据表的方法,对于大型数据集,这种方法可能要快得多。
library(data.table)
LOC <- data.table(LOC, key="ID,Yr")
PACK <- data.table(PACK, key="ID,Yr")
LOC$Pack <-LOC[PACK,all=T][Dy>=EnterDay & Dy<=ExitDay,Pack]
LOC
# ID Yr Dy Pack
# 1 W1 A 4 SunnyLake
# 2 W1 A 200 SunnyLake
# 3 W1 A 300 RainyLake
# 4 W1 A 335 RainyLake
# 5 W1 B 3 RainyLake
# 6 W1 B 100 RainyLake
# 7 W1 B 150 RainyLake
# 8 W1 B 350 RainyLake
这是一个使用数据框的方法(不需要sqldf
)。
M <- merge(LOC,PACK,by=c("ID","Yr"))
is.between <- function(x,low,hi)return(x>=low & x<=hi)
LOC$Pack <- with(M,M[is.between(Dy,EnterDay, ExitDay),]$Pack)