我真的坚持在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
非常感谢任何帮助!
答案 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。