我有以下功能:
func <- function(scores, labels, thresholds) {
labels <- if (is.data.frame(labels)) labels else data.frame(labels)
sapply(thresholds, function(t) { sapply(labels, function(lbl) { sum(lbl[which(scores >= t)]) }) })
}
我还有以下内容,我将转入func
。
> scores
[1] 0.187 0.975 0.566 0.793 0.524 0.481 0.005 0.756 0.062 0.124
> thresholds
[1] 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
> var1
[1] 1 1 0 0 0 1 0 1 1 1
> df
var1 var2
1 1 0
2 1 1
3 0 0
4 0 0
5 0 0
6 1 1
7 0 1
8 1 1
9 1 1
10 1 0
以下是两个不同的调用两个func
,一个调用labels
作为向量,另一个调用labels
作为data.frame:
> func(scores, var1, thresholds)
labels labels labels labels labels labels labels labels labels labels labels
6 5 3 3 3 2 2 2 1 1 0
> func(scores, df, thresholds)
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
var1 6 5 3 3 3 2 2 2 1 1 0
var2 5 3 3 3 3 2 2 2 1 1 0
为什么“标签”是否在矢量版本中作为colname应用,“var1”和“var2”在data.frame版本中作为rowname应用?
我正在寻找的是矢量版本更像:
> func(scores, var1, thresholds)
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
labels 6 5 3 3 3 2 2 2 1 1 0
要创建上述变量:
scores <- sample(seq(0, 1, 0.001), 10, replace = T)
thresholds <- seq(0, 1, 0.1)
var1 <- sample(c(0, 1), 10, replace = T)
var2 <- sample(c(0, 1), 10, replace = T)
df <- data.frame(var1, var2)
答案 0 :(得分:4)
尝试切换嵌套sapply
的顺序:
func <- function(scores, labels, thresholds) {
labels <- if (is.data.frame(labels)) labels else data.frame(labels)
t(sapply(labels, function(lbl) {
sapply(thresholds, function(t) sum(lbl[which(scores >= t)]))
}))
}
来自?sapply
:
'sapply'是一个用户友好的版本和'lapply'的包装 默认返回一个向量,矩阵,如果'simplify =“array”',则为 数组(如果适用),应用'simplify2array()'。
要了解原始函数中发生的情况,依次考虑每个sapply
可能会有用。
inner sapply(labels, ...)
创建一个长度为k的命名向量(其中k是labels
中的列数 - 因此k在向量的情况下为1,并且数据框示例中的2,其中向量元素的名称由列名称给出(向量大小写中为labels
,数据框示例中为var1
/ var2
。“ / p>
外部 sapply(thresholds, ...)
运行内部sapply
11次,每次都使用不同的t
值。所以在向量的情况下,你将得到11个长度为1的向量,其中每个向量中唯一元素的名称为labels
,sapply
返回(“简化”)为一个矢量长度为11。
通过切换sapply
s的顺序,内部sapply
现在返回一个长度为11的未命名向量。外部sapply
然后执行k次。在向量的情况下,k是1,返回的向量的名称是labels
。在数据框示例中,k为2,返回的2个向量的名称为var1
和var2
。
(在thresholds
向量中命名元素也可能是一项有用的练习;例如thresholds <- setNames(seq(0, 1, 0.1), LETTERS[1:11])
并重新运行func
以查看会发生什么。)
答案 1 :(得分:4)
注意: @ weihuang-wong的答案很棒,解决方案在某些方面比这个更好。但是在答案发布之前我已经写了大部分答案,所以无论如何我决定发布这个答案。
你得到你所做的名字,因为那些是你迭代的东西的名字。但是为什么在第一种情况下得到一个命名向量,在第二种情况下得到一个带有rownames的矩阵?这是一个更简单的案例,可以让您更容易看到。
sapply(1, function(x) sapply(c(a = 1), function(y) y))
# a
# 1
sapply(1, function(x) sapply(c(a = 1, b = 2), function(y) y))
# [,1]
# a 1
# b 2
好的,那么这里发生了什么?让我们把它分解,以便我们看到。
sapply(c(a = 1), function(y) y)
返回一个命名长度为一的向量。
sapply(c(a = 1, b = 2), function(y) y)
返回一个命名的length-two向量。
现在,外部sapply
的工作就是结合这些结果。当它看到内部sapply
返回长度为一的向量时,它将其简化为命名向量。当返回值的长度> 1时,该简化不起作用。 1,所以sapply
简化为矩阵。
因此,如果我们想要一致性,我们需要sapply
来返回矩阵,即使在长度为一的情况下也是如此。我们如何使sapply
保持一致?这是非常困难的。最后,我会在事后将其转换为矩阵。
matrix(sapply(1, function(x) sapply(c(a = 1), function(y) y)), dimnames = list("a"))
# [,1]
# a 1
现在我们了解了正在发生的事情,我们可以将我们学到的知识应用到原始问题中。
func <- function(scores, labels, thresholds) {
labels <- if (is.data.frame(labels)) labels else data.frame(labels)
r <- sapply(thresholds, function(t) { sapply(labels, function(lbl) { sum(lbl[which(scores >= t)]) }) })
if(!is.matrix(r)) r <- matrix(r, nrow = 1, dimnames = list(names(labels)))
r
}
func(scores, df, thresholds)
# [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
# var1 6 5 3 3 3 2 2 2 1 1 0
# var2 5 3 3 3 3 2 2 2 1 1 0
func(scores, var1, thresholds)
# [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
# labels 6 5 3 3 3 2 2 2 1 1 0