使用另一个数据帧的起点和停止点对数据帧进行子集化?
Subset a dataframe using start and stop points from another dataframe?
我有一个数据框 df 有 3 列 id,第一个和最后一个
id <- c(27,27,134,134)
first <- c(14,20,9,16)
last <- c(17,24,13,20)
df <- as.data.frame(cbind(id,first,last))
df
每一行对应于我要保留的另一个数据框中的一大块数据。
first 和 last 表示相关块的第一帧和最后一帧
我想用它来子集结构如下的其他数据框 dat
dat_id <- c(rep(27, 30), rep(134,30))
dat_frame <- c(seq(1:30), seq(1:30))
dat_data <- c(sample(1:60))
dat <- as.data.frame(cbind(dat_id,dat_frame,dat_data))
dat
我知道提取相关部分的唯一方法是使用如下的 for 循环(这会产生预期的输出),但我认为这是一种非常低效的方法。有什么更好的方法?
#header row
new_df <- data.frame(id = numeric(), frame = numeric(), data = numeric())
#populate
for (i in (seq (1:nrow(df)))){
new_df <- rbind(new_df, subset(dat, dat_id == df[i,"id"])[df[i,"first"]:df[i,"last"],])
}
new_df
我们可以为此使用非相等连接。会更快更有效率
library(data.table)
setDT(dat)[, newcol := dat_frame][df, on = .(dat_id = id,
newcol >= first, newcol <=last)][, .(dat_id, dat_frame, dat_data)]
# dat_id dat_frame dat_data
# 1: 27 14 26
# 2: 27 15 56
# 3: 27 16 30
# 4: 27 17 49
# 5: 27 20 23
# 6: 27 21 37
# 7: 27 22 7
# 8: 27 23 40
# 9: 27 24 12
#10: 134 9 57
#11: 134 10 35
#12: 134 11 31
#13: 134 12 53
#14: 134 13 38
#15: 134 16 15
#16: 134 17 14
#17: 134 18 33
#18: 134 19 54
#19: 134 20 43
或者另一种选择是fuzzyjoin
library(fuzzyjoin)
library(dplyr)
dat %>%
mutate(newcol = dat_frame) %>%
fuzzy_left_join(df, by = c("dat_id" = 'id', 'newcol' = 'first',
'newcol' = 'last'), match_fun = list(`==`, `>=`, `<=`)) %>%
na.omit %>%
select(dat_id, dat_frame, dat_data)
# dat_id dat_frame dat_data
#14 27 14 26
#15 27 15 56
#16 27 16 30
#17 27 17 49
#20 27 20 23
#21 27 21 37
#22 27 22 7
#23 27 23 40
#24 27 24 12
#39 134 9 57
#40 134 10 35
#41 134 11 31
#42 134 12 53
#43 134 13 38
#46 134 16 15
#47 134 17 14
#48 134 18 33
#49 134 19 54
#50 134 20 43
或使用base R
out <- do.call(rbind, Map(function(x, y) do.call(rbind,
Map(function(u, v) subset(x, dat_frame >= u & dat_frame <= v),
y$first, y$last)), split(dat, dat$dat_id), split(df, df$id)))
row.names(out) <- NULL
out
# dat_id dat_frame dat_data
#1 27 14 26
#2 27 15 56
#3 27 16 30
#4 27 17 49
#5 27 20 23
#6 27 21 37
#7 27 22 7
#8 27 23 40
#9 27 24 12
#10 134 9 57
#11 134 10 35
#12 134 11 31
#13 134 12 53
#14 134 13 38
#15 134 16 15
#16 134 17 14
#17 134 18 33
#18 134 19 54
#19 134 20 43
注意:以上所有解决方案都有效
此外,请注意另一个 post 中的解决方案给出了 Error
left_join(dat, df, by = c("dat_id" = "id")) %>%
filter(between(dat_frame, first, last)) %>%
select(-first, -last)
#Error: Expecting a single value: [extent=120].
注意:接受的答案是错误的,并且给出了错误。
使用 dplyr
我们可以对 dat
和 df
和 select 执行 left_join
只有那些位于 between
first
和 last
各自的 id
.
library(dplyr)
left_join(dat, df, by = c("dat_id" = "id")) %>%
filter(between(dat_frame, first, last)) %>%
select(-first, -last)
或者在基础 R 中使用相同的逻辑
subset(merge(dat, df, by.x = "dat_id", by.y = "id", all.x = TRUE),
dat_frame >= first & dat_frame <= last)
这可以通过 sql 中的复杂连接来完成。这避免了仅基于 id 加入然后将其削减来创建大型中间数据框。
library(sqldf)
sqldf("
select dat.*
from dat
join df on dat.dat_id = df.id and
dat.dat_frame between df.first and df.last
")
更新
问题中的示例发生了变化,假设新示例的解决方案已得到简化。
我有一个数据框 df 有 3 列 id,第一个和最后一个
id <- c(27,27,134,134)
first <- c(14,20,9,16)
last <- c(17,24,13,20)
df <- as.data.frame(cbind(id,first,last))
df
每一行对应于我要保留的另一个数据框中的一大块数据。 first 和 last 表示相关块的第一帧和最后一帧 我想用它来子集结构如下的其他数据框 dat
dat_id <- c(rep(27, 30), rep(134,30))
dat_frame <- c(seq(1:30), seq(1:30))
dat_data <- c(sample(1:60))
dat <- as.data.frame(cbind(dat_id,dat_frame,dat_data))
dat
我知道提取相关部分的唯一方法是使用如下的 for 循环(这会产生预期的输出),但我认为这是一种非常低效的方法。有什么更好的方法?
#header row
new_df <- data.frame(id = numeric(), frame = numeric(), data = numeric())
#populate
for (i in (seq (1:nrow(df)))){
new_df <- rbind(new_df, subset(dat, dat_id == df[i,"id"])[df[i,"first"]:df[i,"last"],])
}
new_df
我们可以为此使用非相等连接。会更快更有效率
library(data.table)
setDT(dat)[, newcol := dat_frame][df, on = .(dat_id = id,
newcol >= first, newcol <=last)][, .(dat_id, dat_frame, dat_data)]
# dat_id dat_frame dat_data
# 1: 27 14 26
# 2: 27 15 56
# 3: 27 16 30
# 4: 27 17 49
# 5: 27 20 23
# 6: 27 21 37
# 7: 27 22 7
# 8: 27 23 40
# 9: 27 24 12
#10: 134 9 57
#11: 134 10 35
#12: 134 11 31
#13: 134 12 53
#14: 134 13 38
#15: 134 16 15
#16: 134 17 14
#17: 134 18 33
#18: 134 19 54
#19: 134 20 43
或者另一种选择是fuzzyjoin
library(fuzzyjoin)
library(dplyr)
dat %>%
mutate(newcol = dat_frame) %>%
fuzzy_left_join(df, by = c("dat_id" = 'id', 'newcol' = 'first',
'newcol' = 'last'), match_fun = list(`==`, `>=`, `<=`)) %>%
na.omit %>%
select(dat_id, dat_frame, dat_data)
# dat_id dat_frame dat_data
#14 27 14 26
#15 27 15 56
#16 27 16 30
#17 27 17 49
#20 27 20 23
#21 27 21 37
#22 27 22 7
#23 27 23 40
#24 27 24 12
#39 134 9 57
#40 134 10 35
#41 134 11 31
#42 134 12 53
#43 134 13 38
#46 134 16 15
#47 134 17 14
#48 134 18 33
#49 134 19 54
#50 134 20 43
或使用base R
out <- do.call(rbind, Map(function(x, y) do.call(rbind,
Map(function(u, v) subset(x, dat_frame >= u & dat_frame <= v),
y$first, y$last)), split(dat, dat$dat_id), split(df, df$id)))
row.names(out) <- NULL
out
# dat_id dat_frame dat_data
#1 27 14 26
#2 27 15 56
#3 27 16 30
#4 27 17 49
#5 27 20 23
#6 27 21 37
#7 27 22 7
#8 27 23 40
#9 27 24 12
#10 134 9 57
#11 134 10 35
#12 134 11 31
#13 134 12 53
#14 134 13 38
#15 134 16 15
#16 134 17 14
#17 134 18 33
#18 134 19 54
#19 134 20 43
注意:以上所有解决方案都有效
此外,请注意另一个 post 中的解决方案给出了 Error
left_join(dat, df, by = c("dat_id" = "id")) %>%
filter(between(dat_frame, first, last)) %>%
select(-first, -last)
#Error: Expecting a single value: [extent=120].
注意:接受的答案是错误的,并且给出了错误。
使用 dplyr
我们可以对 dat
和 df
和 select 执行 left_join
只有那些位于 between
first
和 last
各自的 id
.
library(dplyr)
left_join(dat, df, by = c("dat_id" = "id")) %>%
filter(between(dat_frame, first, last)) %>%
select(-first, -last)
或者在基础 R 中使用相同的逻辑
subset(merge(dat, df, by.x = "dat_id", by.y = "id", all.x = TRUE),
dat_frame >= first & dat_frame <= last)
这可以通过 sql 中的复杂连接来完成。这避免了仅基于 id 加入然后将其削减来创建大型中间数据框。
library(sqldf)
sqldf("
select dat.*
from dat
join df on dat.dat_id = df.id and
dat.dat_frame between df.first and df.last
")
更新
问题中的示例发生了变化,假设新示例的解决方案已得到简化。