如何使用基于现有数据框的值和 R 中的数字向量范围创建新数据帧



我有一个 96 x 48 的数据帧 df。第一列是标识字段(字符),第 2 - 48 列是数值。我还有两个数字向量,每个向量有 96 个元素,由对应于每行的上限和下限组成。

我想创建一个具有相同列 1 的新数据帧,但对于第 2-48 列,我想查看该值是否在每行的两个向量中的值之间。然后,如果是,我希望在新数据框中有 1,如果不是,则为 0(各种布尔值)。

例:

DF:

1 2 3 4 .. 48 A 7 11 15 58 b 6 9 13 46 c 8 14 20 73

向量:

上部: 24, 35, 22, 63 下限: 10, 11, 12, 11

返回:

1 2 3 4 .. 48  a 0 1 1 0(在上[1]和下[1]之间) b 0 0 1 0(在上[2]和下[2]之间) c 0 1 1 0 ...

我想在没有循环的情况下执行此操作,因为我很确定有一种方法可以做到这一点,但我似乎找不到它。

一种使用 dplyr 的方法:

# Data
df <- data.frame(id=letters[1:3], col2=c(7,6,8), col3=c(11,9,14), col4=c(15,13,20), col48=c(58,46,73))
# chain of operations
library(dplyr)
df %>%
mutate(upper = c(24, 35, 22), lower = c(10, 11, 12)) %>%
mutate_at(paste0("col", c(2:4, 48)), funs(.>=lower & .<=upper)) %>%
mutate_at(paste0("col", c(2:4, 48)), as.integer) %>%
select(-lower, -upper)

输出:

col1 col2 col3 col4 col48
1    a    0    1    1     0
2    b    0    0    1     0
3    c    0    1    1     0

既然你说其他变量是数字,那么我们可以这样做:

ifelse(t(upper.bounds-t(df[-1])>0&lower.bounds-t(df[-1])<0),1,0)
c2 c3 c4 c48
[1,]  0  0  1   0
[2,]  0  0  1   0
[3,]  0  1  1   0

不需要lapplyforloop其中数据:

df=read.table(text=" c1  c2  c3  c4 c48
a  7  11 15   58
b  6  9  13   46
c  8  14 20   73 
",h=T)

可以通过遍历所有列的lappy使用隐式循环来避免显式for循环。我认为,如果您遍历列,但仅当您遍历行时,从性能的角度来看,循环并不重要(因为 R 将列的元素作为向量存储在连续内存位置中,以便性能最佳,但每行的元素分布在内存位置上,这会导致性能损失循环第 1 行 1 x 1):

df <- data.frame(c1 = c(7, 6, 8), c2 = c(11, 9, 14), c3 = c(15, 13, 20), c48 = c(58, 46, 73))
df
lower.bounds <- c(10, 11, 12) # , 11)
upper.bounds <- c(24, 35, 22) # , 63)
res <- lapply(df, function(col) {ifelse(col >= lower.bounds & col <= upper.bounds, 1, 0)})
as.data.frame(res)
# c1 c2 c3 c48
# 1  0  1  1   0
# 2  0  0  1   0
# 3  0  1  1   0

另一个可能更简单的解决方案可能是:

df <- data.frame(c1 = c(7, 6, 8), 
c2 = c(11, 9, 14), 
c3 = c(15, 13, 20), 
c48 = c(58, 46, 73))
lower.bounds <- c(10, 11, 12)
upper.bounds <- c(24, 35, 22)
ifelse(upper.bounds > df[] & lower.bounds < df[], 1, 0)
# Result:
#       c1 c2 c3 c48
#  [1,]  0  1  1   0
#  [2,]  0  0  1   0
#  [3,]  0  1  1   0

as.data.frame(ifelse(upper.bounds > df[] & lower.bounds < df[], 1, 0))
# Result:
# 
#    c1 c2 c3 c48
#  1  0  1  1   0
#  2  0  0  1   0
#  3  0  1  1   0

另一种选择是只对列使用应用。我认为它非常简单干净。

df <- data.frame(V2=c(7,6,8), V3=c(11,9,14), V4=c(15,13,20), V48=c(58,46,73))
upper <- c(24, 35, 22)
lower <- c(10, 11, 12)
data.frame(apply(df,2,function(x)((upper>=x)*(x>=lower))))
V2 V3 V4 V48
1  0  1  1   0
2  0  0  1   0
3  0  1  1   0

编辑:在MKR评论之后,我变得好奇,不得不测试性能。如果对如何以不同的方式衡量它有任何建议,请发表评论。

df <- data.frame(V2=c(7,6,8), V3=c(11,9,14), V4=c(15,13,20), V48=c(58,46,73))
upper <- c(24, 35, 22)
lower <- c(10, 11, 12)
start.time <- Sys.time()
data.frame(apply(df,2,function(x)((upper>=x)*(x>=lower))))
#V2 V3 V4 V48
#1  0  1  1   0
#2  0  0  1   0
#3  0  1  1   0
Sys.time()-start.time
#Time difference of 0.0146079 secs
start.time <- Sys.time()
data.frame(apply(df,2,function(x)(as.numeric((upper>=x)&(x>=lower)))))
#V2 V3 V4 V48
#1  0  1  1   0
#2  0  0  1   0
#3  0  1  1   0
Sys.time()-start.time
#Time difference of 0.0124476 secs
start.time <- Sys.time()
data.frame(ifelse(upper > df[] & lower < df[], 1, 0))
#V2 V3 V4 V48
#1  0  1  1   0
#2  0  0  1   0
#3  0  1  1   0
Sys.time()-start.time
#Time difference of 0.008914948 secs

最新更新