在R中循环/ if else用于数据框

时间:2016-10-18 12:27:48

标签: r loops if-statement dataframe

我真的坚持在R中做一个循环。我也尝试过使用ifelse,但似乎无法得到结果。

我有一个如下数据框,显示客户ID,旅行日期,模式和旅程开始时间:

ID     |  Date     |   Mode  |  Time
------ | --------- | ------- | -----
1234   | 12/10/16  | Bus     |  120 
1234   | 12/10/16  | Bus     |  130
1234   | 12/10/16  | Bus     |  290
1234   | 12/10/16  | Train   |  310
1234   | 12/10/16  | Bus     |  330
4567   | 12/10/16  | Bus     |  220 
4567   | 12/10/16  | Bus     |  230
4567   | 13/10/16  | Bus     |  290
4567   | 13/10/16  | Bus     |  450
4567   | 14/10/16  | Train   |  1000

所以在12月10日,客户1234制作了4辆公共汽车jnys和1辆火车jny。

我想创建一个第5列,用于标识旅程阶段是否已链接,即与第1次旅程相关联的第2次旅程,是与第2次旅程相关联的第3次旅程(其中1 =已关联,0 =未链接)。

以下条件需要适用:

  • jnys是针对同一个人并在同一天举行

  • 2次巴士行程相距不超过60分钟(因此公共汽车和火车之间的距离不会相隔60分钟)

  • 如果i + 1和第i次旅程相关联,则第i + 1次旅程无法与第i + 2次旅程相关联

我希望输出如下:

ID     |  Date     |   Mode  |  Time  | Linked
------ | --------- | ------- | -----  | -----
1234   | 12/10/16  | Bus     |  120   |  0
1234   | 12/10/16  | Bus     |  130   |  1
1234   | 12/10/16  | Bus     |  290   |  0
1234   | 12/10/16  | Train   |  310   |  0
1234   | 12/10/16  | Bus     |  330   |  0
4567   | 12/10/16  | Bus     |  220   |  0
4567   | 12/10/16  | Bus     |  230   |  1
4567   | 13/10/16  | Bus     |  290   |  0
4567   | 13/10/16  | Bus     |  450   |  0
4567   | 14/10/16  | Train   |  1000  |  0

非常感谢任何帮助!

3 个答案:

答案 0 :(得分:3)

1)ave 试试这个:

transform(DF, linked = ave(Time, ID, Date, cumsum(c(FALSE, Mode[-1] != Mode[-nrow(DF)])), 
      FUN = function(x) c(0, diff(x) < 60)))

,并提供:

     ID     Date  Mode Time linked
1  1234 12/10/16   Bus  120      0
2  1234 12/10/16   Bus  130      1
3  1234 12/10/16   Bus  290      0
4  1234 12/10/16 Train  310      0
5  1234 12/10/16   Bus  330      0
6  4567 12/10/16   Bus  220      0
7  4567 12/10/16   Bus  230      1
8  4567 13/10/16   Bus  290      0
9  4567 13/10/16   Bus  450      0
10 4567 14/10/16 Train 1000      0

2)sqldf 以下是使用sqldf的解决方案。

library(sqldf)
sqldf("select a.*, coalesce(a.ID = b.ID and 
                            a.Date = b.Date and 
                            a.Mode = b.Mode and 
                            a.Time < b.Time + 60, 0) linked 
       from DF a left join DF b on a.rowid = b.rowid + 1")

3)data.table 请注意,data.table往往具有快速和内存效率,并且可以处理内存中的数据大小,而其他方法则无法处理。

library(data.table)

dt <- as.data.table(DF)
dt[, linked := (Time < shift(Time, fill = -60) + 60) * 
               (Mode == shift(Mode, fill = Mode[1])), by = "ID,Date"]

4)dplyr

library(dplyr)
DF %>% 
   group_by(ID, Date) %>%
   mutate(linked = (Time < lag(Time, default = -Inf) + 60) * 
                   (Mode == lag(Mode, default = Mode[1]))) %>%
   ungroup()

给出类似的答案。

注意:可重复形式的输入DF为:

Lines <- 
"ID     |  Date     |   Mode  |  Time
------ | --------- | ------- | -----
1234   | 12/10/16  | Bus     |  120 
1234   | 12/10/16  | Bus     |  130
1234   | 12/10/16  | Bus     |  290
1234   | 12/10/16  | Train   |  310
1234   | 12/10/16  | Bus     |  330
4567   | 12/10/16  | Bus     |  220 
4567   | 12/10/16  | Bus     |  230
4567   | 13/10/16  | Bus     |  290
4567   | 13/10/16  | Bus     |  450
4567   | 14/10/16  | Train   |  1000"
DF <- read.table(text = Lines, header = TRUE, sep = "|", strip.white = TRUE,
 comment = "-", as.is = TRUE)

更新:已修复。

答案 1 :(得分:1)

我喜欢Grothendieck的答案,但对于R新手来说可能并不那么容易理解。所以让我们以一种不那么编程有效的方式来表达你要采取的步骤。我将使用与Grothendieck相同的数据框命名约定。

让我们确定行程之间的时间是否在60分钟之内。让循环遍历数据框中的所有行,如果它们是相同的帐户,并且如果它们是相同类型的模式,则检查它们是否相隔不到60分钟,如果所有三个条件都检出,则设置链接到1.否则,我们将设置链接到0。

for (i in 2:dim(df)[1]){
  if (df$ID[i]==df$ID[i-1]){
    if (df$Mode[i]==df$Mode[i-1]){
      if ((df$Time[i]-df$Time[i-1]) < 60){
        df$linked[i] <- 1
      }
      else {
        df$linked[i] <- 0
      }
    }
    else {
      df$linked[i] <- 0
    }
  }
  else {
    df$linked[i] <- 0
  }
}

答案 2 :(得分:0)

使用dplyr包:

library(dplyr)
DF %>%
    # The journeys are for the same person, take place on the same day
    # and on the same mode of transport
    group_by(ID, Date, Mode) %>% 
    # 2 bus journeys are within 60 mins of one another 
    mutate(linked0 = c(Inf, diff(Time))<60, 
           # if the i+1th and the ith journey are linked, 
           # then the i+1th journey cannot be linked to the i+2th journey
           linkedsum = cumsum(linked0),
           linked = ifelse(linkedsum==1, linked0, 0))

      ID     Date  Mode  Time linked0 linkedsum linked
   <int>    <chr> <chr> <int>   <lgl>     <int>  <dbl>
1   1234 12/10/16   Bus   120   FALSE         0      0
2   1234 12/10/16   Bus   130    TRUE         1      1
3   1234 12/10/16   Bus   290   FALSE         1      0
4   1234 12/10/16 Train   310   FALSE         0      0
5   1234 12/10/16   Bus   330    TRUE         2      0
6   4567 12/10/16   Bus   220   FALSE         0      0
7   4567 12/10/16   Bus   230    TRUE         1      1
8   4567 13/10/16   Bus   290   FALSE         0      0
9   4567 13/10/16   Bus   450   FALSE         0      0
10  4567 14/10/16 Train  1000   FALSE         0      0

要在数据库中执行此操作,请参阅dplyr database vignette