使用sapply将多列作为变量

37

我有一个dataframe,我想应用一个函数来获取三列值之间的最小差。

#dataset
df <- data.frame(a= sample(1:100, 10),b = sample(1:100, 10),c= sample(1:100, 10))

#function
minimum_distance <- function(a,b,c)
{
  dist1 <- abs(a-b)
  dist2 <- abs(a-c)
  dist3 <- abs(b-c)
  return(min(dist1,dist2,dist3))
}

我正在寻找类似于:

df$distance <- sapply(df, function(x) minimum_distance(x$a,x$b,x$c) )
## errormessage
Error in x$a : $ operator is invalid for atomic vectors

虽然我可以使用ddply:

df2 <- ddply(df,.(a),function(r) {data.frame(min_distance=minimum_distance(r$a,r$b, r$c))}, .drop=FALSE)

这个方法不能保留所有列,有什么建议吗?

编辑:最终我使用了以下方法:

df$distance <- mapply(minimum_distance, df$a, df$b, df$c)
4个回答

61
尝试使用mapply():
qq <- mapply(minimum_distance, df$a, df$b, df$c)

哪一个是最快的?或更有效率? - Bharath

6
尝试这个:
do.call("mapply", c(list(minimum_distance), df))

但是你可以编写矢量化版本:
pminimum_distance <- function(a,b,c)
{
 dist1 <- abs(a-b)
 dist2 <- abs(a-c)
 dist3 <- abs(b-c)
 return(pmin(dist1,dist2,dist3))
}
pminimum_distance(df$a, df$b, df$c)

# or
do.call("pminimum_distance", df)

这很聪明,但比mapply略微不太直接。 - zach

6

我知道这个问题已经有答案了,但是我会采取不同的方法,它可以处理任意数量的列,并且更具普适性,使用外连接的方法:

vdiff <- function(x){
    y <- outer(x, x, "-")
    min(abs(y[lower.tri(y)]))
}

apply(df, 1, vdiff)

我认为这种方法更加简洁和灵活。
编辑:根据Zach的评论,我提出这个更为形式化的函数,它可以处理数据框中非数值列,通过删除它们并仅对数值列进行操作。
cdif <- function(dataframe){
    df <- dataframe[, sapply(dataframe, is.numeric)]
    vdiff <- function(x){
        y <- outer(x, x, "-")
        min(abs(y[lower.tri(y)]))
    }
    return(apply(df, 1, vdiff))
}

#TEST it out
set.seed(10)
(df <- data.frame(a = sample(1:100, 10), b = sample(1:100, 10), 
    c = sample(1:100, 10), d =  LETTERS[1:10]))

cdif(df)

不错的想法。但是我的真实数据框不是矩阵,这个方法能否修改以适用于包含文本列的数据框呢?例如像 outer(x,x,"-", drop_string=T) 这样的操作? - zach
函数outer并不一定意味着你正在处理矩阵。它只需要两个向量和一个函数,然后生成这两个向量的所有可能组合的矩阵。在这里,我只是将相同的向量(行)提供给了outer两次,并使用减法-运算符。我对解决方案进行了一些修改,使其成为一个自包含的函数,可作用于数据框并排除任何非数字内容。outer可以非常强大,我只希望我能记得更多地使用它。至于drop_string = T?没有这样的运气,但是sapplyis.numeric查询很好用。 - Tyler Rinker
非常好。我同意outer函数非常强大,对于更大的矩阵,使用它比指定每个列或值更为便捷。 - zach
请注意:由于此答案更具普适性,因此可能会更慢,不确定速度有多大问题(即数据集有多大)。 - Tyler Rinker
在这种情况下,速度不是问题,但我会记住这一点。谢谢 Tyler。 - zach

2

最好编写一个函数,然后在向量上使用mapply:

 f1 <- function(a,b,c){
 d =abs(a-b)
 e =abs(b-c)
 f= abs(c-a)
 return(pmin(d,e,f))
 }

 qq <- mapply(f1, df$a, df$b, df$c)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接