Метка времени создания объекта - PullRequest
17 голосов
/ 16 марта 2011

Есть ли способ получить время, когда был создан объект?

Ответы [ 5 ]

14 голосов
/ 16 марта 2011

Не обычно, но вы можете сделать это для объектов, которые вы создаете сами с помощью

R> df <- data.frame(a=sample(LETTERS[1:5],10,TRUE),b=runif(10))
R> attr(df, "createdAt") <- Sys.time()
R> df
   a         b
1  B 0.8437021
2  D 0.8683446
3  B 0.5194791
4  B 0.0480405
5  B 0.5604978
6  C 0.1938154
7  A 0.1451077
8  D 0.1785405
9  C 0.3937795
10 B 0.2874135
R> str(df)
'data.frame':   10 obs. of  2 variables:
 $ a: Factor w/ 4 levels "A","B","C","D": 2 4 2 2 2 3 1 4 3 2
 $ b: num  0.844 0.868 0.519 0.048 0.56 ...
 - attr(*, "createdAt")= POSIXct, format: "2011-03-16 10:42:10.137434"
R> 

и затем вы можете написать себе некоторые пользовательские функции print() или show(), которые используют этот атрибут. rms Фрэнка Харрелла и его предшественник по дизайну делали что-то подобное долгое время.

10 голосов
/ 16 марта 2011

Краткий ответ: Нет.

Длинный ответ: Да, все, что вам нужно сделать, это переписать код назначения в ядре C R, чтобы хранить метку даты где-то каждый раз, когда объект изменяется. Я пробовал это однажды, сохраняя данные в атрибуте, как и другие ответы здесь, но это имело неприятный побочный эффект, заключающийся в том, что идентичные объекты различались. x = 1 и y = 1 имели разные временные метки, поэтому идентичные (x, y) были ЛОЖНЫМИ, и это великолепно нарушило тесты R. Я сдался.

4 голосов
/ 17 марта 2011

В дополнение к ответу Spacedman и моему комментарию, смотрите этот пример:

x <- 1
print(x)
# [1] 1

`<-` = function(...) {
  eval.parent(replace(match.call(), c(1, 3), list(base::`<-`, structure(..2, ctime=Sys.time()))))
}

x <- 2
print(x)
# [1] 2
# attr(,"ctime")
# [1] "2011-03-17 11:33:55 EDT"

Возможно, вы не захотите делать это в .GlobalEnv, но это может быть полезно в локализованной среде.

3 голосов
/ 14 сентября 2017

Я думаю, что функция Чарльза великолепна, но она может создавать проблемы в глобальной среде.

Я предлагаю создать новый оператор %c%, который будет использоваться вместо <-:

`%c%` = function(...) {
  eval.parent(replace(match.call(), c(1, 3), list(base::`<-`, structure(..2, ctime=Sys.time()))))
}
## object 1 older than object 2?
`%c<%` = function(x1, x2) {
  if (any(names(attributes(x1))=="ctime") && any(names(attributes(x2))=="ctime")) {
    attr(x1, "ctime") < attr(x2, "ctime")
  } else {
    NA
  }
}
## object 1 newer than object 2?
`%c>%` = function(x1, x2) {
  if (any(names(attributes(x1))=="ctime") && any(names(attributes(x2))=="ctime")) {
    attr(x1, "ctime") > attr(x2, "ctime")
  } else {
    NA
  }
}

Две другие функции можно использовать для сравнения отметок времени.(С помощью этих последних функций я могу избежать трудоемких вычислений, если не изменились некоторые объекты аргументов.)

> xx %c% 1
> xx
[1] 1
attr(,"ctime")
[1] "2017-09-14 17:01:03 EEST"
> xx + 1
[1] 2
attr(,"ctime")
[1] "2017-09-14 17:01:03 EEST"
> class(xx)
[1] "numeric"
> yy %c% 2
> xx+yy
[1] 3
attr(,"ctime")
[1] "2017-09-14 17:01:03 EEST"
> yy
[1] 2
attr(,"ctime")
[1] "2017-09-14 17:04:27 EEST"
> xx %c<% yy
[1] TRUE
> xx %c>% yy
[1] FALSE
1 голос
/ 07 февраля 2018

Что вы думаете о том, чтобы не изменять объекты, а вместо этого сохранять временные метки так же, как сохраняется история? R сохраняет историю вашей консоли в файл .Rhistory в папке, в которой вы работаете (т.е. getwd()).

Я позволю R сохранить временные метки (в формате Unix, количество секунд с начала эпохи = 1 января 1970 г. 0:00:00) до .Rtimestamps, но только если это установлено в качестве опции:

#' @export
#' @noRd
`<-` = function(...) {
  if (!is.null(getOption("recordTimestamps"))
      & interactive()
      & environmentName(parent.frame()) == "R_GlobalEnv") {
    # only save timestamp when set in options, in interactive mode and in global env.
    if (getOption("recordTimestamps")) {
      write(
        x = paste(
          as.character(match.call())[2],
          as.double(Sys.time()),
          sep = ","),
        file = ".Rtimestamps",
        append = TRUE)
    }
  }
  eval.parent(
    replace(
      match.call(),
      1,
      list(base::`<-`)))
}

Использование:

options(recordTimestamps = TRUE) # only first time of course
a <- 123
# wait 5 seconds and change it
a <- 456

Теперь вам нужно получить только те даты, которые вы можете сделать с некоторыми вспомогательными функциями, которые печатают действительные классы POSIXct:

#' @export
#' @noRd
ctime <- function(object) {
  target_object <- deparse(substitute(object))
  get_cmtime(target_object, 1)
}

#' @export
#' @noRd
mtime <- function(object) {
  target_object <- deparse(substitute(object))
  get_cmtime(target_object, 2)
}

get_cmtime <- function(target_object, mode) {
  if (!is.null(getOption("recordTimestamps")) & interactive()) {
    # only get dates when set in options and when in interactive mode
    if (getOption("recordTimestamps") & file.exists(".Rtimestamps")) {
      lines <- readLines(con <- file(".Rtimestamps"), warn = FALSE, encoding = "UTF-8")
      close(con)
      target_lines <- lines[grepl(paste0("^(", target_object, "),"), lines)]
      if (length(target_lines) > 0) {
        if (mode == 1) {
          # get first value, second element
          timestamp <- unlist(strsplit(target_lines[1], ","))[2]
        } else if (mode == 2) {
          # get last value, second element
          timestamp <- unlist(strsplit(target_lines[length(target_lines)], ","))[2]
        }
        ## transform to date
        return(as.POSIXct(origin = "1970-01-01", x = as.double(timestamp)))
      } else {
        return(NA)
      }
    }
  }
}

И теперь это работает так:

> a
[1] 456
> ctime(a)
[1] "2018-02-07 10:43:03 CET"
> mtime(a)
[1] "2018-02-07 10:43:08 CET"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...