Получите первое и последнее значения из длинной таблицы путем перестановки на основе даты

В настоящее время я работаю над этим файлом, в котором перечислены административные события, произошедшие в коммунах (местных органах власти) с 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 строк:

"DATE_EFF" "COM_AV" "COM_AP" 1973-01-01 "64031" "64113" 1968-01-01 "78675" "95675" 01.01.2017 "86030" "86281" 01.09.2002 "07021" "07207" 01.01.2005 "52359" "52495" 1969-12-01 "25423" "25138" 1973-01-01 "79232" "79123" 1973-04-01 "70209" "70528" 17 апреля 2014 г. "05002" "05139" 15 ноября 1977 г. "64113" "64031"

За этот период каждая коммуна могла претерпеть ряд изменений.

Например, в примере выше мы видим, что:

  1. 64031 стал 64113 в 1973-01-01
  2. 64113 стал 64031 в 1977-11-15

Я хотел бы иметь таблицу, в которой COM_AV содержит первое значение, а COM_AP содержит последнее значение как таковое:

"COM_AV" "COM_AP" "64031" "64031"

Это предполагает, что несколько COM_AV могут указывать на одно и то же COM_AP и, конечно, COM_AV не всегда совпадает с COM_AP.

Какой был бы наиболее эффективный способ решить эту проблему с помощью tidyverse?

Рассмотрите эту проблему как задачу графа.

Friede 08.07.2024 13:20

@Фриде, кажется, я понял, как решить эту проблему с помощью итерации, как предложено в решении ниже.

pure_func 08.07.2024 14:35
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
70
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

При достаточно небольшом наборе данных один из способов — использовать итерацию.

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() в следующий раз!

pure_func 08.07.2024 14:32

Также @Edward, если у вас есть какие-либо предложения по рекурсивному решению, мне было бы интересно их услышать!

pure_func 08.07.2024 14:38
Ответ принят как подходящий

Я попытался решить эту проблему самостоятельно после публикации этого вопроса, и вот мое текущее предложение:

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?

Edward 08.07.2024 16:27

@Эдвард, отличная мысль, я думаю, это из-за dplyr::full_join(). Если я это сделаю res <- dplyr::distinct(res), у меня будет 5338 строк, что меньше исходного количества уникальных значений COM_AV в data. Я соответствующим образом обновил приведенный выше код.

pure_func 09.07.2024 09:42

ок, но ваш результат все еще содержит дубликаты для COM_AV. Например, «01003», но я заметил, что исходные данные тоже имеют дубликаты. Я проигнорировал это, а вы нет. Я также нашел в данных строку, которую вы не нашли: COM_AV= = "01091". пока мой код находит его. Итак, наши результаты все еще различаются. Я не говорю, что мой код правильный, просто наши результаты различаются.

Edward 09.07.2024 09:54

@Эдвард, спасибо за предупреждение, я проверю, почему они различаются, и вернусь к вам с более подробной информацией, если смогу это выяснить.

pure_func 09.07.2024 11:58

@Эдвард, я согласен, что твое решение наконец-то стало более кратким и элегантным, но наше обсуждение заставило меня осознать два исключения, которые необходимо было принять во внимание в моем собственном коде. Во-первых, если две коммуны сливаются и снова разделяются, вам нужно лишь сохранить исходное значение. Например, x -> a & y -> a и после a -> x & a -> y, затем x -> x & y -> y. Также следует добавить строки, для которых сработало объединение, поэтому COM_AV == "01091" не отображался в предыдущей версии. Я соответствующим образом обновил приведенный выше код, еще раз спасибо за ваши комментарии!

pure_func 09.07.2024 16:54

Другие вопросы по теме