banbh
banbh

Reputation: 1525

How to append a matrix of columns to a data frame in dplyr?

I want to append (mutate) multiple columns to a data frame, where those columns are stored in a matrix. Is there a way to do this using functions from the tidyverse? (Note that it is possible by resorting to base:: functions, though.) Equivalently, what I am asking is what is the most natural (or idiomatic) way to do this using functions from the tidyverse.

For example, suppose we estimate a quantile regression:

library(dplyr)

tibble(x = runif(100)) %>%
  mutate(y = rnorm(n())) ->
  EstimationData

library(quantreg)

taus <- (1:9)/10
rq_fit <- rq(y ~ x, tau = taus, data = EstimationData)

and we would like to predict the model on the following values of x:

PredictionData <- tibble(x = seq(0, 1, len = 10))

This can be done via:

predict(rq_fit, newdata = PredictionData)

which returns a matrix (with one column corresponding to each tau). A natural thing would be to package the predictions along with their corresponding xs. One might hope to be able to mutate() the above matrix onto PredictionData, but as far as I can see that is not possible. One possibility is to do:

PredictionData %>%
  data.frame(predict(rq_fit, newdata = .), check.names = FALSE) # (*)

which works well (particularly since the matrix columns have names), although it relies base::data.frame(). Note that tibble() and as_tibble() do not work.

One way to try to write more idiomatic tidyverse code is to turn the matrix into a list of vectors, as follows:

row_split <- function(X) split(X, row(X, as.factor = TRUE))

PredictionData %>%
  mutate(y = row_split(predict(rq_fit, newdata = .))) %>%
  unnest(.id = 'tau_ix') %>%
  mutate(tau = taus[as.integer(tau_ix)]) %>%
  select(-tau_ix)

But I'm not convinced it's any better.

Is method (*) the best way?

Upvotes: 3

Views: 2602

Answers (1)

duckmayr
duckmayr

Reputation: 16930

I think the function you want is dplyr::bind_cols(). Note this doesn't work with a matrix, so you also have to use dplyr::as_tibble().

If your goal is to keep things as a tibble, use functions from dplyr, etc., I think this is the easiest way:

PredictionData %>% bind_cols(as_tibble(predict(rq_fit, newdata = .)))

However, one might think this is a little too "from the inside out" rather than "left to right" to be really idiomatic for a dplyr approach. So, maybe you want something more like

rq_fit %>%
    predict(newdata = PredictionData) %>%
    as_tibble() %>%
    bind_cols(PredictionData) %>%
    select(x, everything())

Both approaches give the following output:

# A tibble: 10 x 10
           x `tau= 0.1` `tau= 0.2` `tau= 0.3` `tau= 0.4`   `tau= 0.5` `tau= 0.6` `tau= 0.7` `tau= 0.8` `tau= 0.9`
       <dbl>      <dbl>      <dbl>      <dbl>      <dbl>        <dbl>      <dbl>      <dbl>      <dbl>      <dbl>
 1 0.0000000 -1.5755585 -0.8082654 -0.3133431 -0.1952309  0.058074887 0.44450275  0.6679990  0.8802325   1.650510
 2 0.1111111 -1.4767907 -0.7915847 -0.3517192 -0.1909820  0.041473996 0.39935461  0.6132367  0.8618259   1.618999
 3 0.2222222 -1.3780228 -0.7749040 -0.3900952 -0.1867331  0.024873104 0.35420647  0.5584744  0.8434194   1.587488
 4 0.3333333 -1.2792549 -0.7582233 -0.4284712 -0.1824842  0.008272213 0.30905833  0.5037121  0.8250128   1.555976
 5 0.4444444 -1.1804871 -0.7415425 -0.4668472 -0.1782353 -0.008328679 0.26391019  0.4489498  0.8066063   1.524465
 6 0.5555556 -1.0817192 -0.7248618 -0.5052233 -0.1739865 -0.024929570 0.21876205  0.3941875  0.7881997   1.492954
 7 0.6666667 -0.9829513 -0.7081811 -0.5435993 -0.1697376 -0.041530462 0.17361391  0.3394252  0.7697932   1.461442
 8 0.7777778 -0.8841835 -0.6915004 -0.5819753 -0.1654887 -0.058131353 0.12846577  0.2846630  0.7513866   1.429931
 9 0.8888889 -0.7854156 -0.6748196 -0.6203513 -0.1612398 -0.074732245 0.08331763  0.2299007  0.7329801   1.398419
10 1.0000000 -0.6866477 -0.6581389 -0.6587274 -0.1569909 -0.091333136 0.03816949  0.1751384  0.7145735   1.366908

Data

For reproducibility, I created the data using your code, but setting the seed first:

set.seed(1234)

library(dplyr)

tibble(x = runif(100)) %>%
    mutate(y = rnorm(n())) ->
    EstimationData

library(quantreg)

taus <- (1:9)/10
rq_fit <- rq(y ~ x, tau = taus, data = EstimationData)

PredictionData <- tibble(x = seq(0, 1, len = 10))

Upvotes: 2

Related Questions