Проверка непрерывности чисел в строке

У меня есть строка, в которой перечислены сроки пребывания в должности физического лица, например.

all_terms <- "2012 to 2024, 2007 to 2007, 2001 to 2003, 2000 to 2009, 2010 to 2011"

Я хочу знать, занимал ли данное лицо должность непрерывно, что означает:

  1. Год окончания одного семестра и год начала другого семестра могут отличаться на единицу, т. е. семестр, заканчивающийся в 2011 году, и новый семестр, начинающийся в 2012 году, будут считаться непрерывными.

  2. Условия, которые входят в другие условия или пересекаются с ними, не должны влиять на преемственность, т. е. термин с 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)
)
Стоит ли изучать 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
94
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Это немного многословный, но очень аккуратный подход, созданный с учетом возможности использования нескольких идентификаторов.

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.

Это работает! Есть ли способ настроить код, чтобы он возвращал как счетчик терминов, так и длину подсчитываемых терминов в годах - т. е. term_counter(one_term) будет возвращать 1, 24; term_counter(two_term) вернет 2, 23; term_counter(four_term) вернет 4, 18? Не обязательно из одной и той же функции, мой набор данных небольшой, и эффективность не является проблемой.

user17661126 11.04.2024 21:50

@user17661126 смотрите обновления.

M-- 11.04.2024 23:11

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