'Error using data.table for output table generation

I would like some help regarding the adjustment of the second code. Codes are intended to do the same thing. The first code is generating the output table correctly. However, in the second code, which uses the data.table function does not. This second code was taken from here: How adjust code functionality to specifications using data.table function In the example that was done in the question, the result was right, but when I tested for my new database, it didn't give the expected result. The output table for that second code would also have to be coef = 14 and Result = 1

Can you help me to solve it?

libraries

library(dplyr)
library(tidyverse)
library(lubridate)
library(data.table)

database

df1<-structure(list(Id = 8, date1 = structure(1649376000, tzone = "UTC", class = c("POSIXct", 
        "POSIXt")), date2 = structure(1649376000, tzone = "UTC", class = c("POSIXct", 
        "POSIXt")), Week = "Friday", DT = "0", Category = "ABC", 
            GR = 1, DayR1 = 0, DayM000 = 13, DayM001 = 13, 
            DayM002 = 14, DayM003 = 14, DayM004 = 13, DayM005 = 13, DayM006 = 13, 
            DayM007 = 12, DayM008 = 12, DayM009 = 12, coef = 14), class = "data.frame", row.names = c(NA, 
        -1L))
    
  Id      date1      date2   Week DT Category GR DayR1 DayM000 DayM001 DayM002 DayM003 DayM004 DayM005 DayM006 DayM007 DayM008 DayM009 coef
1  8 2022-04-08 2022-04-08 Friday  0      ABC  1     0      13      13      14      14      13      13      13      12      12      12   14

First code (The result is correct)

 df1%>% mutate(across(starts_with("Day"), ~coef - .),
                       across(contains("date"), ymd),
                       datedif = parse_number(as.character(date2-date1)))%>%
    rename_with(~str_replace(.,'(?<=[A-Z])0+(?=.)', ""), starts_with('Day')) %>%
    rowwise %>%
    mutate(Result= if (str_c('DayM', datedif) %in% names(.)) get(str_c('DayM', datedif)) else coef) %>%
    ungroup() %>%
  select(coef, Result)%>%data.frame()

 coef Result
1   14   1

Second Code (Using data.table function). The result is wrong

dr_names <- grep("^Day", names(df1), value = TRUE)
date_names <- grep("date", names(df1), value = TRUE)
setDT(df1)[, (dr_names) := lapply(.SD, function(x) coef - x), .SDcols = dr_names
    ][, (date_names) := lapply(.SD, as.IDate), .SDcols = date_names
    ][, datedif := date2 - date1]
setnames(df1, dr_names, sub("([A-Z])0+", "\\1", dr_names))

   df1[,  .(coef, Result = fcoalesce(as.matrix(.SD)[cbind(.I,         
   match(paste0('DayM', datedif), names(.SD)))], coef)),       .SDcols = patterns("^DayM\\d+")]%>%data.frame()

  coef Result
1   14     14


Solution 1:[1]

The issue is in the sub code with setnames. The code matches one or more 0s (0+) after the uppercase letter ([A-Z]) and removes those in the replacement. In the previous dataset, it worked because the names pattern was different. Here, the names ends with digits for 'dr_names'

> dr_names
 [1] "DayR1"   "DayM000" "DayM001" "DayM002" "DayM003" "DayM004" "DayM005" "DayM006" "DayM007" "DayM008" "DayM009"

Thus, for the 'DayM000', it returns DayM, which wouldn't match correctly with paste0("DayM", datedif) which returns DayM0. A change in the pattern to keep the last digit would fix it

setnames(df1, dr_names, sub("([A-Z])0+(.)", "\\1\\2", dr_names))

Now, we run the code

df1[,  .(coef, Result = fcoalesce(as.matrix(.SD)[cbind(.I,         
    match(paste0('DayM', datedif), names(.SD)))], coef)),  
       .SDcols = patterns("^DayM\\d+")]%>%
   data.frame()
  coef Result
1   14      1

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 akrun