Цикл по объекту Date или POSIXct приводит к числовому итератору.

Почему повторение объекта Date или POSIXct приводит к numeric? Например:

test = as.Date("2009-01-01")
print( class( test ) )
# [1] "Date"
for ( day in test )
{
    print( class( day ) )
}
# [1] "numeric"

То же самое происходит с POSIXct:

test = as.POSIXct("2009-01-01")
print( class( test ) )
# [1] "POSIXct" "POSIXt"
for ( day in test )
{
    print( class( day ) )
}
# [1] "numeric"

person SFun28    schedule 22.06.2011    source источник
comment
as.numeric(test) по сути тот же результат. т.е. количество дней от эпохи 1970-01-01.   -  person Brandon Bertelsen    schedule 22.06.2011
comment
Это сработает: for(d in as.list(test)) print(class(test))   -  person G. Grothendieck    schedule 12.08.2015
comment
У @G.Grothendieck есть правильный ответ на то, что должно было быть основным вопросом: как заставить for делать то, что практически все хотят, чтобы он делал с (вектором? списком? чем угодно) дат.   -  person user3673    schedule 12.07.2017


Ответы (6)


?"for" говорит, что seq (часть после in) является "выражением [A]n, оценивающим вектор (включая список и выражение) или парный список или 'NULL'".

Таким образом, ваш вектор Date приводится к numeric, потому что объекты Date не являются строго векторами:

is.vector(Sys.Date())
# [1] FALSE
is.vector(as.numeric(Sys.Date()))
# [1] TRUE

То же верно и для POSIXct векторов:

is.vector(Sys.time())
# [1] FALSE
is.vector(as.numeric(Sys.time()))
# [1] TRUE
person Joshua Ulrich    schedule 22.06.2011
comment
Довольно близко, но я боюсь, что здесь нет принуждения в цикле for, см. этот ответ... .. - person gagolews; 24.04.2014
comment
@gagolews: Это очень тонкое различие, которое мало кто поймет или оценит. Да, for не удаляет атрибуты и не вызывает coerceVector на итераторе (и потенциально создает копию). Он просто игнорирует атрибуты. В любом случае, практический эффект одинаков. Если бы я сказал, что с вами обращаются, а не принуждают, вам нечего было бы доказывать. - person Joshua Ulrich; 25.04.2014
comment
Было бы менее запутанно признать, что объекты Date являются векторами в том смысле, что большинство людей понимают этот термин, но с утерянным атрибутом. Например, is.atomic(SysDate()) возвращает TRUE. Во всяком случае, действительно удивительным для меня было то, что циклы for проходят по спискам. - person IRTFM; 25.04.2014
comment
@BondedDust, это, вероятно, потому, что если вы сделаете is.vector(as.list(as.Date("2009-01-01"))), он вернет TRUE - person David Arenburg; 25.04.2014
comment
Вот почему иногда полезно изучить исходный код — руководство не объясняет всех нюансов. Действительно, как упоминал Джоушуа, с точки зрения простого пользователя это не так важно. Но хорошо иметь некоторое представление. - person gagolews; 25.04.2014
comment
@gagolews: И не поймите неправильно мой комментарий, это интересный момент, но я просто не думаю, что большинству людей это будет интересно. ;) - person Joshua Ulrich; 25.04.2014

цикл через дни (строки):

     days <- seq(from=as.Date('2011-02-01'), to=as.Date("2011-03-02"),by='days' )
     for ( i in seq_along(days) )
     {
          print(i)
           print(days[i])
      }
person pleo    schedule 24.07.2012

Вы не выбираете правильную функцию для применения к Date векторам при использовании циклов for. Лучше было бы обернуть seq_along практически для каждой даты или фактора, которые зацикливаются. Затем вы сделаете две вещи: а) настройте его так, чтобы вы ожидали индекс, начинающийся с 1, и б) защитите от странных вещей, которые происходят с векторами нулевой длины. Я также думаю, что было бы лучше использовать его с факторами, которые циклы for превратят в векторы символов.

Что касается ответа Джошуа (который, безусловно, правильный и полезный), я думаю, что функция is.vector немного неправильно помечена или, возможно, просто неправильно понята. Его можно было бы точнее назвать hasNoAttributesOtherThanName. Свойство, которое большинство людей считает «векторным», проверяется с помощью is.atomic, а Date и POSIXct объекты возвращают TRUE из этого теста.

