Access to column name of dataframe with *apply function

北慕城南 提交于 2019-12-23 16:40:13

问题


I need to make tutorial for beginner using the R *apply function (without using reshape or plyr package in a first time)

I try to lapply (because i read apply is not good for dataframe) a simple function to this dataframe, and i want to use named column to access data :

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)})

I have this error $ operator is invalid for atomic vectors, it is probably because the dataframe is modified by laply ?... is there a best way to do that with $ named column?

I resolve my first question with @DWin answer. But i have another problem, misunderstanding, with mixed dataframe (numeric + character) :

In my new use case, i use two function to compute distance, because my objective is to compare a distance Point between all of other Point.

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)})

回答1:


In this case apply is what you need. All of the data columns are of the same type and you don't have any worries about loosing attributes, which is where apply causes problems. You will need to write your function differently so it just takes one vector of length 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

If you wanted to use the names of the columns in 'data' then they need to be spelled correctly:

 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

Your updated (and very different) question is easy to resolve. When you use apply it coerces to the lowest common mode denominator, in this case 'character'. You have two choices: either 1) add as.numeric to all of your arguments inside the functions, or 2) only send the columns that are needed which I will illustrate:

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

I really do not like how you are passing parameters to this function. Using "[" and "$" within the formals list "just looks wrong." And you should know that "df" will not be a dataframe, but rather a vector. Because it's not a dataframe (or a list) you should alter the function inside so that it uses "[" rather than "[[". Since you only want two of the coordinates, then only pass the two (numeric) ones that you would be using.




回答2:


As a side note, generally, its best to avoid using data as a variable name since its a function in base 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 feeds a single column of the data.frame to the function.

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

Instead, you want apply. But it feeds a single row as a vector, which doesn't use the $ operator. Instead, you can index directly:

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

Or rewrite the function to take the positional arguments as additional arguments.

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)

However, the best solution would be to vectorize your function completely:

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


来源:https://stackoverflow.com/questions/9624866/access-to-column-name-of-dataframe-with-apply-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!