16

When using apply on a data.frame, the arguments are (implicitly) converted to character. An example:

df <- data.frame(v=1:10, t=1:10)
df <- transform(df, t2 = as.POSIXlt(t, origin = "2013-08-13"))
class(df$t2[1])
## [1] "POSIXct" "POSIXt" (correct)

but:

 apply(df, 1, function(y) class(y["t2"]))
 ## [1] "character" "character" "character" "character" "character" "character"
 ## [7] "character" "character" "character" "character"

Is there any way to avoid this conversion? Or do I always have to convert back through as.POSIXlt(y["t2"])?

edit
My df has 2 timestamps (say, t2 and t3) and some other fields (say, v1, v2). For each row with given t2, I want to find k (e.g. 3) rows with t3 closest to, but lower than t2 (and the same v1), and return a statistics over v2 from these rows (e.g. an average). I wrote a function f(t2, v1, df) and just wanted to apply it on all rows using apply(df, 1, function(x) f(y["t2"], y["v1"], df). Is there any better way to do such things in R?

lmo
  • 37,904
  • 9
  • 56
  • 69
ang mo
  • 718
  • 7
  • 11
  • 5
    The real answer here is that you shouldn't use `apply` on a data frame. What are you trying to do? – joran Aug 13 '13 at 16:31
  • 8
    The *conversion* is occurring because your `data.frame` is being coerced to a `matrix`. – Simon O'Hanlon Aug 13 '13 at 16:35
  • Per your edits you really have two different questions (IMO). I'd say ask the second question (Your Edit) with an appropriate data set, what you've tried and your desired output. – Tyler Rinker Aug 13 '13 at 17:35
  • 1
    I often need to step through the rows of a data frame and `apply` comes to mind as a simple way to grab a row and access the columns. E.g. "take column 1 and column 4 from each row and do something with them". Is there a better way to achieve this? Is using `apply` considered inherently bad practise and if so why? – SlowLearner Aug 13 '13 at 19:05
  • 1
    After rephrasing by @SlowLearner (thanks) I realized my original problem is answered by http://stackoverflow.com/questions/2074606/doing-a-plyr-operation-on-every-row-of-a-data-frame-in-r – ang mo Aug 14 '13 at 07:47
  • I use `strtoi` to convert characters back to numbers. – Josh May 31 '19 at 19:41
  • This is truly disgusting default behaviour – jessexknight Aug 05 '20 at 20:28

2 Answers2

8

Let's wrap up multiple comments into an explanation.

  1. the use of apply converts a data.frame to a matrix. This means that the least restrictive class will be used. The least restrictive in this case is character.
  2. You're supplying 1 to apply's MARGIN argument. This applies by row and makes you even worse off as you're really mixing classes together now. In this scenario you're using apply designed for matrices and data.frames on a vector. This is not the right tool for the job.
  3. In ths case I'd use lapply or sapply as rmk points out to grab the classes of the single t2 column as seen below:

Code:

df <- data.frame(v=1:10, t=1:10)
df <- transform(df, t2 = as.POSIXlt(t, origin = "2013-08-13"))

sapply(df[, "t2"], class)
lapply(df[, "t2"], class)

## [[1]]
## [1] "POSIXct" "POSIXt" 
## 
## [[2]]
## [1] "POSIXct" "POSIXt" 
## 
## [[3]]
## [1] "POSIXct" "POSIXt" 
## 
## .
## .
## . 
## 
## [[9]]
## [1] "POSIXct" "POSIXt" 
## 
## [[10]]
## [1] "POSIXct" "POSIXt" 

In general you choose the apply family that fits the job. Often I personally use lapply or a for loop to act on specific columns or subset the columns I want using indexing ([, ]) and then proceed with apply. The answer to this problem really boils down to determining what you want to accomplish, asking is apply the most appropriate tool, and proceed from there.

May I offer this blog post as an excellent tutorial on what the different apply family of functions do.

Tyler Rinker
  • 108,132
  • 65
  • 322
  • 519
  • 2
    The blog post is excellent, but I think it does not solve my problem. `by` should be used for data frames, but I need more than just grouping by v1. – ang mo Aug 13 '13 at 17:23
1

Try:

sapply(df, function(y) class(y["t2"]))

$v
[1] "integer"

$t
[1] "integer"

$t2
[1] "POSIXct" "POSIXt"
harkmug
  • 2,725
  • 22
  • 26