假设我们在R中有两个数据框,df.A
和df.B
,由此定义:
bin_name <- c('bin_1','bin_2','bin_3','bin_4','bin_5')
bin_min <- c(0,2,4,6,8)
bin_max <- c(2,4,6,8,10)
df.A <- data.frame(bin_name, bin_min, bin_max, stringsAsFactors = FALSE)
obs_ID <- c('obs_1','obs_2','obs_3','obs_4','obs_5','obs_6','obs_7','obs_8','obs_9','obs_10')
obs_min <- c(6.5,0,8,2,1,7,5,6,8,3)
obs_max <- c(7,3,10,3,9,8,5.5,8,10,4)
df.B <- data.frame(obs_ID, obs_min, obs_max, stringsAsFactors = FALSE)
df.A
定义了区间范围,而df.B
由观察行组成,其最小值和最大值可能完全或可能不完全落在df.A
中定义的区间内。
我们希望生成一个长度为nrow(df.B)
的新向量,其中包含df.A
的行索引,对应于每个观察完全落入的bin。如果观察跨越了一个箱子落在其上或部分在它之外,则它不能被分配到一个箱子并且应该返回NA
(或类似的东西)。
在上面的例子中,正确的输出向量是:
bin_rows <- c(4, NA, 5, 2, NA, 4, 3, 4, 5, 2)
我使用sapply
:
bin_assignments <- sapply(1:nrow(df.B), function(i) which(df.A$bin_max >= df.B$obs_max[i] & df.A$bin_min <= df.B$obs_min[i])) #get bin assignments for every observation
bin_assignments[bin_assignments == "integer(0)"] <- NA #replace "integer(0)" entries with NA
bin_assignments <- do.call("c", bin_assignments) #concatenate the output of the sapply call
几个月前,我发现了一个简单的单行解决方案,没有使用apply函数。但是,我忘记了我是如何做到的,而且我无法重新发现它!解决方案可能涉及match()
或which()
。有什么想法吗?
答案 0 :(得分:1)
1)使用SQL可以在一个语句中轻松完成:
library(sqldf)
sqldf('select a.rowid
from "df.B" b
left join "df.A" a on obs_min >= bin_min and obs_max <= bin_max')
rowid
1 4
2 NA
3 5
4 2
5 NA
6 4
7 3
8 4
9 5
10 2
2)合并/ 我们可以使用merge
和by
在两个语句中执行此操作。没有使用包裹。
这确实有一个缺点,即它实现了SQL解决方案不需要做的大型连接。
请注意,问题中定义的df.B
obs_10
是第二级而不是第10级。如果obs_10
是第10级,则by
的第二个参数可能只是m$obs_ID
,因此首先修复输入可以简化它。
m <- merge(df.B, df.A)
stack(by(m, as.numeric(sub(".*_", "", m$obs_ID)),
with, c(which(obs_min >= bin_min & obs_max <= bin_max), NA)[1]))
,并提供:
values ind
1 4 1
2 NA 2
3 5 3
4 2 4
5 NA 5
6 4 6
7 3 7
8 4 8
9 5 9
10 2 10
3)sapply 请注意,使用(2)中的c(..., NA)[1]
技巧,我们可以将问题中的sapply
解决方案简化为一个语句:
sapply(1:nrow(df.B), function(i)
c(which(df.A$bin_max >= df.B$obs_max[i] & df.A$bin_min <= df.B$obs_min[i]), NA)[1])
,并提供:
[1] 4 NA 5 2 NA 4 3 4 5 2
3a)mapply 使用mapply
的更好的变体(@ 3)由@Ronak Shah`在评论中给出:
mapply(function(x, y) c(which(x >= df.A$bin_min & y <= df.A$bin_max), NA)[1],
df.B$obs_min,
df.B$obs_max)
4)外部这是另一个不使用包的语句解决方案。
seq_len(nrow(df.A)) %*%
(outer(df.A$bin_max, df.B$obs_max, ">=") & outer(df.A$bin_min, df.B$obs_min, "<="))
,并提供:
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,] 4 0 5 2 0 4 3 4 5 2