Make the `drop` argument in `dcast` only look at the RHS of the formula : <p>The <code>drop</code> argument in <code>dcast</code> (from "reshape2" or "dplyr") can be useful when going from a "long" to a "wide" dataset and you want to create columns even for combinations that do not exist in the long form.</p>

<p>It turns out that using <code>drop</code> also affects combinations the left hand side (LHS) of the formula as well as the right hand side (RHS). Thus, it also creates extra <em>rows</em> based on the combinations of LHS values.</p>

<p>Is there a way to override this behavior?</p>

<hr>

<p>Here's some sample data:</p>

<pre><code>library(data.table)
DT &lt;- data.table(v1 = c(1.105, 1.105, 1.105, 2.012, 2.012, 2.012),
                 ID = c(1L, 1L, 1L, 2L, 2L, 2L), 
                 v2 = structure(c(2L, 3L, 5L, 1L, 2L, 6L), 
                                .Label = c("1", "2", "3", "4", "5", "6"), 
                                class = "factor"),
                 v3 = c(3L, 2L, 2L, 5L, 4L, 3L)) 
</code></pre>

<p>Notice that "v2" is a <code>factor</code> column with 6 levels. I essentially want to go from "long" to wide", but add in columns for any missing factor levels (in this case "4").</p>

<p><code>reshape</code> handles the shape, but not the missing columns:</p>

<pre><code>reshape(DT, direction = "wide", idvar = c("ID", "v1"), timevar = "v2")
#       v1 ID v3.2 v3.3 v3.5 v3.1 v3.6
# 1: 1.105  1    3    2    2   NA   NA
# 2: 2.012  2    4   NA   NA    5    3
</code></pre>

<p><code>dcast</code> handles adding the missing columns, but only if there's one value on the LHS:</p>

<pre><code>dcast(DT, ID ~ v2, value.var = "v3", drop = FALSE)
#    ID  1 2  3  4  5  6
# 1:  1 NA 3  2 NA  2 NA
# 2:  2  5 4 NA NA NA  3
</code></pre>

<p>If there are multiple values on the LHS, the combinations of the values on the LHS are also expanded out, as if we had used <code>CJ</code> or <code>expand.grid</code>, but rows 2 and 3 are not at all of interest to me:</p>

<pre><code>dcast(DT, ... ~ v2, value.var = "v3", drop = FALSE)
#       v1 ID  1  2  3  4  5  6
# 1: 1.105  1 NA  3  2 NA  2 NA
# 2: 1.105  2 NA NA NA NA NA NA
# 3: 2.012  1 NA NA NA NA NA NA
# 4: 2.012  2  5  4 NA NA NA  3
</code></pre>

<p>This is similar to using <code>xtabs</code> in base R: <code>ftable(xtabs(v3 ~ ID + v1 + v2, DT))</code>.</p>

<hr>

<p>Is there a way to let <code>dcast</code> know that essentially, "Hey. The combination of values on the LHS are the IDs. Don't try to fill them in for me."</p>

<p>My current approach is to do three steps, one for collapsing down the LHS values, another for spreading out the RHS values, and then one for merging the result.</p>

<pre><code>merge(DT[, list(v1 = unique(v1)), .(ID)],  ## or unique(DT[, c("ID", "v1"), with = FALSE])
      dcast(DT, ID ~ v2, value.var = "v3", drop = FALSE), 
      by = "ID")[]
#    ID    v1  1 2  3  4  5  6
# 1:  1 1.105 NA 3  2 NA  2 NA
# 2:  2 2.012  5 4 NA NA NA  3
</code></pre>

<p>Is there a better approach that I'm missing?</p>
