使用多个列作为sapply的变量

时间:2012-04-09 18:56:16

标签: r dataframe apply

我有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 个答案:

答案 0 :(得分:44)

尝试mapply():

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

答案 1 :(得分: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)

答案 2 :(得分:5)

我知道这已经得到了解答,但我实际上采用了一种不同的方法,它采用了任意数量的列,并且使用外部方法更具一般性:

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)

答案 3 :(得分:1)

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

function visitChild(node, array) {
    array[array.length] = { name: node.name, age:node.age };
    if (node.children && node.children.length) {
        node.children.forEach( function(child) { visitChild(child, array); } );
    }
}

function traverseTree(root, list = []) {
    if (root.length) {
        root.forEach( function(node){ visitChild(node, list); });
    }
    return list;
}

console.log( traverseTree(tree) );