почему агрегат по дате возвращает числовое значение - R - PullRequest
1 голос
/ 07 ноября 2019

У меня есть data.table из нескольких миллионов строк, и я пытаюсь агрегировать данные в поле даты. возвращенный результат числовой (вместо даты), и я не могу понять, почему. Я пытался воспроизвести пример, но безрезультатно. Вот пример моего кода:

DT <- data.table(ID = c(1,1,2,1,2,3,2,3,1), col1 = c('Y', 'N', 'Y', 'Y', 'N', 'Y',' Y', 'N', 'N'), col2 = as.Date(c('2001-01-01', '2002-01-01', '2003-01-01', '2004-01-01','2005-01-01', NA, '2007-01-01', '2008-01-01', NA), '%Y-%m-%d'))

> DT
   ID col1       col2
1:  1    Y 2001-01-01
2:  1    N 2002-01-01
3:  2    Y 2003-01-01
4:  1    Y 2004-01-01
5:  2    N 2005-01-01
6:  3    Y       <NA>
7:  2    Y 2007-01-01
8:  3    N 2008-01-01
9:  1    N       <NA>

DT_agg = DT[, .(max_date = if(!any(col1 == "Y")) NA_real_ else max(col2[which(col1 == "Y")], na.rm = TRUE) ) , by = .(ID)]
Warning message:
In max.default(NA_real_, na.rm = TRUE) :
  no non-missing arguments to max; returning -Inf



 # this works on the sample data.table. 
 # and it's desired output. But it doesn't work on my 
 # real data.table. max_date has numeric values instead (e.g. 11124, 22354, etc.)
> DT_agg
   ID   max_date
1:  1 2004-01-01
2:  2 2003-01-01
3:  3       <NA>

# this is what i want for my results. but in my real table max_date becomes numeric and unusable
> class(DT_agg$max_date)
[1] "Date"

в приведенном выше примере кода DT_agg $ max_date является классом даты. но по какой-то причине в моей таблице это число. Я запускаю ту же агрегацию в другом поле даты, и это нормально.

Я действительно нахожусь в своем уме, пытаясь выяснить возможную причину.

1 Ответ

2 голосов
/ 07 ноября 2019

TL; DR

ifelse - это то, где что-то происходит. Положения Да и Нет должны быть одного класса.

Мое решение:

Сначала сгруппируйте по обоим столбцам

> DT[,max(col2),by=list(ID,col1)]
   ID col1         V1
1:  1    Y 2004-01-01
2:  1    N       <NA>
3:  2    Y 2003-01-01
4:  2    N 2005-01-01
5:  3    Y       <NA>
6:  2    Y 2007-01-01
7:  3    N 2008-01-01

Затем просто возьмите Y.

> DT[,max(col2),by=list(ID,col1)][col1=='Y']
   ID col1         V1
1:  1    Y 2004-01-01
2:  2    Y 2003-01-01
3:  3    Y       <NA>

Теперь удалите ненужный второй столбец (или присвойте ему

> DT[,max(col2),by=list(ID,col1)][col1=='Y'][ , .(ID,V1)]
   ID         V1
1:  1 2004-01-01
2:  2 2003-01-01
3:  3       <NA>

Также можно объединить второй и третий этапы:

 DT[,max(col2),by=list(ID,col1)][col1=='Y', .(ID,max_date=V1)]

Обновление : Когда я экспериментировал с ifelse, я обнаружил, что здесь происходит ваше преобразование

 k=as.Date('2001-01-01')
> k
[1] "2001-01-01"
> as.integer(k)
[1] 11323
> max(k,NA)
[1] NA
> min(k,NA)
[1] NA
> ifelse(Y=='N',k,NA)
Error in ifelse(Y == "N", k, NA) : object 'Y' not found
> Y='N'
> ifelse(Y=='N',k,NA)
[1] 11323

Обновление 2

Если вы попытаетесьвыполнив версию таблицы быстрых данных ifelse, вы увидите, в чем проблема. Приведение происходит в обычном ifelse, поскольку предложения «Да» и «Нет» не относятся к одному классу, и R должен принятьвремя для их произнесения, что также дает вам хит производительности. Помните: ifelse должен быть векторизуемым . Для этого предложения Yes и No _ должны быть того же класса.

# the double colon is not really necessary.  I'm just
# doing it to emphasize the provenance of `fifelse`

> data.table::fifelse(Y=='N',k,23)
Error in data.table::fifelse(Y == "N", k, 23) : 
  'yes' has different class than 'no'. Please make sure that both arguments have the same class.

...