34

The question says it all - I want to take a list object full of data.frames and write each data.frame to a separate .csv file where the name of the .csv file corresponds to the name of the list object.

Here's a reproducible example and the code I've written thus far.

df <- data.frame(
    var1 = sample(1:10, 6, replace = TRUE)
    , var2 = sample(LETTERS[1:2], 6, replace = TRUE)
    , theday = c(1,1,2,2,3,3)
)

df.daily <- split(df, df$theday) #Split into separate days

lapply(df.daily, function(x){write.table(x, file = paste(names(x), ".csv", sep = ""), row.names = FALSE, sep = ",")})

And here is the top of the error message that R spits out

Error: Results must have one or more dimensions.
In addition: Warning messages:
1: In if (file == "") file <- stdout() else if (is.character(file)) { :
  the condition has length > 1 and only the first element will be used

What am I missing here?

Gregor Thomas
  • 136,190
  • 20
  • 167
  • 294
Chase
  • 67,710
  • 18
  • 144
  • 161

3 Answers3

33

Try this:

sapply(names(df.daily), 
 function (x) write.table(df.daily[[x]], file=paste(x, "txt", sep=".") )   )

You should see the names ("1", "2", "3") spit out one by one, but the NULLs are the evidence that the side-effect of writing to disk files was done. (Edit: changed [] to [[]].)

IRTFM
  • 258,963
  • 21
  • 364
  • 487
  • Thanks for base R solution and the more succinct way to use `paste`. – Chase Nov 17 '10 at 22:16
  • I think there should be `df.daily[[x]]`. – Marek Nov 17 '10 at 22:22
  • Both df.daily[x] and df.daily[[x]] give the same result on my machine. I guess I can see the logic of your suspicion, though. – IRTFM Nov 17 '10 at 22:38
  • I think the reason that both forms succeed is that write.table coerces to data.frame class with as.data.frame() if the object is not one to start with and that will succeed if the object is a list with one dataframe. – IRTFM Nov 17 '10 at 23:24
  • For me result is not the same. Columns names in `[]` version have prefix (e.g. `X1.var1`), in `[[]]` version don't. – Marek Nov 18 '10 at 08:41
11

You could use mapply:

mapply(
  write.table,
  x=df.daily, file=paste(names(df.daily), "txt", sep="."),
  MoreArgs=list(row.names=FALSE, sep=",")
)

There is thread about similar problem on plyr mailing list.

Marek
  • 49,472
  • 15
  • 99
  • 121
7

A couple of things:

laply performs operations on a list. What you're looking for is d_ply. And you don't have to break it up by day, you can let plyr do that for you. Also, I would not use names(x) as that returns all of the column names of a data.frame.

d_ply(df, .(theday), function(x) write.csv(x, file=paste(x$theday,".csv",sep=""),row.names=F))
Brandon Bertelsen
  • 43,807
  • 34
  • 160
  • 255
  • I always forget about the "_" plyr functions for some reason. This will do the trick. Thanks! – Chase Nov 17 '10 at 22:15
  • this solution doesn;t work for me. I get an error message as follows: Error in file(file, ifelse(append, "a", "w")) : invalid 'description' argument In addition: Warning message: In if (file == "") file <- stdout() else if (is.character(file)) { : the condition has length > 1 and only the first element will be used – zach Jan 24 '12 at 20:58