Цикл по факторам 2 уровня - для каждого фактора и каждой даты

У меня есть много данных, в которых у меня есть 5 переменных: тема, дата, дата + час, мера, которая является концентрацией и кормлением.

Итак, для каждого субъекта мы провели некоторые измерения от даты+часа(1) до даты+часа(n). Итак, у нас есть n измерений для каждого субъекта. Что я хотел бы сделать, так это рассчитать время записи для каждой строки, выполнив для каждого субъекта date+hour[i]-date+hour1. Для этого я сделал петлю. Это работало хорошо, пока я не понял, что у меня есть несколько дней записи по каждому предмету. Это означает, что я должен рассчитать для каждого предмета и каждой даты время записи.

Это мой сценарий:

    getwd()
    setwd("H:/OptiMIR LMD files/week1")

    Week1<-read.csv("week1.csv", header=T)
    head(Week1)
    colnames(Week1)<-c("CowID","Date", "DateHour","Measure","Feeding")
    head(Week1)


    #Association colums with class
    Week1$CowID<-as.factor(Week1$CowID)
    Week1$Date<-as.Date(Week1$Date, format = "%d/%m/%Y")
    Week1$DateHour<-strptime(Week1$DateHour, format = "%Y/%m/%d/%H:%M:%S")
    Week1$Measure<-as.numeric(as.vector(Week1$Measure))
    Week1$Feeding<-as.factor(Week1$Feeding)
    str(Week1)

    summary(Week1)
    unique(Week1$CowID) 

    #Calculate Time of measure
    library(lubridate)
    library(foreach)

    Time<-c()
    #nrow(LMD)
    for (i in 1:nrow(Week1)) {
      for (j in unique(Week1$CowID)) {
        for (k in unique(Week1$Date)) {
          if (Week1$CowID[i]==j & Week1$Date[i]==k) {
            foreach(unique(Week1$CowID) & unique(Week1$Date))
            Time[i]<-c(difftime(Week1[i,3], Week1[match(k,Week1$Date),3], units="secs"))
          }
        }
      }
    }

    Week1<-cbind(Week1,Time)​

Вот заголовок и резюме:

> head(Week1)
  CowID       Date            DateHour Measure Feeding
1  1990 2014-01-13 2014-01-13 16:21:02     119    hoko
2  1990 2014-01-13 2014-01-13 16:21:02     116    hoko
3  1990 2014-01-13 2014-01-13 16:21:03     111    hoko
4  1990 2014-01-13 2014-01-13 16:21:03      77    hoko
5  1990 2014-01-13 2014-01-13 16:21:04      60    hoko
6  1990 2014-01-13 2014-01-13 16:21:04      65    hoko​

> summary(Week1)
     CowID            Date               DateHour                  
 2239   : 1841   Min.   :2014-01-13   Min.   :2014-01-13 14:33:05  
 2067   : 1816   1st Qu.:2014-01-13   1st Qu.:2014-01-13 16:10:14  
 2246   : 1797   Median :2014-01-14   Median :2014-01-14 15:10:51  
 2062   : 1792   Mean   :2014-01-13   Mean   :2014-01-14 14:55:45  
 2248   : 1757   3rd Qu.:2014-01-15   3rd Qu.:2014-01-15 14:32:59  
 2171   : 1738   Max.   :2014-01-15   Max.   :2014-01-15 15:55:09  
 (Other):14259                                                     
    Measure        Feeding     
 Min.   :   4.0   hoko :16857  
 1st Qu.:  65.0   strap: 8143  
 Median : 108.0                
 Mean   : 147.4                
 3rd Qu.: 185.0                
 Max.   :1521.0              ​

Так что на 1990 год у меня будут другие даты записи. И это моя проблема, потому что этот цикл:

