В R: извлечение данных из столбца на основе ближайшей даты

У меня есть два набора данных. Первый набор данных является основным, широким и имеет идентификатор как уникальную/идентифицирующую переменную. Второй набор данных имеет ту же переменную id, длинный и имеет несколько наблюдений для id (т. е. id не уникален).

Второй набор данных имеет только три переменные: идентификатор, дату и значение. Идентификатор и дата вместе также не уникальны — есть повторяющиеся комбинации. Переменная значения представляет собой число. Досадно, что некоторые повторы идентификатора/даты имеют разные числа в поле значения, поэтому мне нужно их идентифицировать, чтобы я мог вручную выбрать нужное мне значение.

Моя конечная цель — скопировать поле значения из второго набора данных в первый, используя наблюдение, дата которого наиболее близка к ключевой дате из первого набора данных. Например, предположим, что в первом наборе данных id==1 имеет контрольную дату 1 мая 2024 года. Затем во втором наборе данных есть одна строка с id==1, датой==30 апреля 2024 г. и значением==3; и есть еще одна строка с id==1, датой==05.05.2024 и значением==4. Я хочу, чтобы 3 было перенесено в первый набор данных в новый столбец для id==1.

https://github.com/markelphelps/sample-data-1 Проверьте файлы в моем github. Второй набор данных начинался как df2_long, и я попытался преобразовать его в df2_wide. Вместо этого лучшее, что я мог сделать, это:

df_long2 <- reshape(df_long,
                       timevar = "date",
                       idvar = "id",
                       direction = "wide")

Я надеялся, что появятся два новых столбца: дата и значение. Вместо этого он создает новый столбец для каждой уникальной даты. Использование Pivot_wider дало мне список столбцов из-за повторов в дате.

Я получаю ошибку «несколько строк для каждой даты»; Я предполагаю, что самый простой способ решить эту проблему — удалить некоторые из них перед изменением формы, но я не знаю, как это сделать. Что-то вроде «распечатайте каждую строку df, где идентификаторы и даты равны». Может быть, что-то с циклом for или lapply/sapply, которое повторяет команду для каждого уникального значения идентификатора.

Далее мне нужно найти ближайшую дату

which.min(abs(df1_main$key_date - df2_long$date))

Я читал, что abs() необходим для того, чтобы df2_long$date находился иногда до, а иногда и после df1_main$key_date. Но я получаю сообщение об ошибке: «abs не определен для объектов «Дата»». Когда я удаляю abs(), команда возвращает «целое число (0)», что не кажется очень полезным.

Следующий шаг после нахождения ближайшей даты — сделать это для всех идентификаторов, а затем превратить их в новую переменную.

изменить: ухожу с работы на день, проверю/отвечу на комментарии завтра

edit2: прошу прощения за путаницу в именах df, я забыл переименовать их в своем github. как вы и предполагали, df1_main == df ex_original; df2_long == df ex_long; и df3_wide == df ex_wide. Я добавил переименованные версии (полные дубликаты) на случай, если это станет понятнее для всех.

редактировать 3: всем спасибо, очень информативно! Лучше всего для моих данных был комментарий @Eugenio.Gastelum96, направляющий меня сюда: https://stackoverflow.com/a/63754287/9588300. Комментарий @teru о выполнении leftjoin() с dplyr отлично работал с примерами данных. На следующей неделе я опробую это на своих реальных данных и проверю, если что-то еще не сработает.

Я предполагаю, что из двух фреймов данных в вашем git df ex_original.csv — это первый фрейм данных, который вы описываете в первом абзаце (я назову его df1). А df ex_long.csv — второй (я назову его df2). Если вы хотите, чтобы df1 получил новый столбец со значением df2 для того же идентификатора и датой, ближайшей к дате df1, тогда вы можете выполнить соединение между ними. Но перед этим, я думаю, важно отметить, что в вашем примере с df2 для id=6 у вас есть две записи на одну и ту же дату. Какое из этих двух значений вы ожидаете выбрать в качестве правильного значения?

Eugenio.Gastelum96 16.05.2024 02:08

Что касается части соединения, о которой я упоминал, вот один пример того, как соединить два фрейма данных, используя при объединении критерий «только ближайшая дата». В этом вопросе упомянуто много других способов, у всех есть свои плюсы и минусы. Если ваш фрейм данных не очень большой и у вас есть оперативная память, решение для объединения по этой ссылке подойдет: stackoverflow.com/a/63754287/9588300

Eugenio.Gastelum96 16.05.2024 02:17

@Eugenio.Gastelum96, сначала я бы хотел идентифицировать эти наблюдения и рассмотреть их индивидуально, чтобы наша команда могла в этом разобраться. BrandonEm прокомментировал, объясняя, как использовать сообщение об ошибке «pivot_wider», чтобы понять это.

Mark A-L 18.05.2024 00:15
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
68
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Что-то, что вы можете попробовать:

### Packages
library(dplyr)
library(readr)
library(lubridate)

### Data
df_ex_original = read_csv("C:/Users/your_name/your_folder/df ex_original.csv")
df_ex_long = read_csv("C:/Users/your_name/your_folder/df ex_long.csv")

