4

Here's a hack to create an empty data frame with no rows and no columns:

iris[FALSE, FALSE]
#> data frame with 0 columns and 0 rows

Smarter-looking code creates a spurious column:

x <- list(NULL)
class(x) <- c("data.frame")
attr(x, "row.names") <- integer(0)
str(x)
#> 'data.frame':    0 obs. of  1 variable:
#>  $ : NULL

Is there a non-hack alternative?

The reason to create such a thing is to satisfy a function that can handle empty data frames but not NULLs.

This is different from similar questions because it is about having no columns as well as no rows.

9
  • 1
    But that question is about specifying column types. Commented Jun 7, 2016 at 1:19
  • 2
    structure(list(),class="data.frame") would be a way to go your original method of trying to add a class to a list. Commented Jun 7, 2016 at 1:32
  • I don't think this is a duplicate Commented Jun 7, 2016 at 1:34
  • OP says they're trying "to satisfy a function that can handle empty data frames but not NULLs"... if OP is the one writing the function then can I suggest they're attacking this from the wrong side? What about testing inherits(x, "data.frame")? which will pass for a data.frame (empty or not) but will fail for NULL. If they're trying to pass data into an existing function, then data.frame() should bypass the test (which could very likely be the above one anyway). Commented Jun 7, 2016 at 1:39
  • @JonathanCarroll OP is trying to satisfy unnest in tidyr package ;) Commented Jun 7, 2016 at 2:13

2 Answers 2

7
df <- data.frame()
str(df)
'data.frame':   0 obs. of  0 variables
Sign up to request clarification or add additional context in comments.

1 Comment

Why didn't I think of that?
2
empty.data.frame <- function() {
  structure(NULL,
            names = character(0),
            row.names = integer(0),
            class = "data.frame")
}
empty.data.frame()
#> data frame with 0 columns and 0 rows

# thelatemail's suggestion in a comment (fastest)
empty.data.frame2 <- function() {
  structure(NULL, class="data.frame")
}

library(microbenchmark)
microbenchmark(data.frame(), empty.data.frame(), empty.data.frame2())
#> Unit: microseconds
#>                 expr    min      lq     mean median     uq    max neval
#>         data.frame() 12.831 13.4485 15.18162 13.879 14.378 65.967   100
#>   empty.data.frame()  8.323  9.0515  9.76106  9.363  9.732 19.427   100
#>  empty.data.frame2()  5.884  6.9650  7.63442  7.240  7.540 17.746   100

5 Comments

Is performance really an issue here? The only possible scaling is with repetition.
@JonathanCarroll how would you scale an empty data frame?
@PierreLafortune that's my point. This isn't going to be a costly procedure no matter which way you do it. You may potentially do it lots of times (under some odd scenario) but even then it's not slow.
His test is most likely for learning to see which function call gets to the point with the least internal moves.
"least internal moves" -- precisely, to get as close as possible to a variable declaration like "int a;". I should have said that in the question. In practice it's far faster to create one and copy it.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.