问题
Is there a way to retrieve the time an object was created at?
回答1:
Not generally but you can do it for objects you create yourself via
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>
and you can then write yourself some custom print()
or show()
functions that use the attribute. Frank Harrell's rms and its Design predecessor have done something like that for a long time.
回答2:
Short answer: No.
Long answer: Yes, all you need to do is rewrite the assignment code in R's C core to store a datestamp somewhere every time an object is changed. I tried this once, storing the data in an attribute much like other answers here, but it had the unfortunate side-effect of making identical objects different. x=1 and y=1 had different timestamps, so identical(x,y) was FALSE and that broke R's tests in magnificent ways. I gave up.
回答3:
Further to Spacedman's answer, and my comment there, see this example:
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"
You probably wouldn't want to do this in .GlobalEnv
, but it could be useful within a localized environment.
回答4:
I think that Charles' function is great, but it can create problems in global environment.
I suggest to create a new operator %c%
to be used instead of <-
:
`%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
}
}
The other two functions can be used to compare the time stamps. (With these latter functions I can avoid time-consuming calculations unless some argument objects have changed.)
> 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
回答5:
What do you think of not altering the objects, but instead save the timestamps the same way as history is being saved? R saves your console history to a file .Rhistory
in the folder you're working in (i.e. getwd()
).
I let R save timestamps (in Unix format, amount of seconds since Epoch = Jan 1st 1970 0:00:00) to .Rtimestamps
, but only when this is set as an option:
#' @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::`<-`)))
}
Usage:
options(recordTimestamps = TRUE) # only first time of course
a <- 123
# wait 5 seconds and change it
a <- 456
Now you only need to get those dates, which you can do with some helper functions that print valid POSIXct classes:
#' @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)
}
}
}
}
And now it works like this:
> a
[1] 456
> ctime(a)
[1] "2018-02-07 10:43:03 CET"
> mtime(a)
[1] "2018-02-07 10:43:08 CET"
来源:https://stackoverflow.com/questions/5327555/object-creation-timestamp