使用*apply函数访问数据框的列名

5

我需要制作一个针对初学者的教程,介绍如何使用R中的*apply函数(首先不使用reshape或plyr包)。

我尝试使用lapply(因为我读到apply不适用于数据框)将一个简单的函数应用于这个数据框,并且我想使用命名列来访问数据:

fDist <- function(x1,x2,y1,y2) {
  return (0.1*((x1 - x2)^2 + (y1-y2)^2)^0.5)  
}

data <- read.table(textConnection("X1 Y1 X2 Y2
 1 3.5 2.1 4.1 2.9
 2 3.1 1.2 0.8 4.3
 "))

data$dist <- lapply(data,function(df) {fDist(df$X1 , df$X2 , df$Y1 , df$Y2)})

我有这个错误$ operator is invalid for atomic vectors,很可能是因为数据框被laply修改了?是否有更好的方法使用$命名列来解决它?
我通过@DWin的答案解决了第一个问题。但我有另一个问题,对于混合数据框(数值+字符)不理解:
在我的新用例中,我使用两个函数来计算距离,因为我的目标是比较点之间的距离。
data2 <- read.table(textConnection("X1 Y1 X2 Y2
     1 3.5 2.1 4.1 2.9
     2 3.1 1.2 0.8 4.3
     "))

data2$char <- c("a","b")

fDist <- function(x1,y1,x2,y2) {
 return (0.1*((x1 - x2)^2 + (y1-y2)^2)^0.5) 
}

fDist2 <- function(fixedX,fixedY,vec) { 
 fDist(fixedX,fixedY,vec[['X2']],vec[['Y2']])
}

# works with data (dataframe without character), but not with data2 (dataframe with character)
#ok
data$f_dist <- apply(data, 1, function(df) {fDist2(data[1,]$X1,data[1,]$Y1,df)})
#not ok
data2$f_dist <- apply(data2, 1, function(df) {fDist2(data2[1,]$X1,data2[1,]$Y1,df)})

2
如果您正在循环遍历数据框的列,这就是lapply所做的,那么内部函数每次只会看到一列。 - IRTFM
3个回答

11

在这种情况下,您需要使用apply函数。所有数据列都是相同类型的,并且您不必担心失去属性,这就是apply函数引起问题的地方。因此,您需要以不同的方式编写函数,使其仅接受一个长度为4的向量:

 fDist <- function(vec) {
   return (0.1*((vec[1] - vec[2])^2 + (vec[3]-vec[4])^2)^0.5)  
                        }
 data$f_dist <- apply(data, 1, fDist)
 data
   X1  Y1  X2  Y2    f_dist
1 3.5 2.1 4.1 2.9 0.1843909
2 3.1 1.2 0.8 4.3 0.3982462

如果您想使用"data"中列的名称,则它们需要正确拼写:

 fDist <- function(vec) {
   return (0.1*((vec['X1'] - vec['X2'])^2 + (vec['Y1']-vec['Y2'])^2)^0.5)  
                        }
 data$f_dist <- apply(data, 1, fDist)
 data
#--------    
X1  Y1  X2  Y2    f_dist
1 3.5 2.1 4.1 2.9 0.1000000
2 3.1 1.2 0.8 4.3 0.3860052

您更新后(也非常不同)的问题很容易解决。当您使用apply时,它会强制转换为最低公共模式,此处为'character'。您有两个选择:要么1)在函数内部将所有参数添加as.numeric,要么2)仅发送所需的列,我将说明:

data2$f_dist <- apply(data2[ , c("X2", "Y2") ], 1, function(coords) 
                                       {fDist2(data2[1,]$X1,data2[1,]$Y1, coords)} )

我真的不喜欢你传递参数给这个函数的方式。在形参列表中使用“ [”和“ $”看起来很奇怪。你应该知道“ df”不会是一个数据框,而只是一个向量。因为它不是数据框(或列表),所以你应该更改函数内部的方法,使其使用“ [”而不是“ [[”。因为你只想要两个坐标,所以只传递你将要使用的两个(数值)坐标。


我在将数据框转换为fDist时遇到了一些问题,不明白原因:fDist2 <- function(X1,X2,columnVector) {fDist(X1,X2,as.numeric(columnVector[["X"]]),as.numeric(columnVector[["Y"]]))}apply(data99_07,1, function(df) { fDist2 (data99_07[data99_07$CODCOM==75101,]$X,data99_07[data99_07$CODCOM==75101,]$Y,df)})我需要进行转换,因为匿名函数返回一个字符向量 :/ - reyman64
如果一个列vec的类别是"factor",那么推荐的方法是使用as.numeric(as.character(vec))将其转换为数字类型。你不能仅仅使用as.numeric(vec)并获得可解释的结果。 - IRTFM
在匿名函数之前,columnVector是数值型的,在匿名函数之后变成了字符向量,因此我需要将其转换为数值型以进行计算,那么是否可能应用或匿名函数使向量进行隐式转换呢? - reyman64
如果一个向量的类别是“character”,那么只需使用as.numeric(colVec)就可以成功地将数值提供给任何函数。但是,如果它是一个因子(你必须检查),你需要使用“双重函数包装”方法。如果你不知道如何检查,双重包装更安全,即... class(colVec) - IRTFM
谢谢您的回答,但我认为我的问题不够清晰,我已经更新了我的帖子以便更好地理解。 - reyman64
它并不是不清楚,而是过于简化了。在R中,参数的模式非常重要,你没有提供一个“混合”模式的示例。 - IRTFM

6
作为一则附注,通常最好避免使用data作为变量名,因为它是R基础函数之一。
dat <- read.table(textConnection("X1 Y1 X2 Y2
 1 3.5 2.1 4.1 2.9
 2 3.1 1.2 0.8 4.3
 "))

lapply 只将数据框中的单个列传给函数。

lapply(dat, function(df) print(df))

相反,你需要使用 apply。但它将一行作为向量进行处理,不使用 $ 操作符。相反,你可以直接索引:

apply(dat, 1, function(vec) {fDist(vec[1] , vec[3] , vec[2] , vec[4])})

或者重写该函数,将位置参数作为附加参数传入。

fDist <- function(vec, pos1, pos2, pos3, pos4) {
    return (0.1*((vec[pos1] - vec[pos2])^2 + (vec[pos3]-vec[pos4])^2)^0.5)
}

apply(dat, 1, fDist, pos1=1, pos2=3, pos3 = 2, pos4=4)

然而,最好的解决方案是完全向量化您的函数:

fDist <- function(df) {
   return (0.1*((df$X1 - df$X2)^2 + (df$Y1-df$Y2)^2)^0.5)  
}

0
对于以后遇到这个问题的任何人来说,接受答案中建议使用的vec['X1']方法确实有效,但它会丢失X1的数据类型,并使所有内容变为chr。更好的解决方案是使用lapply()按名称访问列并保持数据类型。就像下面这样:
lapply(1, function(i, df) {fDist2(df[1,]$X1,df[1,]$Y1,df)}, df=data2)[[1]]

在这里,lapply()中,i是必须的,然后只需将你的数据框data2作为附加参数df传递进去,然后你就可以在函数(){}内部使用df$any_column_you_want来引用任何列。

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