Условное изменение на основе порядка переменных

У меня есть векторы, которые индексируют события A, B, C. Каждое событие происходит хотя бы один раз, но может произойти несколько раз. Например:

  • Последовательность 1: c("A", "B", "C")
  • Последовательность 2: c("A", "A", "B", "B", "C")
  • Последовательность 3: c("B", "A", "C")
  • Последовательность 4: c("C", "B", "A")
  • Последовательность 5: c("A", "B", "B", "C", "A")

Для каждой последовательности я хочу определить события, которые происходят в следующем порядке: A-B-C. Я хочу проиндексировать эти последовательности вектором 0/1, указывающим, соответствует ли они этому условию. В каждой последовательности должно быть только одно A или C, но между ними допускается несколько B. Если события не в порядке A-B-C, они не соответствуют условию. Эти правила должны возвращать векторы:

  • Последовательность 1: c(1,1,1)
  • Последовательность 2: c(0,1,1,1,1)
  • Последовательность 3: c(0,0,0)
  • Последовательность 4: c(0,0,0)
  • Последовательность 5: c(1,1,1,1,0)

Я не уверен, к какому общему типу проблемы относится эта проблема, поэтому у меня возникли проблемы с поиском решения. Мы ценим любые предложения!

Обновлено: открыть либо базовое решение R, примененное к каждому вектору (например, через lapply), либо решение tidyverse будет более подходящим, поэтому отредактировал мой вопрос, включив в него как список векторов, так и фрейм данных с вводом/желаемым выводом. Edit^2: добавлен дополнительный тестовый пример.

## sequences
s1<-c("A", "B", "C")  
s2<-c("A", "A", "B", "B", "C")  
s3<-c("B", "A", "C")  
s4<-c("C", "B", "A")
s5<-c("A", "B", "B", "C", "A")

## make into a list
s.list <- list(s1,s2,s3,s4,s5)

## s.list as a data frame
# https://stackoverflow.com/questions/57270001/list-to-dataframe-conversion-in-r-keeping-list-indexes
s.df <- s.list %>% 
  purrr::map(~as_tibble(.)) %>% 
  dplyr::bind_rows(.id = "group")

## solution...

## desired output
# as a list
s.indexed <- list(c(1,1,1),
                  c(0,1,1,1,1),
                  c(0,0,0),
                  c(0,0,0),
                  c(1,1,1,1,0)
)
s.indexed 

# as a data frame
s.df <- s.df %>%
  bind_cols(index = unlist(s.indexed))

s.df

Стоит ли изучать 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
97
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Попытка, основанная на большем количестве ручных правил, чем я надеялся справиться с разрешенным дублированием B, но не A или C:

result <- lapply(s.list, \(x) {
    ox <- ordered(x, levels=c("A","B","C"))
    os <- is.unsorted(ox)
    out <- rep(0, length(ox))
    if (!os) {
        out[ox == "A" & (!duplicated(ox, fromLast=TRUE))] <- 1
        out[ox == "C" & (!duplicated(ox))] <- 1
        out[ox == "B"] <- 1
    }
    out
})
result
#[[1]]
#[1] 1 1 1
#
#[[2]]
#[1] 0 1 1 1 1
#
#[[3]]
#[1] 0 0 0
#
#[[4]]
#[1] 0 0 0

Выезд:

identical(result, s.indexed)
#[1] TRUE

Базовая логика заключается в использовании фактора ordered с желаемым порядком ABC, который позволяет сначала идентифицировать неотсортированные векторы. Тогда только последний A и первый C помечаются как 1 с использованием флага duplicated.

Спасибо за это предложение. Я не знал о проверке дубликатов в обратном порядке, это умно. Я думаю, что ваше решение подходит для вопроса, который я задал... поэтому я ценю это. На практике A и C также могут дублироваться, а A может стоять последним. Я добавил еще один тестовый пример, в котором это решение не работает (думаю, потому что оно нарушает проверку дубликатов в обратном порядке). Я могу открыть еще один вопрос, если это более уместно, чем редактировать этот.

gregor-fausto 18.07.2024 01:20

@gregor-fausto - это потребует от меня некоторого переосмысления. Если у меня будет время, я посмотрю, смогу ли я скорректировать эту логику.

thelatemail 18.07.2024 01:37
Ответ принят как подходящий

При этом используется str_locate для поиска нужных персонажей/событий. Это работает для одного возникновения события на запись. Это можно расширить для работы с любым количеством случаев (используя str_locate_all), но это добавит несколько уровней сложности.

library(stringr)

lapply(s.list, \(x){
  strng <- paste0(x, collapse = "")
  loc <- str_locate(strng, "AB+C")
  zeros <- rep(0, nchar(strng))
  if (!is.na(loc[1])){ 
    zeros[seq(loc[1], loc[2])] <- 1
  }
  zeros
})
[[1]]
[1] 1 1 1

[[2]]
[1] 0 1 1 1 1

[[3]]
[1] 0 0 0

[[4]]
[1] 0 0 0

[[5]]
[1] 1 1 1 1 0

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

Строка sapply преобразует каждый вектор в строку символов; следующий строка заменяет целевые строки тем же количеством единиц; следующий строка заменяет все остальные символы нулями, а последние две строки преобразуются из вектора символов в список числовых векторов.

Если результат вектора символов c("111", "01111", ...) в порядке, то последний две строки можно опустить.

library(gsubfn)

L |>
  sapply(paste, collapse = "") |>
  gsubfn("(AB+C)", ~ strrep("1",nchar(x)), x = _) |>
  gsub("[^1]", "0", x = _) |>
  strsplit("") |>
  lapply(as.numeric)

Примечание

Используемый вход

L <- list(
  c("A", "B", "C") ,
  c("A", "A", "B", "B", "C") ,
  c("B", "A", "C") ,
  c("C", "B", "A"),
  c("A", "B", "B", "C", "A")
)
is_next_legal <- function(cur, next_elem) {
  (cur == "A" && next_elem == "B") || (cur == "B" && next_elem %in% c("B", "C"))
}

foo <- function(x) {
  n <- length(x)
  out <- integer(n)
  i <- 1L
  p <- 1L
  while (i <= (n-2L)) {
    if (x[i] == "A") {
      p <- i
      while (p <= (n-1L) && is_next_legal(x[p], x[p+1])) {
        p <- p + 1L
      }
      if (x[p] == "C") out[i:p] <- 1L
    }
    i <- max(i, p) + 1L
  }
  out
}

lapply(s.list, foo)
[[1]]
[1] 1 1 1

[[2]]
[1] 0 1 1 1 1

[[3]]
[1] 0 0 0

[[4]]
[1] 0 0 0

[[5]]
[1] 1 1 1 1 0

Для пояснения: чему соответствует i в вашем утверждении while? Я получаю сообщение об ошибке о том, что не нашел этот объект при запуске вашей функции.

gregor-fausto 18.07.2024 19:00

Извините, я изменил свое решение и не смог проверить перед публикацией. Теперь это должно сработать.

s_baldur 19.07.2024 00:37

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