У меня есть строка, в которой перечислены сроки пребывания в должности физического лица, например.
all_terms <- "2012 to 2024, 2007 to 2007, 2001 to 2003, 2000 to 2009, 2010 to 2011"
Я хочу знать, занимал ли данное лицо должность непрерывно, что означает:
Год окончания одного семестра и год начала другого семестра могут отличаться на единицу, т. е. семестр, заканчивающийся в 2011 году, и новый семестр, начинающийся в 2012 году, будут считаться непрерывными.
Условия, которые входят в другие условия или пересекаются с ними, не должны влиять на преемственность, т. е. термин с 2001 по 2003 год, указанный выше, попадает в период с 2000 по 2009 год и не нарушает преемственности. Аналогично, срок с 2008 по 2013 год не нарушит преемственности.
Так что приведенный выше пример будет признан непрерывным, а вот этот - "1989 по 2008, 2020 по 2024" - не будет.
Я придумал этот код, но он не работает:
all_terms <- "2012 to 2024, 2007 to 2007, 2001 to 2003, 2000 to 2009, 2010 to 2011"
# Process terms to extract years and create a data frame
terms_list <- str_split(all_terms, ",\\s*")[[1]]
years <- map(terms_list, ~str_extract_all(.x, "\\d{4}")[[1]])
years_df <- map_df(years, ~data.frame(start = as.numeric(.x[1]), end = as.numeric(.x[2])))
# Sort years by start date
years_df <- years_df %>% arrange(start)
# Adjust end year by adding one for continuity check
years_df$modified_end <- years_df$end + 1
# Check for continuity
is_continuous <- all(c(TRUE, tail(years_df$start, -1) <= head(years_df$modified_end, -1)))
# Results
list(
is_continuous = is_continuous,
start_years = min(years_df$start),
end_years = max(years_df$end)
)
Это немного многословный, но очень аккуратный подход, созданный с учетом возможности использования нескольких идентификаторов.
all_terms <- "2012 to 2024, 2007 to 2007, 2001 to 2003, 2000 to 2009, 2010 to 2011"
library(tidyverse)
data.frame(id = 1, all_terms) |>
separate_longer_delim(all_terms, delim = ", ") |>
separate_wider_delim(cols = all_terms, names = c("from", "to"), delim = " to ") |>
mutate(row = row_number()) |>
reframe(year = seq(from, to, 1), .by = c(id, row)) |>
distinct(id, year) |>
arrange(id, year) |>
summarize(terms = max(cumsum(year > lag(year,1,0) + 1)), .by = id)
Это помещает строку в фрейм данных, разбивает ее на строки в каждом ,
, разбивает ее на столбцы from
и to
, затем создает последовательность лет, охватывающую этот диапазон, выбирает по одному году каждого года для каждого идентификатора, а затем проверяет, сколько пробелов есть для каждого идентификатора. .
Он сообщает один термин для исходных данных и два термина для вторых данных.
Мы можем использовать cummax
и cumsum
. Я создал функцию, которая будет подсчитывать количество непоследовательных терминов. Более подробную информацию об этих функциях можно найти в моем предыдущем ответе: Свернуть и объединить перекрывающиеся временные интервалы. *
one_term <- "2012 to 2024, 2007 to 2007, 2001 to 2003, 2000 to 2009, 2010 to 2011"
two_term <- "2013 to 2024, 2007 to 2007, 2001 to 2003, 2000 to 2009, 2010 to 2011"
four_term <- "2013 to 2024, 2007 to 2007, 2001 to 2003, 2000 to 2005, 2010 to 2011"
library(dplyr)
term_counter <- function(string_dat) {
as.data.frame(
do.call(rbind,
strsplit(strsplit(string_dat,
", ")[[1]],
" to "))) %>%
mutate(across(everything(), as.numeric)) %>%
arrange(V1, V2) %>%
mutate(terms = 1 + c(0, cumsum(lead(V1 - 1) >
cummax(V2))[-n()])) %>%
pull(terms) %>% max()
}
term_counter(one_term)
#> [1] 1
term_counter(two_term)
#> [1] 2
term_counter(four_term)
#> [1] 4
Если вы хотите получить длину каждого термина и, возможно, его начало и конец, вы можете использовать измененную версию ниже;
term_counter_mod <- function(string_dat) {
as.data.frame(
do.call(rbind,
strsplit(strsplit(string_dat,
", ")[[1]],
" to "))) %>%
mutate(across(everything(), as.numeric)) %>%
arrange(V1, V2) %>%
mutate(terms = 1 + c(0, cumsum(lead(V1 - 1) >
cummax(V2))[-n()])) %>%
summarise(from = min(V1), to = max(V2),
len = to - from + 1,
.by = terms)
}
lapply(setNames(list(one_term, two_term, four_term),
c("one", "two", "four")),
term_counter_mod)
#> $one
#> terms from to len
#> 1 1 2000 2024 25
#>
#> $two
#> terms from to len
#> 1 1 2000 2011 12
#> 2 2 2013 2024 12
#>
#> $four
#> terms from to len
#> 1 1 2000 2005 6
#> 2 2 2007 2007 1
#> 3 3 2010 2011 2
#> 4 4 2013 2024 12
Created on 2024-04-11 with reprex v2.0.2
* This is not a duplicate of that question.
@user17661126 смотрите обновления.
Это работает! Есть ли способ настроить код, чтобы он возвращал как счетчик терминов, так и длину подсчитываемых терминов в годах - т. е. term_counter(one_term) будет возвращать 1, 24; term_counter(two_term) вернет 2, 23; term_counter(four_term) вернет 4, 18? Не обязательно из одной и той же функции, мой набор данных небольшой, и эффективность не является проблемой.