Разница в результатах с использованием cumsum - PullRequest
7 голосов
/ 23 марта 2019

Почему эти две операции различны?

library(lubridate)
library(magrittr)

> seconds_to_period(1:1000) %>% cumsum %>% sum
[1] 14492440
> 1:1000 %>% cumsum %>% sum
[1] 167167000

Однако я видел, что проблема заключается в том, что cumsum добавляет только секунды периода и игнорирует остальные:

seconds_to_period(60) +  seconds_to_period(60)
[1] "2M 0S"

но

> cumsum(c(seconds_to_period(60), seconds_to_period(60)))
[1] 0 0

Почему это поведение является формой по умолчанию?Я думаю, что это довольно не интуитивно понятно.Кроме того, как это можно преодолеть и получить в результате то же самое, что и cumsum(1:1000), используя классы 'Периода' смазки, которые не включают в себя что-то вроде:

c(seconds_to_period(60), seconds_to_period(60)) %>% as.numeric %>% cumsum

1 Ответ

6 голосов
/ 23 марта 2019

Будучи cumsum примитивом, вы можете увидеть здесь https://github.com/Microsoft/microsoft-r-open/blob/master/source/src/main/cum.c что R он делает под капотом. Более того, если вы читаете из строки 215:

PROTECT(t = coerceVector(CAR(args), REALSXP));
    n = XLENGTH(t);
    PROTECT(s = allocVector(REALSXP, n));
    setAttrib(s, R_NamesSymbol, getAttrib(t, R_NamesSymbol));
    UNPROTECT(2); 

Это делает принуждение от period до numeric и, поскольку структура периода, это только сохранение .Data

Сравнить

seconds_to_period(60)@.Data
seconds_to_period(59)@.Data

Следовательно, на уровне C, R не выполняет as.numeric, но быстрее, более эффективно (но вы можете сказать менее тонко, потому что он не понимает другие элементы из .Data, как это делает as.numeric) приведения данных.

Смотри как это:

 setClass("Foo", representation(.Data="numeric", number1 = "numeric", number2 = "numeric"))

 bar <- new("Foo",.Data=5, number1 = 12, number2 = 31)

 cumsum(bar) 

Результат равен 5, потому что он только приводит к numeric Data

Кроме того:

 setClass("Foo2", representation(.Data="numeric", number1 = "numeric", number2 = "numeric"))

 bar2 <- new("Foo2", number1 = 12, number2 = 31)

 cumsum(bar2) 

вернем вам numeric(0) потому что нет .Data

И

 setClass("Foo3", representation( number1 = "numeric", number2 = "numeric"))

 bar3 <- new("Foo3", number1 = 12, number2 = 31)

 cumsum(bar3) 

Это вообще не работает: без .Data, внутренне, R не знает, как привести его к numeric при выполнении cumsum

Итак: это из-за того, как R внутренне работает со сложными объектами S4. Вы всегда можете указать lubridate сотрудникам создать новый параметр seconds и сохранить в .Data совокупные секунды всего объекта S4. Я думаю, что таким образом cumsum будет работать. Но сейчас они используют .Data для хранения второго аргумента. См edit(seconds_to_period):

function (x) 
{
  span <- as.double(x)
  remainder <- abs(span)
  newper <- period(second = rep(0, length(x)))
  slot(newper, "day") <- remainder%/%(3600 * 24)
  remainder <- remainder%%(3600 * 24)
  slot(newper, "hour") <- remainder%/%(3600)
  remainder <- remainder%%(3600)
  slot(newper, "minute") <- remainder%/%(60)
  slot(newper, ".Data") <- remainder%%(60)
  newper * sign(span)
}

Наконец, просто для удовольствия. Это моя пробная версия того, как заставить cumsum работать здесь:

setClass("Period2",representation(.Data="numeric", period="Period"))


seconds_to_period_2 <- function(x){
   (lapply(x, function(y) new("Period2", .Data=y, period=seconds_to_period(y))))
}

a<-seconds_to_period_2(1:60)

cumsum(a)

Лучший!

...