如何从R数据表中找到特定值以外的最小值?
例如,数据表中可能存在零,目标是找到最小非零值。
我尝试将sapply
与min
一起使用,但我不确定如何指定我们拥有的额外条件,以使最小值不等于某个值。
更一般地说,我们如何从数据表中找到不等于可能值列表中任何元素的最小值?
答案 0 :(得分:5)
如果您希望在排除该向量中的某些值时从向量中找到最小值,则可以使用%in%
:
v <- c(1:10) # values 1 .. 10
v.exclude <- c(1, 2) # exclude the values 1 and 2 from consideration
min.exclude <- min(v[!v %in% v.exclude])
如果您使用数据表/框架中的列,则逻辑不会发生太大变化。在这种情况下,您可以使用适当的列替换向量v
。如果列表中包含排除的值,则可以展平它以生成v.exclude
向量。
答案 1 :(得分:2)
假设您正在使用data.frame
df <- data.frame(a = c(0,2,3,2,1,2,3,4,5,6),
b = c(10,10,20,20,30,30,40,40,50,60))
从我们的最小搜索中排除的值
exclude <- c(0,1,2,3)
我们可以找到列a
的最小值,不包括我们的exclude
向量
## minimum from column a
min(df[!df$a %in% exclude,]$a)
# [1] 4
或来自b
exclude <- c(10, 20, 30, 40)
min(df[!df$b %in% exclude,]$b)
# [1] 50
返回与最小值对应的行
df[df$b == min( df[ !df$b %in% exclude, ]$b ),]
# a b
# 9 5 50
<强>更新强>
要找到多行的最小值,我们可以这样做:
## values to exclude
exclude_a <- c(0,1)
exclude_b <- c(10)
## exclude rows/values from each column we don't want
df2 <- df[!(df$a %in% exclude_a) & !(df$b %in% exclude_b),]
## order the data
df3 <- df2[with(df2, order(a,b)),]
## take the first row
df3[1,]
# > df3[1,]
# a b
#4 2 20
更新2
要从多个列中进行选择,我们可以在@akrun显示时对其进行迭代,或者我们可以使用expression
和eval
在我们的[
操作中构建我们的子集公式
exclude <- c(0,1,2, 10)
## construct a formula/expression using the column names
n <- names(df)
expr <- paste0("(", paste0(" !(df$", n, " %in% exclude) ", collapse = "&") ,")")
# [1] "( !(df$a %in% exclude) & !(df$b %in% exclude) )"
expr <- parse(text=expr)
df2 <- df[eval(expr),]
## order and select first row as before
df2 <- df2[with(df2, order(a,b)),]
df2 <- df2[1,]
如果我们想使用data.table
:
library(data.table)
setDT(df)[ eval(expr) ][order(a, b),][1,]
比较方法
library(microbenchmark)
fun_1 <- function(x){
df2 <- x[eval(expr),]
## order and select first row as before
df2 <- df2[with(df2, order(a,b)),]
df2 <- df2[1,]
return(df2)
}
fun_2 <- function(x){
df2 <- setDT(x)[ eval(expr) ][order(a, b),][1,]
return(df2)
}
## including @akrun's solution
fun_3 <- function(x){
setDT(df)
MinVal <- vector('list', length(df))
for(j in seq_along(df)){
setkeyv(df, names(df)[j])
MinVal[[j]] <- min(df[!.(exclude)][[j]])
}
return(MinVal)
}
microbenchmark(fun_1(df), fun_2(df), fun_3(df) , times=1000)
# Unit: microseconds
# expr min lq mean median uq max neval
# fun_1(df) 770.376 804.5715 866.3499 833.071 869.2195 2728.740 1000
# fun_2(df) 854.862 893.1220 952.1207 925.200 962.6820 3115.119 1000
# fun_3(df) 1108.316 1148.3340 1233.1268 1186.938 1234.3570 5400.544 1000
答案 2 :(得分:2)
在设置data.table
key
(正如OP提到的关于帖子中数据表的内容)来完成此操作
library(data.table)
setDT(df, key='a')[!.(exclude)]
# a b
#1: 4 40
#2: 5 50
#3: 6 60
如果我们需要'{'
的min
值
min(setDT(df, key='a')[!.(exclude)]$a)
#[1] 4
为了在所有列中找到min
(使用setkey
方法),我们遍历数据集的列,将键设置为每个列,将数据集的子集,获取先前创建的min
对象中的list
值。
setDT(df)
MinVal <- vector('list', length(df))
for(j in seq_along(df)){
setkeyv(df, names(df)[j])
MinVal[[j]] <- min(df[!.(exclude)][[j]])
}
MinVal
#[[1]]
#[1] 4
#[[2]]
#[1] 10
df <- data.frame(a = c(0,2,3,2,1,2,3,4,5,6),
b = c(10,10,20,20,30,30,40,40,50,60))
exclude <- c(0,1,2,3)