【问题标题】:Loop/if else in R for data frame在R中循环/如果其他数据框
【发布时间】:2017-02-27 17:29:30
【问题描述】:

我真的坚持在 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 辆公共汽车和 1 辆火车。

我想创建第 5 列,用于标识旅程阶段是否链接,即第二旅程是否链接到第一旅程,第三旅程是否链接到第二旅程(其中 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

任何帮助将不胜感激!

【问题讨论】:

  • 非常感谢您自己表现出任何努力!
  • 我真的什么都没有

标签: r loops if-statement dataframe


【解决方案1】:

1) 大道试试这个:

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)

更新:已修复。

【讨论】:

  • 感谢您的回复 - 有没有办法避免阅读表格?桌子很大,所以这不是正确的做法
  • R 仅适用于内存中的数据帧,因此您必须加载数据。如果您的数据在数据库中,您可以尝试使用例如dplyr 包。它将 R 语句转换为在数据库连接内运行的 SQL,然后只返回您感兴趣的子集。
  • 已添加 sqldf 解决方案。就目前而言,它假设 DF 在内存中,但您可能会根据您的数据库设置调整 SQL。
【解决方案2】:

我喜欢 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
  }
}

【讨论】:

  • 你不想要df$linked[i] 而不是df$linked 吗?另外:逻辑+1,但请注意,这可能比@G.Grothendieck 的回答慢很多倍...
  • 当然 G. Grothendieck 可以不那么苛刻,但 R 实际上鼓励您使用矢量化操作,例如 diff()cumsum()。对于 R 的新手来说,向量化操作也很容易解释。
  • 这是我正在尝试的,但是,我无法让它工作,因为它显示错误:
  • 错误:意外 '
  • 我对此稍作修改,但每条记录在链接列中返回 1。我不确定为什么,因为我认为这似乎是合乎逻辑的方法。我适应了使用“和”语句,但仍然得到同样的结果
【解决方案3】:

使用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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-24
    • 1970-01-01
    • 2019-03-18
    • 1970-01-01
    • 2012-11-06
    相关资源
    最近更新 更多