В настоящее время я работаю над этим файлом, в котором перечислены административные события, произошедшие в коммунах (местных органах власти) с 1943 года во Франции.
Меня интересуют переменные DATE_EFF
, COM_AV
и COM_AP
, а точнее наблюдения, где COM_AV
отличается от COM_AP
:
data <-
readr::read_csv("v_mvt_commune_2024.csv") |>
dplyr::select(DATE_EFF, COM_AV, COM_AP) |>
dplyr::filter(COM_AV != COM_AP)
Вот образец из 10 строк:
За этот период каждая коммуна могла претерпеть ряд изменений.
Например, в примере выше мы видим, что:
64031
стал 64113
в 1973-01-01
64113
стал 64031
в 1977-11-15
Я хотел бы иметь таблицу, в которой COM_AV
содержит первое значение, а COM_AP
содержит последнее значение как таковое:
Это предполагает, что несколько COM_AV
могут указывать на одно и то же COM_AP
и, конечно, COM_AV
не всегда совпадает с COM_AP
.
Какой был бы наиболее эффективный способ решить эту проблему с помощью tidyverse
?
@Фриде, кажется, я понял, как решить эту проблему с помощью итерации, как предложено в решении ниже.
При достаточно небольшом наборе данных один из способов — использовать итерацию.
f <- function(data) {
# Create a minimum data
data_1 <- slice_min(data, DATE_EFF, by=COM_AV, with_ties=FALSE, n=1)
for(i in 1:nrow(data_1)) {
AP <- data_1$COM_AP[i]
# Find rows in the whole data containing this AP value in COM_AV
AP_2 <- filter(data, COM_AV==AP)
# While rows exist in the data with COM_AV containing this AP value
while(nrow(AP_2)>0) {
# Find the row with the minimum value for DATE_EFF
AP_2 <- slice_min(AP_2, DATE_EFF, by=COM_AV, with_ties=FALSE, n=1)
# Save the date
DATE_EFF <- pull(AP_2, DATE_EFF)
# update COM_AP with the last value found
data_1$COM_AP[i] <- pull(AP_2, COM_AP)
# Try to find another
AP_2 <- filter(data, COM_AV==AP & DATE_EFF>DATE_EFF)
}
}
data_1
}
data_2 <- f(data)
filter(data_2, COM_AV= = "64031")
# A tibble: 1 × 3
DATE_EFF COM_AV COM_AP
<date> <chr> <chr>
1 1973-01-01 64031 64031
Большое спасибо @Edward, я сам нашел довольно похожее решение, но ваш пост напомнил мне использовать dplyr::slice_min()
в следующий раз!
Также @Edward, если у вас есть какие-либо предложения по рекурсивному решению, мне было бы интересно их услышать!
Я попытался решить эту проблему самостоятельно после публикации этого вопроса, и вот мое текущее предложение:
data <-
readr::read_csv("v_mvt_commune_2024.csv") |>
dplyr::select(DATE_EFF, COM_AV, COM_AP) |>
dplyr::group_by(COM_AV) |>
dplyr::filter(DATE_EFF == max(DATE_EFF)) |>
dplyr::ungroup() |>
dplyr::arrange(DATE_EFF, COM_AV)
dates <-
data |>
dplyr::arrange(DATE_EFF) |>
dplyr::distinct(DATE_EFF) |>
dplyr::pull()
res <-
data |>
dplyr::filter(DATE_EFF == dplyr::first(dates)) |>
dplyr::select(!DATE_EFF)
for (i in 2:length(dates)) {
curr_data <-
data |>
dplyr::filter(DATE_EFF == dplyr::nth(dates, n = i)) |>
dplyr::select(!DATE_EFF)
res <-
res |>
dplyr::full_join(curr_data, dplyr::join_by(COM_AP == COM_AV)) |>
dplyr::mutate(
COM_AV = dplyr::if_else(!is.na(COM_AV), COM_AV, COM_AP),
COM_AP = dplyr::if_else(!is.na(COM_AP.y), COM_AP.y, COM_AP)
) |>
dplyr::select(!dplyr::contains("."))
# If two communes merge and split again, keep only the original value
eq <-
res |>
dplyr::filter(COM_AV == COM_AP) |>
dplyr::distinct(COM_AV) |>
dplyr::pull()
# If join_by conditions are matched, add the corresponding lines to res as well
com_ap_after_join <-
res |>
dplyr::distinct(COM_AP) |>
dplyr::arrange() |>
dplyr::pull()
to_bind <-
curr_data |>
dplyr::filter(COM_AP %in% com_ap_after_join)
res <-
res |>
dplyr::filter((COM_AV %in% eq & COM_AV == COM_AP) | !COM_AV %in% eq) |>
dplyr::bind_rows(to_bind)
}
res <-
res |>
dplyr::filter(COM_AV != COM_AP) |>
dplyr::distinct() |>
dplyr::arrange(COM_AV)
Ваш результат содержит больше строк, чем исходные данные. Разве он не должен содержать количество уникальных значений в COM_AV?
@Эдвард, отличная мысль, я думаю, это из-за dplyr::full_join()
. Если я это сделаю res <- dplyr::distinct(res)
, у меня будет 5338 строк, что меньше исходного количества уникальных значений COM_AV
в data
. Я соответствующим образом обновил приведенный выше код.
ок, но ваш результат все еще содержит дубликаты для COM_AV. Например, «01003», но я заметил, что исходные данные тоже имеют дубликаты. Я проигнорировал это, а вы нет. Я также нашел в данных строку, которую вы не нашли: COM_AV= = "01091". пока мой код находит его. Итак, наши результаты все еще различаются. Я не говорю, что мой код правильный, просто наши результаты различаются.
@Эдвард, спасибо за предупреждение, я проверю, почему они различаются, и вернусь к вам с более подробной информацией, если смогу это выяснить.
@Эдвард, я согласен, что твое решение наконец-то стало более кратким и элегантным, но наше обсуждение заставило меня осознать два исключения, которые необходимо было принять во внимание в моем собственном коде. Во-первых, если две коммуны сливаются и снова разделяются, вам нужно лишь сохранить исходное значение. Например, x -> a
& y -> a
и после a -> x
& a -> y
, затем x -> x
& y -> y
. Также следует добавить строки, для которых сработало объединение, поэтому COM_AV == "01091"
не отображался в предыдущей версии. Я соответствующим образом обновил приведенный выше код, еще раз спасибо за ваши комментарии!
Рассмотрите эту проблему как задачу графа.