У меня есть два набора данных. Первый набор данных является основным, широким и имеет идентификатор как уникальную/идентифицирующую переменную. Второй набор данных имеет ту же переменную 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 отлично работал с примерами данных. На следующей неделе я опробую это на своих реальных данных и проверю, если что-то еще не сработает.
Что касается части соединения, о которой я упоминал, вот один пример того, как соединить два фрейма данных, используя при объединении критерий «только ближайшая дата». В этом вопросе упомянуто много других способов, у всех есть свои плюсы и минусы. Если ваш фрейм данных не очень большой и у вас есть оперативная память, решение для объединения по этой ссылке подойдет: stackoverflow.com/a/63754287/9588300
@Eugenio.Gastelum96, сначала я бы хотел идентифицировать эти наблюдения и рассмотреть их индивидуально, чтобы наша команда могла в этом разобраться. BrandonEm прокомментировал, объясняя, как использовать сообщение об ошибке «pivot_wider», чтобы понять это.





Что-то, что вы можете попробовать:
### 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?
Посмотрите мое редактирование, которое, надеюсь, ответит на ваши вопросы. Извините за даты. У меня была опечатка.
и снова здравствуйте! что бы вы изменили в конечном выводе, чтобы включить key_date от df_ex_original вместо даты от df_ex_long? @Эдвард
Выходные данные содержат значения key_date из df_ex_original, но называются date, потому что имя переменной происходит от df_ex_long. Изменить имя очень просто. Либо измените его в df_ex_long, до слияния или после в результате.
Я предполагаю, что из двух фреймов данных в вашем git
df ex_original.csv— это первый фрейм данных, который вы описываете в первом абзаце (я назову его df1). Аdf ex_long.csv— второй (я назову его df2). Если вы хотите, чтобы df1 получил новый столбец со значением df2 для того же идентификатора и датой, ближайшей к дате df1, тогда вы можете выполнить соединение между ними. Но перед этим, я думаю, важно отметить, что в вашем примере с df2 для id=6 у вас есть две записи на одну и ту же дату. Какое из этих двух значений вы ожидаете выбрать в качестве правильного значения?