Проверить, помещается ли векторный элемент одного значения между векторными элементами двух других значений в R

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

id    letter
1     B
2     A
3     B
4     B
5     C
6     B
7     A
8     B
9     C

Все, что я нашел, связано с числовыми значениями и с понятием общего порядка (а не с индексом элемента в конкретном векторе).

Я хочу добавить новый столбец с логическими значениями (1, если B находится между A и C; 0, если B находится между C и A) в фрейм данных,

id    letter    between
1     B         0
2     A         NA
3     B         1
4     B         1
5     C         NA
6     B         0
7     A         NA
8     B         1
9     C         NA

Каков желаемый результат для последовательности ... "A", "B", "B", "A" ...?

markus 09.04.2019 10:36

Последовательность упорядочена по времени. Желаемый результат - удалить строки df$between==0.

NAN 09.04.2019 10:42
Стоит ли изучать 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
2
123
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Комбинация rle (кодирование длин серий) и zoo::rollapply является одним из вариантов:

library(zoo) 
d <- structure(list(id     = 1:9, 
                    letter = structure(c(2L, 1L, 2L, 2L, 3L, 2L, 1L, 2L, 3L), 
                                       .Label = c("A", "B", "C"), 
                                       class = "factor")), 
                    class  = "data.frame", row.names = c(NA, -9L)) 
rl <- rle(as.numeric(d$letter)) 
rep(rollapply(c(NA, rl$values, NA), 
             3,
             function(x) if (x[2] == 2) 
                             ifelse(x[1] == 1 && x[3] == 3, 1, 0) 
                         else NA),
    rl$lengths)
# [1]  0 NA  1  1 NA  0 NA  1 NA

Объяснение

  1. С помощью rle вы идентифицируете блоки последовательных значений.
  2. С помощью rollapply вы «прокручиваете» функцию с заданным размером окна (здесь 3) по вектору.
  3. Наш вектор rl$values содержит различные элементы, и функция, которую мы применяем к нему, довольно проста:
    • если второй элемент не равен 2 (соответствует B), верните NA
    • если вторым элементом является 2 а также, элемент 1 является A, а элемент 3 является C, возвращает 1 и 0 в противном случае

Вы можете использовать функции lead и lag, чтобы узнать буквы до и после, а затем mutate, как показано ниже:

library(dplyr)
df %>%
  mutate(letter_lag = lag(letter, 1),
         letter_lead = lead(letter, 1)) %>%
  mutate(between = case_when(letter_lag == "A" | letter_lead == "C" ~ 1,
                             letter_lag == "C" | letter_lead == "A" ~ 0,
                             TRUE ~ NA_real_)) %>%
  select(id, letter, between)
  id letter between
1  1      B       0
2  2      A      NA
3  3      B       1
4  4      B       1
5  5      C      NA
6  6      B       0
7  7      A      NA
8  8      B       1
9  9      C      NA

Этот код неверен. С этим изменением df$letters[5] <- "A" вы получите неправильные результаты для строк 3 и 6

thothal 09.04.2019 09:45

Другая tidyverse возможность может быть:

 df %>%
  group_by(grp = with(rle(letter), rep(seq_along(lengths), lengths))) %>%
  filter(row_number() == 1) %>%
  ungroup() %>%
  mutate(res = ifelse(lag(letter, default = first(letter)) == "A" & 
                      lead(letter, default = last(letter)) == "C", 1, 0)) %>%
  select(-letter, -grp) %>%
  full_join(df, by = c("id" = "id")) %>%
  arrange(id) %>%
  fill(res) %>%
  mutate(res = ifelse(letter != "B", NA, res))

    id   res letter
  <int> <dbl> <chr> 
1     1     0 B     
2     2    NA A     
3     3     1 B     
4     4     1 B     
5     5    NA C     
6     6     0 B     
7     7    NA A     
8     8     1 B     
9     9    NA C 

В этом случае он сначала группируется по идентификатору серийного типа и сохраняет первые строки с заданным идентификатором. Во-вторых, проверяет состояние. В-третьих, он выполняет полное соединение с исходным df в столбце «id». Наконец, он упорядочивает по «id», заполняет пропущенные значения и присваивает NA строкам, где «буква» != B.

Ответ принят как подходящий

Вот одно решение, которое, я надеюсь, довольно просто концептуально. Для «особых» случаев, таких как B, находящийся вверху или внизу списка, или имеющий A или C с обеих сторон, я установил такие значения в 0.

# Create dummy data - you use your own
df <- data.frame(id=1:100, letter=sample(c("A", "B", "C"), 100, replace=T))

# Copy down info on whether A or C is above each B
acup <- df$letter
for(i in 2:nrow(df))
  if (df$letter[i] == "B")
    acup[i] <- acup[i-1]

# Copy up info on whether A or C is below each B
acdown <- df$letter
for(i in nrow(df):2 -1)
  if (df$letter[i] == "B")
    acdown[i] <- acdown[i+1]

# Set appropriate values for column 'between'
df$between <- NA
df$between[acup == "A" & acdown == "C"] <- 1
df$between[df$letter == "B" & is.na(df$between)] <- 0   # Includes special cases

Из вопроса неясно, должны ли чередоваться «A» и «C», хотя это подразумевается, потому что нет кодирования «B» между «A» и «A» или vv. Предположим, что да, для вектора

x = c("B", "A", "B", "B", "C", "B", "A", "B", "C")

сопоставьте числовые значения c(A=1, B=0, C=-1) и сформируйте кумулятивную сумму

v = cumsum(c(A=1, B=0, C=-1)[x])

(увеличение на 1 при встрече с "A", уменьшение на единицу при "C"). Замените позиции, не соответствующие «B», на NA

v[x != "B"] = NA

давать

> v
 B  A  B  B  C  B  A  B  C
 0 NA  1  1 NA  0 NA  1 NA

Это может быть захвачено как функция

fun = function(x, map = c(A = 1, B = 0, C = -1)) {
    x = map[x]
    v = cumsum(x)
    v[x != 0] = NA
    v
}

и используется для преобразования data.frame или tibble, например,

tibble(x) %>% mutate(v = fun(x))

@markus спасибо; опечатка, но я хотел использовать x != "B", а не names(v), другой способ снять шкуру с кошки.

Martin Morgan 09.04.2019 10:39

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