#r #loops #if-statement #dataframe
#r #циклы #if-оператор #фрейм данных
Вопрос:
Я действительно застрял на выполнении цикла в R. Я тоже пытался использовать ifelse, но, похоже, просто не могу получить результат.
У меня есть следующий фрейм данных, который показывает идентификатор клиента, дату его поездки, режим и время начала поездки:
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 шины jny и 1 поезд jny.
Я хочу создать 5-й столбец, в котором указывается, связаны ли этапы путешествия, т.Е. Связано ли 2-е путешествие с 1-м путешествием, связано ли 3-е путешествие со 2-м путешествием (где 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
Любая помощь будет высоко оценена!
Комментарии:
1. Было бы очень полезно показать какие-либо усилия самостоятельно!
2. У меня буквально ничего с этим не получилось
Ответ №1:
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. Оцените ответ — есть ли способ избежать чтения таблицы? Таблица довольно большая, поэтому это не кажется правильным
2. R работает только с фреймами данных в памяти, поэтому вам нужно загрузить данные. Если ваши данные находятся в базе данных, вы можете попробовать использовать, например
dplyr
, пакет. Он преобразует операторы R в SQL, выполняемые внутри подключения к базе данных, а затем возвращает только то подмножество, которое вас интересует.3. Добавлено решение sqldf. В его нынешнем виде предполагается, что DF находится в памяти, но вы, вероятно, могли бы адаптировать SQL к настройке вашей базы данных, какой бы она ни была.
Ответ №2:
Мне нравится ответ Гротендика, но это может быть не так просто интерпретировать для кого-то нового для R. Итак, давайте сделаем это менее программно эффективным способом, который покажет вам шаги, которые нужно предпринять. Я буду использовать то же соглашение об именовании фреймов данных, что и Гротендик.
Давайте определим, находится ли время между переходами в пределах 60 минут. Позволяет перебирать все строки в фрейме данных, и если это одна и та же учетная запись, и если они имеют одинаковый тип режима, то проверьте, находятся ли они на расстоянии менее 60 минут друг от друга, и если все три условия выполняются, тогда установите значение linked равным 1. В противном случае мы установим linked равным 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
}
}
Комментарии:
1. разве вы не хотите
df$linked[i]
, а неdf$linked
? Также: 1 для логики, но обратите внимание, что это, вероятно, будет во много раз медленнее, чем @G. Ответ Гротендика …2. Конечно
G. Grothendieck
, можно сделать менее критичным, но R на самом деле побуждает вас использовать векторизованные операции, такие какdiff()
иcumsum()
. Векторизованные операции также могут быть легко интерпретированы для кого-то, кто не знаком с R.3. Это то, что я пробовал, однако я не могу заставить его работать, потому что в нем говорится об ошибке:
4. Ошибка: неожиданное ‘<‘ в: » if (df $MODE[i]==df $MODE[i-1]) { if (df $TIME[i]-df $TIME[i-1])<»
5. Я немного изменил это, но каждая запись возвращает 1 в связанном столбце. Я не уверен, почему, поскольку я думал, что это кажется логичным подходом. Я адаптировался к использованию операторов ‘and’, но все равно получил то же самое
Ответ №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.