minitable

minitable provides a few tibble-style construction/coercion helpers – mtable_tibble(), mtable_as_tibble(), mtable_rownames_to_column(), mtable_add_row() – built entirely on data.frame()/as.data.frame(). Results are plain data frames, not tibbles. See the README for the full scope notes; this page walks through each function, including the cross-column-reference behaviour that’s the trickiest part of the mini to get right.

Functions

  • mtable_tibble(...) – build a data frame column by column, sequentially, so later columns can refer to earlier ones by name.
  • mtable_as_tibble(x, ...) – coerce x to a plain data frame via as.data.frame(), preserving literal (non-syntactic) column names.
  • mtable_rownames_to_column(.data, var) – move .data’s row names into an explicit column named var; a no-op if the row names are just the default sequential ones.
  • mtable_add_row(.data, ...) – append a single row to .data, with the same cross-column-reference support as mtable_tibble().

minitable has one internal helper, .mtable_drop_dup_list(), which mtable_tibble() and mtable_add_row() both use to resolve what a column reference means when a name is reused more than once while constructing columns in sequence (see below).

mtable_tibble(): sequential, cross-referencing construction

Building a data frame with data.frame() directly requires every column to already exist as a complete vector before the call – you can’t refer to a column you’re building in the same call. Like tibble::tibble(), mtable_tibble() lifts that restriction: later columns can refer to earlier ones by name, because each column expression is evaluated in order, in an environment built up from the columns already constructed:

mtable_tibble(a = 1:3, b = a * 2, c = a + b)
  a b c
1 1 2 3
2 2 4 6
3 3 6 9

Here b refers to a, and c refers to both a and b – neither would resolve to anything if evaluated outside the call, since a, b, and c don’t otherwise exist in this session. Getting this right took real care in the implementation: the sequential-evaluation logic lives directly inside mtable_tibble() itself, rather than in a shared helper that ... gets forwarded through. Forwarding ... to a separate function turns each argument into an opaque ..1/..2 pronoun that resolves free variables against the caller’s environment instead of the column-by-column environment being built up – which would silently break exactly this feature, evaluating b and c against whatever (unrelated) a/b might exist in the calling code, or erroring if there simply isn’t one.

Unnamed arguments are named after their deparsed expression

Just as in tibble::tibble(), an argument with no name = is named after its own source expression, converted to a string:

mtable_tibble(1:3, sqrt(1:3))
  1:3 sqrt(1:3)
1   1  1.000000
2   2  1.414214
3   3  1.732051

This is mostly useful for quick, throwaway construction; naming columns explicitly (mtable_tibble(x = 1:3, y = sqrt(1:3))) is more readable for anything that will be used again later.

mtable_as_tibble(): coercion only

There’s no tibble class or special printing here – it’s just as.data.frame() with check.names = FALSE, so a literal, non-syntactic column name (like "1:3" from the previous section) survives unchanged instead of being sanitised by make.names():

mtable_as_tibble(list(a = 1:2, b = c("x", "y")))
  a b
1 1 x
2 2 y

The difference from calling as.data.frame() directly shows up with a name that isn’t a syntactically valid R identifier, like the "1:3" name mtable_tibble() generated automatically two sections ago:

names(mtable_as_tibble(list(`1:3` = 1:3)))
[1] "1:3"
names(as.data.frame(list(`1:3` = 1:3)))
[1] "X1.3"

as.data.frame()’s default check.names = TRUE rewrites "1:3" into the syntactically valid "X1.3"; mtable_as_tibble() passes check.names = FALSE through instead, so the literal name survives.

mtable_rownames_to_column(): no-op on default row names

A data frame with only the default sequential row names ("1", "2", …) is returned unchanged – there’s nothing meaningful to move into a column:

identical(
  mtable_rownames_to_column(data.frame(x = 1:3), var = "id"),
  data.frame(x = 1:3)
)
[1] TRUE

Real (non-default) row names do get moved into a new column, placed first:

mtable_rownames_to_column(head(mtcars, 3), var = "model")
          model  mpg cyl disp  hp drat    wt  qsec vs am gear carb
1     Mazda RX4 21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
2 Mazda RX4 Wag 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
3    Datsun 710 22.8   4  108  93 3.85 2.320 18.61  1  1    4    1

mtable_add_row(): the same cross-referencing support

Appending a row supports the same sequential evaluation as mtable_tibble(), so a later value in the new row can refer to an earlier one – either a column already in .data, or one being added in the same call:

df <- mtable_tibble(a = 1:2, b = a * 10)
df
  a  b
1 1 10
2 2 20
mtable_add_row(df, a = 3, b = a * 10)
  a  b
1 1 10
2 2 20
3 3 30

Here a * 10 in the new row refers to the a = 3 just supplied in that same call, not to the existing column a in df – the most recently constructed value for a given name always wins, which is what .mtable_drop_dup_list() is for internally.