person IRTFM    schedule 22.08.2011
comment
+1 особенно за hasNoAttributesOtherThanName, хотя я думаю, что его следует назвать has_no_attributes_other_than_name. ;-) - person Joshua Ulrich; 06.01.2012
comment
Ваша точка «а)» действительна, но в отношении «б)» документы для for говорят, что если seq имеет нулевую длину, тело цикла пропускается. - person Ken Williams; 06.09.2012
comment
В этом случае это может не иметь значения, но в случае, когда человек использовал 1:length(x), он получает итерации, которых не должен хотеть. Безопаснее использовать seq_along(). - person IRTFM; 06.09.2012

Кажется, что функция C, реализующая цикл for, не копирует ни один из атрибутов вектора. Это также включает атрибут class, который должен сделать i объектом Date.

Вы можете изучить исходный код функции do_for(SEXP, SEXP, SEXP, SEXP) (той, что вызывается R’s for) здесь .

person gagolews    schedule 24.04.2014

Это старый вопрос, но я новичок в R и столкнулся с той же проблемой. Поскольку моя проблема будет обрабатываться параллельно, я использовал foreach и увидел, что поведение отличается от обычного for:

library(foreach)

start_date = as.Date("2013-08-1")
end_date = as.Date("2013-08-13")
days = seq(start_date, end_date, by = "day")

foreach(day = days, .combine='rbind') %dopar% {
  print(class(day))
}

[1] "Date"
[1] "Date"
[1] "Date"
[1] "Date"
...

Поскольку я не знаком с внутренними вещами большинства R, поэтому я не знаю, почему foreach в конечном итоге ведет себя иначе, но это сработало для моей цели и, надеюсь, может быть полезно для кого-то еще.

person Eduardo    schedule 31.07.2014

Любая числовая операция с объектами даты обычно возвращает количество дней. В этом вы просите его предоставить вам количество дней с эпохи. 14245 — количество дней с 01.01.1970 по 01.01.2009.

От ?Даты:

Даты представлены как количество дней с 1 января 1970 г. с отрицательными значениями для более ранних дат. Они всегда печатаются по правилам действующего григорианского календаря, хотя этот календарь давно не использовался (он был принят в 1752 году в Великобритании и ее колониях).

Предполагается, что дата должна быть целым числом, но это не обязательно во внутреннем представлении. Дробные дни будут игнорироваться при печати. Можно получить дробные дни с помощью метода среднего или путем добавления или вычитания (см. Ops.Date).

Попробуйте добавить print(day), чтобы понять, что я имею в виду.

test = as.Date("2009-01-01")
print( class( test ) )
for ( day in test )
{
  print(day)
  print( class( day ) )
}
person Brandon Bertelsen    schedule 22.06.2011
comment
хм... какую числовую операцию я выполняю? Я просто использую цикл for. Если я использую цикл for из i:length(test), а затем вызываю test[i], я получаю Date. Для меня не интуитивно понятно, почему for-each приводит к числовому - person SFun28; 22.06.2011
comment
Если вы хотите просмотреть количество дней, используйте for(1:as.numeric(test)) - person Brandon Bertelsen; 22.06.2011
comment
Ваше настоящее заявление просто возвращает количество дней между тестом и 1970-01-01. Или day:test, но на самом деле вам нужно что-то вроде 1:day:test - person Brandon Bertelsen; 22.06.2011
comment
Думаю, я просто не вижу этого. Поправьте меня, если я ошибаюсь, но я читаю (день в тесте), как перебирать каждый элемент в тесте и присваивать значение переменной день - person SFun28; 22.06.2011
comment
@BrandonBertelsen - for(1:as.numeric(test)) не перебирает количество дней в test, он начинается с 1 января 1970 года и отсчитывает вперед количество дней, прошедших до дня test. - person Ken Williams; 07.09.2012
comment
@Ken, установив 1: as.numeric(test), вы указываете, что он должен начинаться с 1, следовательно, с 1 января 1970 года. - person Brandon Bertelsen; 07.09.2012
comment
Верно, но это не всегда полезно. 1970 год был не таким уж замечательным. ;-) @SFun28 был заинтересован в повторении нескольких записей test, а не от какой-то внешней даты начала до test. И если test имеет длину больше 1, 1:as.numeric(test) выдает строгое предупреждение и отбрасывает все элементы, кроме первого. - person Ken Williams; 07.09.2012