У меня есть набор входных данных, который выглядит следующим образом:
И мой желаемый результат выглядит так:
В настоящее время у меня есть цикл for (анализируем год и квартал и продолжаем добавлять 1 к началу квартала, пока не достигнем конечной даты) Однако выполнение цикла занимает много времени. Мне интересно, есть ли более быстрые способы сделать это?
Спасибо!





library(tidyr)
library(purrr)
# Sample data
data <- data.frame(
Name = c('A', 'A', 'B', 'B'),
Date = c('2018 Q2', '2019 Q3', '2018 Q4', '2019 Q4'),
stringsAsFactors = FALSE
)
# Function to create a sequence of quarters
create_quarter_sequence <- function(start, end) {
start_year <- as.numeric(substr(start, 1, 4))
start_quarter <- as.numeric(substr(start, 7, 7))
end_year <- as.numeric(substr(end, 1, 4))
end_quarter <- as.numeric(substr(end, 7, 7))
# Create sequence of quarters
quarters <- c()
current_year <- start_year
current_quarter <- start_quarter
while (current_year < end_year || (current_year == end_year && current_quarter <= end_quarter)) {
quarters <- c(quarters, paste(current_year, paste0("Q", current_quarter), sep = " "))
current_quarter <- current_quarter + 1
if (current_quarter > 4) {
current_quarter <- 1
current_year <- current_year + 1
}
}
return(quarters)
}
# Process the data
result <- data %>%
group_by(Name) %>%
summarise(
start_date = min(Date),
end_date = max(Date)
) %>%
rowwise() %>%
mutate(
Date = list(create_quarter_sequence(start_date, end_date))
) %>%
unnest(cols = c(Date)) %>%
select(Name, Date)
print(result)
С помощью dplyr и tidyr и немного математики вы можете сделать следующее
library(tidyr)
library(dplyr)
dd %>%
separate_wider_delim(Date, " Q", names = c("year", "q")) %>%
mutate(across(c(year, q), as.numeric)) %>%
mutate(index=year * 4 + q-1) %>%
group_by(Name) %>%
reframe(index = full_seq(index, period=1),
year = index %/% 4,
q = index %% 4+1,
Date = paste0(year, " Q", q)) %>%
select(Name, Date)
который возвращает
Name Date
<chr> <chr>
1 A 2018 Q2
2 A 2018 Q3
3 A 2018 Q4
4 A 2019 Q1
5 A 2019 Q2
6 A 2019 Q3
7 B 2018 Q4
8 B 2019 Q1
9 B 2019 Q2
10 B 2019 Q3
11 B 2019 Q4
Мы создаем специальный индекс, чтобы нам было легче заполнить последовательность год + квартал для каждой группы.
Преобразуйте вyearqtr и затем используйте seq. Обратите внимание, чтоyearqtr представляет год и кварталы как год плюс 0, 1/4, 2/4 и 3/4 для 4 кварталов, поэтому использование seq с by=1/4 будет работать.
library(dplyr)
library(zoo)
DF %>%
mutate(Date = as.yearqtr(Date)) %>%
reframe(Date = seq(Date[1], Date[2], 1/4), .by = Name)
предоставление
Name Date
1 A 2018 Q2
2 A 2018 Q3
3 A 2018 Q4
4 A 2019 Q1
5 A 2019 Q2
6 A 2019 Q3
7 B 2018 Q4
8 B 2019 Q1
9 B 2019 Q2
10 B 2019 Q3
11 B 2019 Q4
DF <- data.frame(
Name = rep(c("A", "B"), each = 2L),
Date = c("2018 Q2", "2019 Q3", "2018 Q4", "2019 Q4")
)
На самом деле это похоже на expand.grid(LETTERS[1:2], 2018:2019, paste0('Q', 1:4)). Чтобы получить коэффициенты, мы можем использовать unique и substr. Я думаю, мы можем жестко запрограммировать кварталы.
> by(df, ~Name, \(x) {
+ with(x,
+ list(unique(Name),
+ unique(substr(Date, 1, 4)),
+ paste0('Q', 1:4))
+ ) |> do.call(what='expand.grid') |>
+ {\(.) transform(., Date=Reduce(paste, .[2:3]),
+ Var2=NULL, Var3=NULL)}() |>
+ setNames(names(x)) |>
+ sort_by(~list(Name, Date)) |>
+ subset(Date >= min(x$Date) & Date <= max(x$Date))
+ }) |> c(make.row.names=FALSE) |> do.call(what='rbind')
Name Date
1 A 2018 Q2
2 A 2018 Q3
3 A 2018 Q4
4 A 2019 Q1
5 A 2019 Q2
6 A 2019 Q3
7 B 2018 Q4
8 B 2019 Q1
9 B 2019 Q2
10 B 2019 Q3
11 B 2019 Q4
Данные:
> dput(df)
structure(list(Name = c("A", "A", "B", "B"), Date = c("2018 Q2",
"2019 Q3", "2018 Q4", "2019 Q4")), class = "data.frame", row.names = c(NA,
-4L))