Time<-c()
for (i in 1:nrow(Week1) {
  for (j in unique(Week1$CowID)) {
    for (k in min(Week1$Date):max(Week1$Date)) {
      if ((week1$CowID[i]==j) & (Week1$Date[i]==k)) {
        Time[i]<-c(difftime(Week1[i,3], Week1[match(k, Week1$Date),3], units="secs"))
      }
    }
  }
}

работает, когда у меня есть один день измерения / предмета. а сейчас у меня несколько дней записи, по одной теме работает, а по другой теме у меня отрицательное время записи...

Кажется, я знаю, в чем проблема: в цикле "for k...". Я должен сказать R, что он должен смотреть на одну дату ДЛЯ каждого уникального предмета. Но я не знаю, как это сделать

Спасибо


person Marie    schedule 13.10.2016    source источник
comment
Эти петли - сложный способ сделать это. Самый простой способ — dplyr или data.table. Используя dplyr, я думаю, что вы хотите group_by(Week1, CowID, Date) %>% mutate(Time = DateHour - min(DateHour)), но трудно сказать наверняка. Можете ли вы показать желаемый результат для head данных, которые вы показываете?   -  person Gregor Thomas    schedule 13.10.2016
comment
Хорошо.. Я собираюсь исследовать это... Первые значения, которые я могу получить для вектора Time: [1] 0 0 1 1 2 2 3 4 4 5 5 6 [13] 6 7 7 8 8 9 9 10 10 11 11 12 [25] 12 13 13 14 15 15 16 16 17 17 18 18 [37] 19 19 20 20 21 21 22 22 23 23 24 24 Это правильно, но когда это новый субъект (cowID), он дает неправильный результат, как будто он не принял во внимание, что это был другой CowID   -  person Marie    schedule 13.10.2016
comment
Если вы хотите исправить свой код цикла, я думаю, что самая большая проблема заключается в том, что ваш самый внешний цикл охватывает все строки. Вы как бы работаете над этим с помощью match, но более естественным способом сделать это с помощью циклов было бы использование групп в качестве внешних циклов, а затем самый внутренний цикл проходит по каждой строке в группе.   -  person Gregor Thomas    schedule 13.10.2016
comment
Не помещайте вывод в комментарии, это трудно понять. Отредактируйте его в своем вопросе (желательно добавив его во фрейм данных), чтобы мы могли видеть. Вы также можете ознакомиться с советами по созданию воспроизводимых примеров. Это довольно хороший вопрос, но было бы еще лучше, если бы ваши данные воспроизводились совместно, что-то вроде dput(droplevels(head(Week1, 10))) или какого-то другого небольшого подмножества, в котором есть пара коров и пара дней, достаточно, чтобы проиллюстрировать проблему. Вывод dput() выглядит уродливо, но его можно скопировать/вставить в R, чтобы воссоздать ваши данные.   -  person Gregor Thomas    schedule 13.10.2016
comment
Хорошо, спасибо, я обновлю это. Я попробовал group_by, и появляется ошибка: Ошибка в eval (expr, envir, enclos): столбец «DateHour» имеет неподдерживаемый класс: POSIXlt, POSIXt   -  person Marie    schedule 13.10.2016
comment
Чтобы dplyr заработало, вам нужно преобразовать POSIXlt в POSIXct как показано здесь.   -  person Gregor Thomas    schedule 13.10.2016
comment
Ничего себе, это работает отлично и это супер быстро! Данные, над которыми я работаю, содержат 130 000 наблюдений, надеюсь, это будет так быстро! Спасибо. Это было супер просто!   -  person Marie    schedule 13.10.2016


Ответы (1)


Циклы for — плохой способ группового выполнения операций в R. data.table и dplyr обеспечивают более быстрые и удобные альтернативы:

library(dplyr)
group_by(Week1, CowID, Date) %>% 
    mutate(Time = DateHour - min(DateHour))

Обратите внимание, что если ваши столбцы даты и времени относятся к классу POSIXlt, вам нужно будет сначала преобразовать их в POSIXct с помощью as.POSIXct().

person Gregor Thomas    schedule 13.10.2016
comment
Спасибо! Но у меня проблема с моей строкой (Week1), появляется много новых терминов (см. в посте) - person Marie; 14.10.2016