Выберите строки, которые не существуют в других df
У меня есть df с идентификатором поездки, идентификатором остановки, отметкой времени и скоростью.
trip_id stop_id speed timestamp
1 1 1 5 1
2 1 1 0 2
3 1 1 0 3
4 1 1 5 4
5 1 2 2 101
6 1 2 2 102
7 1 2 2 103
8 1 2 2 104
9 1 3 4 201
10 1 3 0 202
Я сохранил первую и последнюю строки, где скорость равна нулю для групп, где trip_id и stop_id одинаковы.
df_departure_z <- sqldf("SELECT trip_id, stop_id, MAX(timestamp) FROM df WHERE speed = 0 GROUP BY trip_id,stop_id")
df_arrival_z <- sqldf("SELECT trip_id, stop_id, MIN(timestamp) FROM df WHERE speed = 0 GROUP BY trip_id,stop_id")
Который дал результаты:
df_departure_z:
trip_id stop_id MAX(timestamp)
1 1 1 3
2 1 3 203
df_arrival_z:
trip_id stop_id MIN(timestamp)
1 1 1 2
2 1 3 202
Моя проблема: есть одна остановка (остановка 2), где скорость никогда не равна нулю, и поэтому я хочу найти способ сохранить одну временную метку для остановок, где скорость никогда не равна нулю. Я попробовал это:
df_arr_dep <- sqldf("SELECT trip_id, stop_id, MIN(timestamp) FROM df GROUP BY trip_id, stop_id EXCEPT SELECT trip_id, stop_id FROM df_arrival_z ")
Но это дает мне ошибку, так как я пытаюсь сохранить три столбца на основе значений в двух столбцах в другой df. По сути, я хочу снова просмотреть свой df и найти те комбинации trip_id и stop_id, которых нет в df_departure_z или df_arrival_z. Если я попытаюсь с помощью SELECT *, я получу все строки, которые не были сохранены, что также неверно.
2 ответа
Можете ли вы использовать другие библиотеки, кроме sqldf
? Я думаю, что следующее выполняет то, что вы ищете, используя dplyr
:
library(dplyr)
dat %>%
group_by(trip_id, stop_id) %>%
filter(speed == 0 | sum(speed == 0) == 0) %>%
summarize(min_time = min(timestamp),
max_time = if_else(sum(speed == 0) == 0,
NA_real_,
max(timestamp)))
# A tibble: 3 x 4
# Groups: trip_id [?]
trip_id stop_id min_time max_time
<int> <int> <dbl> <dbl>
1 1 1 2 3
2 1 2 101 NA
3 1 3 202 202
данные
dat <- structure(list(trip_id = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L),
stop_id = c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 3L, 3L),
speed = c(5L, 0L, 0L, 5L, 2L, 2L, 2L, 2L, 4L, 0L),
timestamp = c(1L, 2L, 3L, 4L, 101L, 102L, 103L, 104L, 201L, 202L)),
.Names = c("trip_id", "stop_id", "speed", "timestamp"),
row.names = c(NA, -10L),
class = "data.frame")
Если я правильно понимаю каждую поездку и остановку, вы хотите, чтобы в строке была максимальная временная метка, для которой скорость равна нулю, или если такой строки нет, то в строке максимальной временной отметки для строк, имеющих скорость, отличную от 0, в этой группе. Далее мы сделаем альтернативное предположение, что в случае отсутствия скоростных рядов в группе просто используйте NA. После этого мы обсуждаем запрос EXCEPT в вопросе.
В первом случае выше, сгруппируйте по отключению, остановке и скорости == 0. Это даст 2 строки за отключение и остановку, если есть скорости 0 и не 0, и даст 1 отключение и остановку по ряду, если есть только не -0 скоростей. В каждой группе мы берем строку для скорости == 0 является наибольшим. Так как TRUE > FALSE, тогда, если есть две строки, он будет принимать строку, для которой скорость равна 0, а в противном случае он будет принимать одну ненулевую строку скорости.
sqldf("SELECT trip_id, stop_id, timestamp, MAX(speed0) speed0
FROM
(SELECT trip_id, stop_id, MAX(timestamp) timestamp, speed == 0 speed0
FROM df
GROUP BY 1, 2, 4)
GROUP BY 1, 2")
давая:
trip_id stop_id timestamp speed0
1 1 1 3 1
2 1 2 104 0
3 1 3 202 1
1 в строке 1 для speed0 означает, что строка скорости == 0 была найдена для этой группы, поэтому для этой группы использовалась максимальная временная метка, равная только строке скорости == 0. Аналогично, в строке 2 значение 0 для speed0 означает, что для этой группы не было найдено строки speed == 0, поэтому она использовала максимальную временную метку для строк, отличных от 0 в этой группе.
Если вам не нужен четвертый столбец, просто добавьте [-4] после окончания) .
Альтернативная интерпретация
Если вы хотели, чтобы в этих строках был NA, для которого нет скорости == 0, то просто замените первую строку в sql выше, как показано здесь:
sqldf("SELECT trip_id, stop_id, NULLIF(MAX(speed0) * timestamp, 0) timestamp
FROM
(SELECT trip_id, stop_id, MAX(timestamp) timestamp, speed == 0 speed0
FROM df
GROUP BY 1, 2, 4)
GROUP BY 1, 2")
давая:
trip_id stop_id timestamp
1 1 1 3
2 1 2 NA
3 1 3 202
Другой способ сделать это - левое соединение, дающее тот же результат:
sqldf("WITH a(trip_id, stop_id) AS (
SELECT distinct trip_id, stop_id
FROM df),
b(trip_id, stop_id, timestamp) AS (
SELECT trip_id, stop_id, MAX(timestamp) timestamp
FROM df
WHERE speed == 0
GROUP BY 1, 2)
SELECT *
FROM a LEFT JOIN b
USING (trip_id, stop_id)")
ИСКЛЮЧИТЬ против НЕ СУЩЕСТВУЕТ
Что касается последней строки кода в вопросе, касающейся ИСКЛЮЧЕНИЯ, это будет сделано с использованием следующего коррелированного подзапроса, показанного с НЕ СУЩЕСТВУЮЩИМ, как это:
sqldf("SELECT a.trip_id, a.stop_id, MIN(a.timestamp) timestamp
FROM df a
WHERE NOT EXISTS (
SELECT *
FROM df b
WHERE speed == 0 AND a.trip_id = b.trip_id AND a.stop_id = b.stop_id)
GROUP by 1, 2")
давая:
trip_id stop_id timestamp
1 1 2 101
Заметка
Мы предполагаем, что этот вход показан в воспроизводимой форме:
Lines <- "
trip_id stop_id speed timestamp
1 1 1 5 1
2 1 1 0 2
3 1 1 0 3
4 1 1 5 4
5 1 2 2 101
6 1 2 2 102
7 1 2 2 103
8 1 2 2 104
9 1 3 4 201
10 1 3 0 202"
df <- read.table(text = Lines)