tidyr::expand_grid结合do.call/lift_dl输出异常技术问询
do.call()/lift_dl() with tidyr::expand_grid() gives different results than direct call? The core issue here is how R's c() function handles NULL elements when building argument lists, plus how purrr::lift_dl() relies on this behavior under the hood. Let's break this down clearly:
1. Direct call vs. do.call() argument construction
When you call expand_grid() directly:
dat <- list(tibble(a = 1:2), NULL, tibble(b = 2)) tidyr::expand_grid(dat[[1]], dat[[2]], dat[[3]], .name_repair = "minimal")
You’re explicitly passing three positional arguments (including the NULL), plus the .name_repair named argument. tidyr::expand_grid() processes the NULL as a valid input—with .name_repair="minimal", it creates a placeholder column for the NULL value.
But when you use c(dat, list(.name_repair = "minimal")) to build the argument list for do.call():
do.call(tidyr::expand_grid, c(dat, list(.name_repair = "minimal")))
R’s c() function automatically drops NULL elements when concatenating lists. The resulting argument list becomes list(tibble(a=1:2), tibble(b=2), .name_repair="minimal")—the NULL is entirely removed. This means do.call() is actually calling expand_grid() with only two positional arguments, hence the mismatched output.
2. Why lift_dl() behaves like do.call()
purrr::lift_dl() converts a function using ... into one that accepts a list of positional arguments plus extra named args. Internally, it uses do.call() with c() to combine these inputs. So:
purrr::lift_dl(expand_grid)(dat, .name_repair = "minimal")
Is equivalent to the problematic do.call() call above—the NULL in dat gets dropped by c(), leading to an incomplete argument list.
3. Fix: Use append() to preserve NULL
To keep the NULL element in your argument list, replace c() with append(), which preserves NULL values when combining lists:
# Fixed do.call() version do.call(tidyr::expand_grid, append(dat, list(.name_repair = "minimal"))) # Fixed lift_dl() version (or use the do.call fix directly) purrr::lift_dl(function(args, ...) do.call(expand_grid, append(args, list(...))))(dat, .name_repair = "minimal")
This will pass the exact same arguments to expand_grid() as your direct call, resulting in matching output.
内容的提问来源于stack exchange,提问作者Fabian Gehring

