получение заклинаний и статистики из последовательности чисел

#r #dplyr #stringr #stringi

#r #dplyr #stringr #stringi

Вопрос:

У меня есть строка, в которой я хотел бы извлечь заклинания из последовательности, например,

    A<- c('000001111000', '0110011', '110001')
 

Я хотел бы получить непрерывные длины заклинаний 0 и 1 в формате последовательности. Затем, используя длины заклинаний, я хотел бы вычислить описательную статистику, такую как среднее значение, режим, sd и т. Д., (spell_0 и spell_1 — это последовательности из вектора A.

Например,

     spell_0  spell_1   mean_spell_0   mean_spell_1

       5-3        4           4               4
       1-2        2-2         1.5             2
        3         2-1         3               1.5
 

Есть предложения?

Комментарии:

1. Что происходит, когда у вас более 2 заклинаний?

2. смотрите rle (хотя вывод не в самом удобном формате)

3. В нем может быть более 2 заклинаний, а также может быть около 1000, это был просто пример

Ответ №1:

Ваш вопрос включает в себя фактически несколько вопросов.

Из вашего исходного вектора вам сначала нужно получить разные последовательности, после разделения строк на символы. Этого можно достичь, rle как указано в комментариях. Затем для каждого значения («0» и «1») в вашем примере вам нужно получить значение lengths каждой последовательности, соответствующее значению. Затем вам нужно поместить их в нужный формат (хотя это может быть не самым подходящим.

Вот мое предложение сделать все это:

 seqA <- lapply(strsplit(A, ""), rle)

do.call(cbind,lapply(c("0", "1"), # this can be made more general, for example using unique(unlist(strsplit(A, "")))
       function(i){
         do.call(rbind, lapply(seqA, 
                function(x){
                lesSeq <- x$lengths[x$values==i]
                  res <- data.frame(paste(lesSeq, collapse="-"), mean(lesSeq))
                  colnames(res) <- paste(c("spell", "mean_spell"), i, sep="_")
                return(res)
            }))
       }))[, c(1, 3, 2, 4)] # this rearrangment may not be needed...
#  spell_0 spell_1 mean_spell_0 mean_spell_1
#1     5-3       4          4.0          4.0
#2     1-2     2-2          1.5          2.0
#3       3     2-1          3.0          1.5
 

Ответ №2:

Вы могли бы попробовать что-то вроде этого:

 do.call(rbind,
  lapply(strsplit(A, ""), 
         function(x) {
           lengths <- rle(x)$lengths
           values  <- rle(x)$values
           data.frame(spell_0      = paste(lengths[values == "0"], collapse = "-"),
                      spell_1      = paste(lengths[values == "1"], collapse = "-"),
                      mean_spell_0 = mean(lengths[values == "0"]),
                      mean_spell_1 = mean(lengths[values == "1"]))
           }))

#>   spell_0 spell_1 mean_spell_0 mean_spell_1
#> 1     5-3       4          4.0          4.0
#> 2     1-2     2-2          1.5          2.0
#> 3       3     2-1          3.0          1.5
 

Ответ №3:

Сначала мы извлекаем и подсчитываем 0 s и 1 s:

 library(stringr)
spell_0a <- sapply(str_extract_all(A, "0 "), function(x) str_count(x, "0"))
spell_1a <- sapply(str_extract_all(A, "1 "), function(x) str_count(x, "1"))
 

Затем мы сворачиваем результаты и выполняем математические операции:

 df <- data.frame(
# collapse results:
  spell_0 = unlist(lapply(spell_0a, function(x) paste0(x, collapse = "-"))),
  spell_1 = unlist(lapply(spell_1a, function(x) paste0(x, collapse = "-"))),
# calculate means:
  mean_spell_0 = unlist(lapply(spell_0a, function(x) ifelse(length(x)==1, x[1], sum(x[1] x[2])/2))),
  mean_spell_1 = unlist(lapply(spell_1a, function(x) ifelse(length(x)==1, x[1],sum(x[1] x[2])/2)))
)
 

Результат:

 df
  spell_0 spell_1 mean_spell_0 mean_spell_1
1     5-3       4          4.0          4.0
2     1-2     2-2          1.5          2.0
3       3     2-1          3.0          1.5
 

Ответ №4:

Вот удобное для всех решение, которое позволяет избежать apply функций.

 library(tidyverse)
library(stringr)

A <- c('000001111000', '0110011', '110001')

data.frame(A) %>% 
  mutate(A = str_replace_all(A, "01", "0-1"), 
         A = str_replace_all(A, "10", "1-0")) %>% 
  mutate(A_split = str_split(A, "-")) %>% 
  unnest(A_split) %>% 
  mutate(n_0 = str_count(A_split, "0"), 
         n_0 = ifelse(n_0 == 0, NA, n_0), 
         n_1 = str_count(A_split, "1"), 
         n_1 = ifelse(n_1 == 0, NA, n_1)) %>% 
  group_by(A) %>% 
  summarise(spell_0 = paste(na.omit(n_0), collapse = "-"), 
            spell_1 = paste(na.omit(n_1), collapse = "-"), 
            mean_spell_0 = mean(n_0, na.rm = T), 
            mean_spell_1 = mean(n_1, na.rm = T)) 
 

Результат:

 #>                A spell_0 spell_1 mean_spell_0 mean_spell_1
#> 1     0-11-00-11     1-2     2-2          1.5          2.0
#> 2 00000-1111-000     5-3       4          4.0          4.0
#> 3       11-000-1       3     2-1          3.0          1.5
 

Создано 2021-10-25 пакетом reprex (v2.0.1)

Комментарии:

1. Вы можете свернуть первые три mutate s в одно: data.frame(A) %>% mutate(A = str_replace_all(A, "(.)(?!\1)(?!$)", "\1,"))

2. * Умно, но, вероятно, не очень эффективно! Мне нравится ваше упрощение! Однажды я выучу регулярное выражение.

Ответ №5:

 get_spells <- function(x, char){
  s <- sapply(gregexpr(paste0(char, " "), x), attr, "match")
  u <- sapply(s, paste0, collapse = "-")
  v <- sapply(s, mean)
  nms <- paste0("spell_", c(char, paste0("mean_", char)))
  setNames(data.frame(u, v) ,nms)
}

do.call(cbind, lapply(0:1, get_spells, x = A))
  spell_0 spell_mean_0 spell_1 spell_mean_1
1     5-3          4.0       4          4.0
2     1-2          1.5     2-2          2.0
3       3          3.0     2-1          1.5
 

Другим способом может быть:

 a <- strsplit(A, "(?<=(.))(?!\1)", perl=TRUE)


b <- lapply(a, function(x)
  unlist(tapply(nchar(x),sub("(.) ", "\1", x), function(x)
    c(setNames(paste(x, collapse = '-'), "spell"),
      setNames(mean(x), "mean_spell")))))

d <- type.convert(data.frame(do.call(rbind, b)), as.is = TRUE)
d
  X0.spell X0.mean_spell X1.spell X1.mean_spell
1      5-3           4.0        4           4.0
2      1-2           1.5      2-2           2.0
3        3           3.0      2-1           1.5