Перечислите все месяцы между двумя датами в R

Я пытаюсь добавить список месяца и года между двумя датами.

У меня есть следующий набор данных:

PKID    Name    Gender  DateStart   DateEnd
68      PAUL    1       24/11/2021  23/02/2022
68      PAUL    1       24/04/2022  23/06/2023
40      KATE    2       01/01/2000  14/03/2000
40      KATE    2       03/12/2000  31/01/2001

И я хочу создать следующий набор данных:

PKID    Name    Gender  DateStart   DateEnd     year    Month
68      PAUL    1       24/11/2021  23/02/2022  2021    11
68      PAUL    1       24/11/2021  23/02/2022  2021    12
68      PAUL    1       24/11/2021  23/02/2022  2022    1
68      PAUL    1       24/11/2021  23/02/2022  2022    2
68      PAUL    1       24/04/2022  23/06/2023  2022    4
68      PAUL    1       24/04/2022  23/06/2023  2022    5
68      PAUL    1       24/04/2022  23/06/2023  2022    6
40      KATE    2       01/01/2000  14/03/2000  2000    1
40      KATE    2       01/01/2000  14/03/2000  2000    2
40      KATE    2       01/01/2000  14/03/2000  2000    3
40      KATE    2       03/12/2000  31/01/2001  2000    12
40      KATE    2       03/12/2000  31/01/2001  2001    1

Где месяц соответствует месяцу между звездной датой и конечной датой, а год соответствует месяцу.

Я пробовал следующее:

# Load necessary libraries
library(dplyr)
library(tidyr)
library(lubridate) # For handling date operations