### Format correctly the columns which contain the dates
df1=df_ex_original %>% select(1,2) %>% mutate(key_date=mdy(key_date))
df2=df_ex_long %>% mutate(date=mdy(date))

### Join steps to find the closest date and the corresponding value for each id
### A count column has been added to identify duplicates
df1 %>%
  left_join(y = df2, by = join_by(id,closest(key_date<=date))) %>% 
  left_join(y = df2, by = join_by(id,closest(key_date>=date))) %>% 
  mutate(value=pmin(value.x,value.y,na.rm = TRUE)) %>% 
  select(id,key_date,last_col()) %>% 
  group_by(id) %>% 
  mutate(n=n())

Выход :

# A tibble: 7 × 4
# Groups:   id [6]
     id key_date   value     n
  <dbl> <date>     <dbl> <int>
1     1 2024-01-06     3     1
2     2 2024-01-01     1     1
3     3 2024-01-10     1     1
4     4 2024-01-12     0     1
5     5 2024-01-14     6     1
6     6 2024-01-02     5     2
7     6 2024-01-02     1     2

Я думаю, здесь есть два вопроса, да?

Первый:

tidyr::pivot_wider имеет действительно хорошие сообщения об ошибках, в которых фактически указан код, который можно использовать для отладки, например:

Use the following dplyr code to identify duplicates.
  {data} |>
  dplyr::summarise(n = dplyr::n(), .by = c(id, date)) |>
  dplyr::filter(n > 1L) 

Где {data} ваш фрейм данных. Альтернативно, вы можете суммировать дубликаты, если вам не нужно проверять их один за другим, например:

read.csv('df_ex_long.csv') |>
  dplyr::group_by(id, date) |>
  dplyr::summarize(value = mean(value)) |>
  dplyr::ungroup()

Второй:

abs должен полностью работать с объектами Date-класса:

Sys.Date() - (Sys.Date() - 5)
# Time difference of 5 days
Sys.Date() - (Sys.Date() + 5)
# Time difference of -5 days
abs(Sys.Date() - (Sys.Date() + 5))
# Time difference of 5 days

# OR:

time <- data.frame(t0 = c(Sys.Date() + seq(100)), t1 = c(Sys.Date() + rnorm(100, 50, 10)))
time$t1 - time$t0
abs(time$t1 - time$t0)

Вы можете проверить свои классы объектов с помощью class (например, class(time$t1) и при необходимости преобразовать их в другие (например, ?as.POSIXct, ?as.POSIXlt, ?strptime).

Ответ принят как подходящий

Решение таблицы данных с использованием скользящего соединения (возвращает первую запись, если есть связи):

library(data.table)

df_ex_original = fread("df ex_original.csv")
df_ex_long = fread("df ex_long.csv")

df_ex_original[, `:=`(key_date=as.Date(key_date, format = "%m/%d/%Y"),
                     value=NULL)]

df_ex_long[, date:=as.Date(date, format = "%m/%d/%Y")]

setkeyv(df_ex_original, c('id', 'key_date'))
setkeyv(df_ex_long, c('id', 'date'))

df_ex_long[df_ex_original, roll = "nearest"]

Key: <id, date>
      id       date value
   <int>     <Date> <int>
1:     1 2024-01-06     3
2:     2 2024-01-01     1
3:     3 2024-01-10     1
4:     4 2024-01-12     0
5:     5 2024-01-14     6
6:     6 2024-01-02     5

Отвечая на комментарий:

Команды data.table обновляются по ссылке, поэтому нет необходимости использовать <-. Более подробную информацию можно найти на странице помощи . Здесь показано, как использовать := при обновлении нескольких столбцов.

Присвоение переменной значения NULL фактически удаляет эту переменную из данных, и мне не нужна переменная value в df1, поскольку вы хотели получить ее значения из df2. Я мог бы использовать обновление, но мне показалось, что это лучший способ.

В моей команде as.Date() произошла непростительная ошибка. Извини! Пожалуйста, замените «%M» на «%m».

спасибо! похоже, это сработает. всего несколько уточняющих вопросов: 'df_ex_original[, :=(key_date=as.Date(key_date, format = "%M/%d/%Y"), value=NULL)]', кажется, меняет даты. например, для наблюдения 1 исходная дата — 6 января 2024 г., и я думаю, что она будет изменена на 6 мая 2024 г. # как код as.Date меняет df_ex_original без начала с «df_ex_original <-»? # что такое оператор := и почему он должен быть заключен в одинарные кавычки? # что делает аргумент value=NULL и почему он нужен только для df1?

Mark A-L 17.05.2024 22:40

Посмотрите мое редактирование, которое, надеюсь, ответит на ваши вопросы. Извините за даты. У меня была опечатка.

Edward 18.05.2024 03:36

и снова здравствуйте! что бы вы изменили в конечном выводе, чтобы включить key_date от df_ex_original вместо даты от df_ex_long? @Эдвард

Mark A-L 08.06.2024 01:20

Выходные данные содержат значения key_date из df_ex_original, но называются date, потому что имя переменной происходит от df_ex_long. Изменить имя очень просто. Либо измените его в df_ex_long, до слияния или после в результате.

Edward 08.06.2024 03:39

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