"group_by->summarise->mean()" taking way longer than expected

Viewed 148

I have a dataset of around 4.2 million observations. My code is below:

new_dataframe = original_dataframe %>%
   group_by(user_id, date) %>%
   summarise(delay = mean(delay, na.rm=TRUE)
   )

This pipeline should be taking a 4.2 million x 3 dataframe with 3 columns: user_id, date, delay; and outputting a dataframe that's less than 4.2 million x 3.

A little bit about why I'm doing this, the problem involves users making payments on a given due date. Sometimes a user makes multiple payments for the same due date with different delay times (e.g. made a partial payment on the due date but completed the rest a few days later). I would like to have a single delay measure (the mean delay) associated with each unique user & due date combination.

For most due dates, users make a single payment so the mean function should essentially just copy a single number from the original dataframe to the new one. In all other cases there are at most 3 different delay values associated with a given due date.

My understanding is that the time complexity of this should be around O(2n), but this has been running for more than 24 hours on a powerful VM. Can anyone help me understand what I'm missing here? I'm beginning to wonder if this pipeline is instead O(n^2), by sorting user ID's and dates simultaneously instead of sequentially

2 Answers

This is due to this issue: https://github.com/tidyverse/dplyr/issues/5113

The poor performance results from the fact that delay is a difftime (as confirmed by the OP in the comments above), and difftime isn't (yet) supported by native C code. As a work around, convert the difftime to numeric before calling summarize.

Note: The above issue in the dplyr github repo is marked as closed only because it's now being tracked in the vctrs repo here: https://github.com/r-lib/vctrs/issues/1293

We can use data.table methods

library(data.table)
setDT(original_dataframe)[, .(delay = mean(delay, na.rm=TRUE)), by = .(user_id, date)]

Or use collapse

library(collapse)
collap(original_dataframe, delay ~ user_id + date, fmean)
Related