data.frame vs vector(colnames,rownames)

时间:2016-12-24 00:20:14

标签: r

我有以下功能:

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)

2 个答案:

答案 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的向量,其中每个向量中唯一元素的名称为labelssapply返回(“简化”)为一个矢量长度为11。

通过切换sapply s的顺序,内部sapply现在返回一个长度为11的未命名向量。外部sapply然后执行k次。在向量的情况下,k是1,返回的向量的名称是labels。在数据框示例中,k为2,返回的2个向量的名称为var1var2

(在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