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

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

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

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

Iхотите добавить новый столбец с логическими значениями (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

Ответы [ 5 ]

1 голос
/ 09 апреля 2019

Непонятно из вопроса, должны ли чередоваться «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))
1 голос
/ 09 апреля 2019

Комбинация 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 в противном случае
0 голосов
/ 09 апреля 2019

Вот одно решение, которое, я надеюсь, довольно легко концептуально.Для «особых» случаев, таких как 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
0 голосов
/ 09 апреля 2019

Другая 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 для строк, где «letter»! = B.

0 голосов
/ 09 апреля 2019

Вы можете использовать функции 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...