11

I am creating a data.frame with a column of type Date. When indexing the data frame with [[ and a numeric vector, the Date becomes a number. This is causing a problem when using purrr::pmap. Can anyone explain why this is happening and is there a work around?

Example:

x <- data.frame(d1 = lubridate::ymd(c("2018-01-01","2018-02-01")))

class(x$d1)
# [1] "Date"

x[[1]]
# [1] "2018-01-01" "2018-02-01"

x[[c(1, 1)]]
# [1] 17532
Henrik
  • 65,555
  • 14
  • 143
  • 159
MrHopko
  • 879
  • 1
  • 7
  • 16
  • try using `purrr:map_df`.. (I think) `map` turns the df to a matrix, casting the POSIXct to character.. – Wimpel Sep 30 '18 at 16:53
  • I meant to say pmap. now edited. – MrHopko Sep 30 '18 at 16:56
  • 1
    Turns out this is an ongoing issue: https://github.com/tidyverse/purrr/issues/358 – MrHopko Sep 30 '18 at 16:57
  • 1
    Yes, did you note [@hadley's comment there](https://github.com/tidyverse/purrr/issues/358#issuecomment-363091446)? – Henrik Sep 30 '18 at 17:28
  • You mean that this is a base R issue (which makes sense given my example). I guessed at the issue from the traceback:`.f(cost_name = .l[[c(1L, i)]], cost_value = .l[[c(2L, i)]], cost_start = .l[[c(3L, i)]], cost_end = .l[[c(4L, i)]], cost_inflation = .l[[c(5L, i)]], period_type = .l[[c(6L, i)]], ...)` – MrHopko Sep 30 '18 at 17:31
  • 1
    Just out of curiosity, why `x[[c(1, 1)]]` instead of `x[1, 1]`? – Cristian E. Nuno Sep 30 '18 at 17:43
  • 1
    the `x[[c(1,1)]]` is how `purrr::pmap` appears to be accessing the value as it iterates through the data.frame. This is where I encountered the issue above. – MrHopko Sep 30 '18 at 17:46
  • 5
    From `help(Extract)` - *Subsetting (except by an empty index) will drop all attributes except names, dim and dimnames.* Keep in mind that *subsetting* is different from *indexing*. In `x[[1]]` you are indexing, in `x[[c(1, 1)]]` you are subsetting and hence attributes lost. – Rich Scriven Sep 30 '18 at 17:52
  • 1
    Dunno about the terminogy here, but at least in `?Extract` they call `z[[c(1, 2)]]` recursive _indexing_. And class `Date` is preserved when extracting from an nested list: `z <- list(a = list(x = 1, date = as.Date("2018-01-01")), b = as.Date("2018-02-01"))`; `z[[c(1, 2)]]`. Not from the second 'non-nested' slot though: `z[[c(2, 1)]]`... – Henrik Sep 30 '18 at 18:05

1 Answers1

5

Overview

After reading why does unlist() kill dates in R and the documentation of unlist(), you've got to manually prevent purrr::map() from coercing the Date objects to integer by way of using the base::c() function.

Load mikmart's PR version of purrr::pmap()

After reading pmap strips Date, it looks like someone very awesome submitted a pull request to resolve this issue within a refactored version of the indexing that happens under the hood in purrr::pmap().

Using devtools::dev_mode(), you can install mikmart/purrr's "pmap" branch version of purrr to retain Date objects while using pmap().

# ******pmap() example ****
# load necessary packages -----
library(devtools)
library(lubridate)

# enter dev mode so you don't have to uninstall the cran version of purrr ----
dev_mode(on = TRUE)

# install mikmart's PR to fix the coercing of Dates to integer ----
install_github(repo = "mikmart/purrr", ref = "pmap")

# load mikmart's PR version of purrr ----
library(purrr)

# load necessary data
x <- data.frame(d1 = lubridate::ymd(c("2018-01-01","2018-02-01")))

# for the first column in x ----
# give me each element
# note: no need for c()
list.of.dates <-
  x %>%
  pmap(.f = ~ .x)

# view results -----
list.of.dates
# [[1]]
# [1] "2018-01-01"
# 
# [[2]]
# [1] "2018-02-01"

# view the class of each list -----
map_chr(list.of.dates, class) # [1] "Date" "Date"
#
# 
# turn off dev mode ---
dev_mode(on = FALSE)
#
# restart R -----
# Manually hit Shift+Cmd+F10 or point in click under the "Session" tab
#
# clear global environment ----
rm(list = ls())
#
# ******map() example********
# load necessary packages -----
library(tidyverse)
library(lubridate)

# load necessary data ----
x <- data.frame(d1 = lubridate::ymd(c("2018-01-01","2018-02-01")))

# from the first column ------
# give me each element
# and ensure the dates don't get coerced to integers
list.of.dates <-
  x$d1 %>%
  map(.f = ~ .x %>% c()) 

# view results -----
list.of.dates
# [[1]]
# [1] "2018-01-01"
# 
# [[2]]
# [1] "2018-02-01"

# # view the class of each list -----
map_chr(list.of.dates, class) # [1] "Date" "Date"

# end of script #
Cristian E. Nuno
  • 2,822
  • 2
  • 19
  • 33
  • Very interesting handling of the package update process (as it wasn't updated at purrr, yet), and made it to my notes. The `ref = 'pmap'` in the `install_github(repo = "mikmart/purrr", ref = "pmap")` call suggests it was just the refactored pmap function that was installed; am I reading this correctly? – Chris Sep 30 '18 at 19:47
  • @Chris that's correct. Looks like the refactoring of indexing took place in the [`src/map.c`](https://github.com/tidyverse/purrr/pull/554/files#diff-ed6bf403234aac5ffa77de8c10222317R193) script. – Cristian E. Nuno Sep 30 '18 at 19:53
  • 1
    Great Cristian. I think your description and code above would make this entirely explicit by editing to `# load mikmart's PR version of purrr pmap ----`, and all ambiguity is gone. – Chris Sep 30 '18 at 20:09