# Sample data 
df <- read.table(text = "
PKID    Name    Gender  DateStart   DateEnd
68  PAUL    1   24/11/2021  23/02/2022
68  PAUL    1   24/04/2022  23/06/2023
40  KATE    2   01/01/2000  14/03/2000
40  KATE    2   03/12/2000  31/01/2001
", header = TRUE, stringsAsFactors = FALSE)

# Convert date columns to Date format
df$DateStart <- dmy(df$DateStart)
df$DateEnd <- dmy(df$DateEnd)

# Generate sequence of dates for each row
df <- df %>%
  group_by(PKID, Name, Gender, DateStart, DateEnd) %>%
  complete(Date = seq.Date(DateStart, DateEnd, by = "month")) %>%
  ungroup() %>%
  mutate(
    year = year(Date),   # Extract year
    Month = month(Date)  # Extract month
  ) %>%
  select(-Date)  # Remove the temporary Date column

# Print the result
print(df)

Но я получаю следующую ошибку:

Error in reframe():
ℹ In argument: complete(data = pick(everything()), ..., fill = fill, explicit = explicit).
ℹ In group 1: PKID = 40, Name = "KATE", Gender = 2, DateStart = 2000-01-01, DateEnd = 2000-03-14.
Caused by error:
! object 'DateStart' not found
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
81
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Я внес несколько изменений в ваш подход, чтобы он работал.

library(dplyr)
library(tidyr)
library(lubridate)

df %>%
  mutate(row = row_number(), 
         Date = DateStart) %>%
  group_by(row) %>%
  complete(PKID, Name, Gender, DateStart, DateEnd, 
           Date = seq(DateStart, DateEnd, by = "month")) %>%
  ungroup() %>%
  mutate(
    year = year(Date),  
    Month = month(Date) 
  ) %>%
  select(-Date, -row)

# A tibble: 22 × 7
#    PKID Name  Gender DateStart  DateEnd     year Month
#   <int> <chr>  <int> <date>     <date>     <dbl> <dbl>
# 1    68 PAUL       1 2021-11-24 2022-02-23  2021    11
# 2    68 PAUL       1 2021-11-24 2022-02-23  2021    12
# 3    68 PAUL       1 2021-11-24 2022-02-23  2022     1
# 4    68 PAUL       1 2022-04-24 2023-06-23  2022     4
# 5    68 PAUL       1 2022-04-24 2023-06-23  2022     5
# 6    68 PAUL       1 2022-04-24 2023-06-23  2022     6
# 7    68 PAUL       1 2022-04-24 2023-06-23  2022     7
# 8    68 PAUL       1 2022-04-24 2023-06-23  2022     8
# 9    68 PAUL       1 2022-04-24 2023-06-23  2022     9
#10    68 PAUL       1 2022-04-24 2023-06-23  2022    10
# ℹ 12 more rows
# ℹ Use `print(n = ...)` to see more rows

Спасибо . Это работает, однако последний месяц отсутствует в списке. Как и для даты начала 24.11.2021 и даты окончания 23.02.2022, у нас есть выходные данные для месяцев 11 и 12 для 2021 года и только для месяца 1 для 2022 года. Месяц 2 отсутствует в выходных данных.

Nat 06.07.2024 08:31

Альтернатива использованию гнезда.

df |>
     nest(data=c(DateStart, DateEnd), .by=c(PKID, Name, Gender, DateStart, DateEnd)) |>
     mutate(date=sapply(data, \(x) unique(
       c(seq.Date(x$DateStart, x$DateEnd, by = "month"), x$DateEnd))),
                     year=sapply(date, year),
                     Month=sapply(date, month)) |>
     unnest(c(year, Month)) |>
  select(-c(data, date))

# A tibble: 26 × 7
    PKID Name  Gender DateStart  DateEnd     year Month
   <int> <chr>  <int> <date>     <date>     <dbl> <dbl>
 1    68 PAUL       1 2021-11-24 2022-02-23  2021    11
 2    68 PAUL       1 2021-11-24 2022-02-23  2021    12
 3    68 PAUL       1 2021-11-24 2022-02-23  2022     1
 4    68 PAUL       1 2021-11-24 2022-02-23  2022     2
 5    68 PAUL       1 2022-04-24 2023-06-23  2022     4
 6    68 PAUL       1 2022-04-24 2023-06-23  2022     5
 7    68 PAUL       1 2022-04-24 2023-06-23  2022     6
 8    68 PAUL       1 2022-04-24 2023-06-23  2022     7
 9    68 PAUL       1 2022-04-24 2023-06-23  2022     8
10    68 PAUL       1 2022-04-24 2023-06-23  2022     9
# ℹ 16 more rows
# ℹ Use `print(n = ...)` to see more rows
Ответ принят как подходящий

Можешь попробовать:

library(dplyr)
library(purrr)
library(tidyr)
library(lubridate)

df |> 
  mutate(date = map2(DateStart,  rollforward(DateEnd), \(x, y) seq(x, y, by = "month"))) |> 
  unnest(date) |> 
  mutate(year = year(date),
         month = month(date),
         date = NULL)

# A tibble: 26 × 7
    PKID Name  Gender DateStart  DateEnd     year month
   <int> <chr>  <int> <date>     <date>     <dbl> <dbl>
 1    68 PAUL       1 2021-11-24 2022-02-23  2021    11
 2    68 PAUL       1 2021-11-24 2022-02-23  2021    12
 3    68 PAUL       1 2021-11-24 2022-02-23  2022     1
 4    68 PAUL       1 2021-11-24 2022-02-23  2022     2
 5    68 PAUL       1 2022-04-24 2023-06-23  2022     4
 6    68 PAUL       1 2022-04-24 2023-06-23  2022     5
 7    68 PAUL       1 2022-04-24 2023-06-23  2022     6
 8    68 PAUL       1 2022-04-24 2023-06-23  2022     7
 9    68 PAUL       1 2022-04-24 2023-06-23  2022     8
10    68 PAUL       1 2022-04-24 2023-06-23  2022     9
# ℹ 16 more rows
# ℹ Use `print(n = ...)` to see more rows

Или альтернативно с reframe()

df |> 
  reframe(date = seq(DateStart, rollforward(DateEnd), by = "month"),
          year = year(date),
          month = month(date),
          .by = everything()) |> 
  select(-date)

Это не сработает, если EndDate приходится на тот же день, что и StartDate. Замените в первой строке 23.02.2022 на 24.02.2022. Вы получаете дополнительный месяц.

Edward 06.07.2024 09:33

@Эдвард - хороший улов. Зафиксированный.

lotus 06.07.2024 10:39

Спасибо, что научили меня rollforward - Сегодня я кое-чему научился!

Edward 06.07.2024 11:39

В базе R мы можем добавить столбец "list", используя seq.Date в Map с strftime для месяцев. Затем мы создаем data.frame, скажем, из PKID и списков соответствующих месяцев, которые мы rbind и, наконец, merge в исходный фрейм данных.

> dat |>
+   transform(...=Map(seq.Date, DateStart, DateEnd, by='month') |> 
+               lapply(strftime, '%m') |> list() |> list2DF() |>
+               setNames('mlst')) |> 
+   with(Map(data.frame, PKID=PKID, months=mlst)) |> 
+   do.call(what='rbind') |> merge(dat, sort=FALSE)
   PKID months Name Gender  DateStart    DateEnd
1    68     11 PAUL      1 2021-11-24 2022-02-23
2    68     11 PAUL      1 2022-04-24 2023-06-23
3    68     12 PAUL      1 2021-11-24 2022-02-23
4    68     12 PAUL      1 2022-04-24 2023-06-23
5    68     01 PAUL      1 2021-11-24 2022-02-23
6    68     01 PAUL      1 2022-04-24 2023-06-23
7    68     04 PAUL      1 2021-11-24 2022-02-23
8    68     04 PAUL      1 2022-04-24 2023-06-23
9    68     05 PAUL      1 2021-11-24 2022-02-23
10   68     05 PAUL      1 2022-04-24 2023-06-23
11   68     06 PAUL      1 2021-11-24 2022-02-23
12   68     06 PAUL      1 2022-04-24 2023-06-23
13   68     07 PAUL      1 2021-11-24 2022-02-23
14   68     07 PAUL      1 2022-04-24 2023-06-23
15   68     08 PAUL      1 2021-11-24 2022-02-23
16   68     08 PAUL      1 2022-04-24 2023-06-23
17   68     09 PAUL      1 2021-11-24 2022-02-23
18   68     09 PAUL      1 2022-04-24 2023-06-23
19   68     10 PAUL      1 2021-11-24 2022-02-23
20   68     10 PAUL      1 2022-04-24 2023-06-23
21   68     11 PAUL      1 2021-11-24 2022-02-23
22   68     11 PAUL      1 2022-04-24 2023-06-23
23   68     12 PAUL      1 2021-11-24 2022-02-23
24   68     12 PAUL      1 2022-04-24 2023-06-23
25   68     01 PAUL      1 2021-11-24 2022-02-23
26   68     01 PAUL      1 2022-04-24 2023-06-23
27   68     02 PAUL      1 2021-11-24 2022-02-23
28   68     02 PAUL      1 2022-04-24 2023-06-23
29   68     03 PAUL      1 2021-11-24 2022-02-23
30   68     03 PAUL      1 2022-04-24 2023-06-23
31   68     04 PAUL      1 2021-11-24 2022-02-23
32   68     04 PAUL      1 2022-04-24 2023-06-23
33   68     05 PAUL      1 2021-11-24 2022-02-23
34   68     05 PAUL      1 2022-04-24 2023-06-23
35   40     01 KATE      2 2000-01-01 2000-03-14
36   40     01 KATE      2 2000-12-03 2001-01-31
37   40     02 KATE      2 2000-01-01 2000-03-14
38   40     02 KATE      2 2000-12-03 2001-01-31
39   40     03 KATE      2 2000-01-01 2000-03-14
40   40     03 KATE      2 2000-12-03 2001-01-31
41   40     12 KATE      2 2000-01-01 2000-03-14
42   40     12 KATE      2 2000-12-03 2001-01-31
43   40     01 KATE      2 2000-01-01 2000-03-14
44   40     01 KATE      2 2000-12-03 2001-01-31

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


Данные:

> dput(dat)
structure(list(PKID = c(68L, 68L, 40L, 40L), Name = c("PAUL", 
"PAUL", "KATE", "KATE"), Gender = c(1L, 1L, 2L, 2L), DateStart = c("24/11/2021", 
"24/04/2022", "01/01/2000", "03/12/2000"), DateEnd = c("23/02/2022", 
"23/06/2023", "14/03/2000", "31/01/2001")), class = "data.frame", row.names = c(NA, 
-4L))
> date_cols <- c('DateStart', 'DateEnd')
> dat[date_cols] <- lapply(dat[date_cols], as.Date, '%d/%m/%Y')

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