Ответ 1
Действительно, существует расхождение в разных методах as.Date
, и вот почему (или, вернее, "как" ):
Во-первых, ваш пример:
> as.Date(c(ind = "2015-07-04", nyd = "2016-01-01"))
[1] "2015-07-04" "2016-01-01"
Здесь мы используем метод as.Date.character
:
> as.Date.character
function (x, format = "", ...)
{
charToDate <- function(x) {
xx <- x[1L]
if (is.na(xx)) {
j <- 1L
while (is.na(xx) && (j <- j + 1L) <= length(x)) xx <- x[j]
if (is.na(xx))
f <- "%Y-%m-%d"
}
if (is.na(xx) || !is.na(strptime(xx, f <- "%Y-%m-%d",
tz = "GMT")) || !is.na(strptime(xx, f <- "%Y/%m/%d",
tz = "GMT")))
return(strptime(x, f))
stop("character string is not in a standard unambiguous format")
}
res <- if (missing(format))
charToDate(x)
else strptime(x, format, tz = "GMT")
as.Date(res)
}
<bytecode: 0x19d3dff8>
<environment: namespace:base>
Если задан формат или нет, ваш вектор передается в strptime
, который преобразует его в класс POSIXlt, а затем он снова передается в as.Date
, но на этот раз с помощью метода as.Date.POSIXlt
, который есть:
> as.Date.POSIXlt
function (x, ...)
.Internal(POSIXlt2Date(x))
<bytecode: 0x19d2df50>
<environment: namespace:base>
означает, что в конечном итоге функция, используемая для преобразования в класс Date, - это функция C, называемая POSIXlt2Date
(быстрый просмотр файла names.c
показывает, что функция do_POSIXlt2D
из файла datetime.c
). Для справки:
SEXP attribute_hidden do_POSIXlt2D(SEXP call, SEXP op, SEXP args, SEXP env)
{
SEXP x, ans, klass;
R_xlen_t n = 0, nlen[9];
stm tm;
checkArity(op, args);
PROTECT(x = duplicate(CAR(args)));
if(!isVectorList(x) || LENGTH(x) < 9)
error(_("invalid '%s' argument"), "x");
for(int i = 3; i < 6; i++)
if((nlen[i] = XLENGTH(VECTOR_ELT(x, i))) > n) n = nlen[i];
if((nlen[8] = XLENGTH(VECTOR_ELT(x, 8))) > n) n = nlen[8];
if(n > 0) {
for(int i = 3; i < 6; i++)
if(nlen[i] == 0)
error(_("zero-length component in non-empty \"POSIXlt\" structure"));
if(nlen[8] == 0)
error(_("zero-length component in non-empty \"POSIXlt\" structure"));
}
/* coerce relevant fields to integer */
for(int i = 3; i < 6; i++)
SET_VECTOR_ELT(x, i, coerceVector(VECTOR_ELT(x, i), INTSXP));
PROTECT(ans = allocVector(REALSXP, n));
for(R_xlen_t i = 0; i < n; i++) {
tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
tm.tm_mday = INTEGER(VECTOR_ELT(x, 3))[i%nlen[3]];
tm.tm_mon = INTEGER(VECTOR_ELT(x, 4))[i%nlen[4]];
tm.tm_year = INTEGER(VECTOR_ELT(x, 5))[i%nlen[5]];
/* mktime ignores tm.tm_wday and tm.tm_yday */
tm.tm_isdst = 0;
if(tm.tm_mday == NA_INTEGER || tm.tm_mon == NA_INTEGER ||
tm.tm_year == NA_INTEGER || validate_tm(&tm) < 0)
REAL(ans)[i] = NA_REAL;
else {
/* -1 must be error as seconds were zeroed */
double tmp = mktime00(&tm);
REAL(ans)[i] = (tmp == -1) ? NA_REAL : tmp/86400;
}
}
PROTECT(klass = mkString("Date"));
classgets(ans, klass);
UNPROTECT(3);
return ans;
}
К сожалению, мое понимание С слишком ограничено, чтобы знать, почему здесь теряются атрибуты. Мое предположение заключалось бы в том, что это происходит либо во время операции coerceVector
, либо когда каждый элемент списка POSIXlt индивидуально принуждается к целым числам (если это происходит, строки 1268-70).
Но посмотрим на другой метод as.Date
, начиная с основного нарушителя, as.Date.POSIXct
:
> as.Date.POSIXct
function (x, tz = "UTC", ...)
{
if (tz == "UTC") {
z <- floor(unclass(x)/86400)
attr(z, "tzone") <- NULL
structure(z, class = "Date")
}
else as.Date(as.POSIXlt(x, tz = tz))
}
<bytecode: 0x19c268bc>
<environment: namespace:base>
При этом, если часовой пояс не задан, или если часовой пояс "UTC", функция просто управляет списками POSIXct
для извлечения данных, которые могут быть разрешены для объекта Date, таким образом, не теряя атрибуты, но , если даны какие-либо другие часовые пояса, он затем преобразуется в объект POSIXlt
и поэтому переходит далее к тому же POSIXlt2Date
внутреннему, который в итоге потеряет свои атрибуты! И действительно:
> as.Date(c(a = as.POSIXct("2016-01-01")), tz="UTC")
a
"2015-12-31"
> as.Date(c(a = as.POSIXct("2016-01-01")), tz="CET")
[1] "2016-01-01"
И, наконец, как отметил @Roland, as.Date.numeric
сохраняет атрибуты:
> as.Date.numeric
function (x, origin, ...)
{
if (missing(origin))
stop("'origin' must be supplied")
as.Date(origin, ...) + x
}
<bytecode: 0x568943d4>
<environment: namespace:base>
origin
преобразуется в Date через as.Date.character
, а затем добавляется вектор числа, сохраняя при этом атрибуты:
> c(a=1) + 2
a
3
Итак, естественно:
> c(a=16814) + as.Date("1970-01-01")
a
"2016-01-14"
До тех пор, пока это несоответствие не позаботится, единственные решения, которые вы должны будете сохранить, по моему мнению, должны либо сначала преобразовать в POSIXct (но остерегайтесь проблем с часовым поясом), либо числовым, либо скопировать атрибуты вашего оригинала вектор:
> before <- c(ind = "2015-07-04", nyd = "2016-01-01")
> after <- as.Date(before)
> names(after) <- names(before)
> after
ind nyd
"2015-07-04" "2016-01-01"