2016-05-29 2 views
7

Фон:Создание групп из вектора 0,1 и NA

Я пытаюсь вырезать корпус, в котором указан динамик. Я уменьшил проблему удаления конкретного динамика от corpuse до следующего потока 1,0 и NA (x). 0 означает, что человек говорит, 1 говорит кто-то другой, NA означает, что тот, кто был последним оратором, все еще говорит.

Вот визуальный пример:

0 1 S0: Hello, how are you today? 
1 2 S1: I'm great thanks for asking! 
NA 3 I'm a little tired though! 
0 4 S0: I'm sorry to hear that. Are you ready for our discussion? 
1 5 S1: Yes, I have everything I need. 
NA 7 Let's begin. 

Так из этого кадра, я хотел бы взять 2,3,5 и 7. Или ,. Я бы хотел, чтобы результат был 0,1,1,0,1,1.

Как поместить позиции каждого прогона 1 и NA до положения до следующего 0 в векторе.

Вот пример, и мой желаемый результат:

Пример ввода:

x <- c(NA,NA,NA,NA,0,1,0,1,NA,NA,NA,0,NA,NA,1,NA,NA,0) 

Пример вывода:

Эти позиции, которые я хочу, потому что они определяют, что «динамик 1» (1 или 1, а затем NA до следующего 0)

pos <- c(6,8,9,10,11,15,16,17) 

Альтернативный выход будет начинка:

fill <- c(0,0,0,0,0,1,0,1,1,1,1,0,0,0,1,1,1,0) 

В случае, если значения NA предыдущего 1 или 0 заполнены до следующего нового значения.

+0

Я не совсем понимаю, как именно вы получаете нужную 'pos' вектор , Не могли бы вы объяснить немного более точно, что вы хотите? –

+0

Я попытался обновить его наглядным примером моей проблемы. –

ответ

4
s <- which(x==1); 
e <- c(which(x!=1),length(x)+1L); 
unlist(Map(seq,s,e[findInterval(s,e)+1L]-1L)); 
## [1] 6 8 9 10 11 15 16 17 

Каждое вхождение 1 во входном векторе является начало последовательности позиционных индексов, применимых к оратору 1. Мы регистрируем это в s с which(x==1).

Для каждого начального индекса мы должны найти длину его содержащей последовательности. Длина определяется ближайшим прямым вхождением 0 (или, в более общем смысле, любым значением не-NA, отличным от 1, если таковое возможно). Следовательно, мы должны сначала вычислить which(x!=1), чтобы получить эти индексы. Поскольку окончательное появление 1 может не иметь прямого появления 0, мы должны добавить дополнительный виртуальный индекс на единицу за конец входного вектора, поэтому мы должны позвонить c(), чтобы объединить length(x)+1L. Мы сохраняем это как e, что отражает (потенциал) end индексы. Обратите внимание, что это эксклюзивные концевые индексы; они фактически не являются частью (потенциальной) предшествующей колонке 1 последовательности.

Наконец, мы должны сгенерировать фактические последовательности. Чтобы сделать это, мы должны сделать один звонок seq() для каждого элемента s, также передавая его соответствующий конечный индекс от e. Чтобы найти конечный индекс, мы можем использовать findInterval(), чтобы найти индекс в e, значение элемента которого (то есть индекс конца в x) падает только до каждого соответствующего элемента s. (Причина, почему это просто перед тем является то, что алгоритм, используемый findInterval() является v[i[j]] ≤ x[j] < v[i[j]+1] как объяснено на doc странице.), То мы должны прибавить к нему, чтобы получить индекс в e, значение которого элемент падает только после каждого соответствующего элемент s. Затем мы индексируем e, предоставляя нам конечные индексы в x, которые следуют за каждым соответствующим элементом s. Мы должны вычесть его из этого, потому что генерируемая последовательность должна исключать (исключительный) конечный элемент. Самый простой способ сделать звонки seq() - это Map() два вектора конечной точки, возвращая список каждой последовательности, который мы можем сделать unlist(), чтобы получить требуемый результат.


s <- which(!is.na(x)); 
rep(c(0,x[s]),diff(c(1L,s,length(x)+1L))); 
## [1] 0 0 0 0 0 1 0 1 1 1 1 0 0 0 1 1 1 0 

Каждого вхождение не-NA значения во входном векторе является начала сегмента, который, в выходных данных, должен стать повторением значения элемента при этом начальном индексе. Мы фиксируем эти индексы в s с which(!is.na(x));.

Затем мы должны повторить каждый начальный элемент достаточное количество раз, чтобы достичь следующего сегмента. Следовательно, мы можем назвать rep() по адресу x[s] с векторизованным аргументом times, значения которого состоят из diff(), вызываемого по адресу s. Чтобы обработать конечный сегмент, мы должны добавить индекс на единицу за конец входного вектора, length(x)+1L. Кроме того, для рассмотрения возможного случая NA, ведущего входной вектор, мы должны добавить 0 к x[s] и 1 к аргументу diff(), который будет повторять 0 достаточное количество раз, чтобы охватить ведущие НС, если таковые существуют.


Сравнительный анализ (Положение)

library(zoo); 
library(microbenchmark); 
library(stringi); 

marat <- function(x) { v <- na.locf(zoo(x)); index(v)[v==1]; }; 
rawr <- function(x) which(zoo::na.locf(c(0L, x))[-1L] == 1L); 
jota1 <- function(x) { stringx <- paste(x, collapse = ""); stringx <- gsub("NA", "N", stringx, fixed = TRUE); while(grepl("(?<=1)N", stringx, perl = TRUE)) stringx <- gsub("(?<=1)N", "1", stringx, perl = TRUE); unlist(gregexpr("1", stringx)); }; 
jota2 <- function(x) { stringx <- paste(x, collapse = ""); stringx <- gsub("NA", "N", stringx, fixed = TRUE); while(grepl("(?<=1)N", stringx, perl = TRUE)) stringx <- gsub("(?<=1)N", "1", stringx, perl = TRUE); newx <-unlist(strsplit(stringx, "")); which(newx == 1); }; 
jota3 <- function(x) {x[is.na(x)] <- "N"; stringx <- stri_flatten(x); ones <- stri_locate_all_regex(stringx, "1N*")[[1]]; unlist(lapply(seq_along(ones[, 1]), function(ii) seq.int(ones[ii, "start"], ones[ii, "end"]))); }; 
bgoldst <- function(x) { s <- which(x==1); e <- c(which(x!=1),length(x)+1L); unlist(Map(seq,s,e[findInterval(s,e)+1L]-1L)); }; 

## OP's test case 
x <- c(NA,NA,NA,NA,0,1,0,1,NA,NA,NA,0,NA,NA,1,NA,NA,0); 

ex <- marat(x); 
identical(ex,rawr(x)); 
## [1] TRUE 
identical(ex,jota1(x)); 
## [1] TRUE 
identical(ex,jota2(x)); 
## [1] TRUE 
identical(ex,jota3(x)); 
## [1] TRUE 
identical(ex,bgoldst(x)); 
## [1] TRUE 

microbenchmark(marat(x),rawr(x),jota1(x),jota2(x),jota3(x),bgoldst(x)); 
## Unit: microseconds 
##  expr  min  lq  mean median  uq  max neval 
## marat(x) 411.830 438.5580 503.24486 453.7400 489.2345 2299.915 100 
##  rawr(x) 115.466 143.0510 154.64822 153.5280 163.7920 276.692 100 
## jota1(x) 448.180 469.7770 484.47090 479.6125 491.1595 835.633 100 
## jota2(x) 440.911 464.4315 478.03050 472.1290 484.3170 661.579 100 
## jota3(x) 53.885 65.4315 74.34808 71.2050 76.9785 158.232 100 
## bgoldst(x) 34.212 44.2625 51.54556 48.5395 55.8095 139.843 100 

## scale test, high probability of NA 
set.seed(1L); 
N <- 1e5L; probNA <- 0.8; x <- sample(c(NA,T),N,T,c(probNA,1-probNA)); x[which(x)] <- rep(c(0,1),len=sum(x,na.rm=T)); 

ex <- marat(x); 
identical(ex,rawr(x)); 
## [1] TRUE 
identical(ex,jota1(x)); 
## [1] TRUE 
identical(ex,jota2(x)); 
## [1] TRUE 
identical(ex,jota3(x)); 
## [1] TRUE 
identical(ex,bgoldst(x)); 
## [1] TRUE 

microbenchmark(marat(x),rawr(x),jota1(x),jota2(x),jota3(x),bgoldst(x)); 
## Unit: milliseconds 
##  expr  min  lq  mean median  uq  max neval 
## marat(x) 189.34479 196.70233 226.72926 233.39234 237.45738 293.95154 100 
##  rawr(x) 24.46984 27.46084 43.91167 29.92112 68.86464 79.53008 100 
## jota1(x) 154.91450 157.09231 161.73505 158.18326 160.42694 206.04889 100 
## jota2(x) 149.47561 151.68187 155.92497 152.93682 154.79668 201.13302 100 
## jota3(x) 82.30768 83.89149 87.35308 84.99141 86.95028 129.94730 100 
## bgoldst(x) 80.94261 82.94125 87.80780 84.02107 86.10844 130.56440 100 

## scale test, low probability of NA 
set.seed(1L); 
N <- 1e5L; probNA <- 0.2; x <- sample(c(NA,T),N,T,c(probNA,1-probNA)); x[which(x)] <- rep(c(0,1),len=sum(x,na.rm=T)); 

ex <- marat(x); 
identical(ex,rawr(x)); 
## [1] TRUE 
identical(ex,jota1(x)); 
## [1] TRUE 
identical(ex,jota2(x)); 
## [1] TRUE 
identical(ex,jota3(x)); 
## [1] TRUE 
identical(ex,bgoldst(x)); 
## [1] TRUE 

microbenchmark(marat(x),rawr(x),jota1(x),jota2(x),jota3(x),bgoldst(x)); 
## Unit: milliseconds 
##  expr  min  lq  mean median  uq  max neval 
## marat(x) 178.93359 189.56032 216.68963 226.01940 234.06610 294.6927 100 
##  rawr(x) 17.75869 20.39367 36.16953 24.44931 60.23612 79.5861 100 
## jota1(x) 100.10614 101.49238 104.11655 102.27712 103.84383 150.9420 100 
## jota2(x) 94.59927 96.04494 98.65276 97.20965 99.26645 137.0036 100 
## jota3(x) 193.15175 202.02810 216.68833 209.56654 227.94255 295.5672 100 
## bgoldst(x) 253.33013 266.34765 292.52171 292.18406 311.20518 387.3093 100 

Бенчмаркинг (Fill)

library(microbenchmark); 

bgoldst <- function(x) { s <- which(!is.na(x)); rep(c(0,x[s]),diff(c(1L,s,length(x)+1L))); }; 
user31264 <- function(x) { x[is.na(x)]=2; x.rle=rle(x); val=x.rle$v; if (val[1]==2) val[1]=0; ind = (val==2); val[ind]=val[which(ind)-1]; rep(val,x.rle$l); }; 

## OP's test case 
x <- c(NA,NA,NA,NA,0,1,0,1,NA,NA,NA,0,NA,NA,1,NA,NA,0); 

ex <- bgoldst(x); 
identical(ex,user31264(x)); 
## [1] TRUE 

microbenchmark(bgoldst(x),user31264(x)); 
## Unit: microseconds 
##   expr min  lq  mean median  uq max neval 
## bgoldst(x) 10.264 11.548 14.39548 12.403 13.258 73.557 100 
## user31264(x) 31.646 32.930 35.74805 33.785 35.068 84.676 100 

## scale test, high probability of NA 
set.seed(1L); 
N <- 1e5L; probNA <- 0.8; x <- sample(c(NA,T),N,T,c(probNA,1-probNA)); x[which(x)] <- rep(c(0,1),len=sum(x,na.rm=T)); 

ex <- bgoldst(x); 
identical(ex,user31264(x)); 
## [1] TRUE 

microbenchmark(bgoldst(x),user31264(x)); 
## Unit: milliseconds 
##   expr  min  lq  mean median  uq  max neval 
## bgoldst(x) 10.94491 11.21860 12.50473 11.53015 12.28945 50.25899 100 
## user31264(x) 17.18649 18.35634 22.50400 18.91848 19.53708 65.02668 100 

## scale test, low probability of NA 
set.seed(1L); 
N <- 1e5L; probNA <- 0.2; x <- sample(c(NA,T),N,T,c(probNA,1-probNA)); x[which(x)] <- rep(c(0,1),len=sum(x,na.rm=T)); 

ex <- bgoldst(x); 
identical(ex,user31264(x)); 
## [1] TRUE 

microbenchmark(bgoldst(x),user31264(x)); 
## Unit: milliseconds 
##   expr  min  lq  mean median  uq  max neval 
## bgoldst(x) 5.24815 6.351279 7.723068 6.635454 6.923264 45.04077 100 
## user31264(x) 11.79423 13.063710 22.367334 13.986584 14.908603 55.45453 100 
+1

Интересно. Знаете, мне заплатили, чтобы писать R-код уже более 7 лет, и я до сих пор не понимаю 'findInterval()'. Кажется, это лучший ответ этой группы, но объяснение вашего кода решения может быть полезно для прохожего. –

+1

@BrandonBertelsen См. Редактирование. – bgoldst

+1

@Jota Nice дополнение. Также добавлено решение rawr. – bgoldst

2

Приклеивание последовательности в строку и используя время цикла, который проверяет (с grep) имеется ли какая-либо NA ей предшествует 1 с и заменителями (с gsub) такие случаи с 1 будут делать это:

# substitute NA for "N" for later ease of processing and locating 1s by position 
x[is.na(x)] <- "N" 
# Collapse vector into a string 
stringx <- paste(x, collapse = "") 

while(grepl("(?<=1)N", stringx, perl = TRUE)) { 
    stringx <- gsub("(?<=1)N", "1", stringx, perl = TRUE) 
} 

Затем вы можете использовать gregexpr, чтобы получить индексы 1s.

unlist(gregexpr("1", stringx)) 
#[1] 6 8 9 10 11 15 16 17 

Или вы можете разбить строку и просмотреть, чтобы найти индексы 1s в результирующем векторе:

newx <-unlist(strsplit(stringx, "")) 
#[1] "N" "N" "N" "N" "0" "1" "0" "1" "1" "1" "1" "0" "N" "N" "1" "1" "1" "0" 

which(newx == "1") 
#[1] 6 8 9 10 11 15 16 17 


Использование stri_flatten из stringi пакета вместо paste и stri_locate_all_fixed, а не gregexpr, или трассировка строки может обеспечить немного большую производительность, если это более крупный vecto Вы обрабатываете. Если вектор невелик, результат не достигнет производительности.

library(stringi) 
x[is.na(x)] <- "N" 
stringx <- stri_flatten(x) 

while(grepl("(?<=1)N", stringx, perl = TRUE)) { 
    stringx <- gsub("(?<=1)N", "1", stringx, perl = TRUE) 
} 

stri_locate_all_fixed(stringx, "1")[[1]][,"start"] 

следующий подход довольно прост и выполняет относительно хорошо (на основе прекрасным примером bgoldst в контрольных параметров) на малых и больших образцов (очень хорошо на высокой вероятностью bgoldst в качестве примера NA)

x[is.na(x)] <- "N" 
stringx <- stri_flatten(x) 

ones <- stri_locate_all_regex(stringx, "1N*")[[1]] 

#[[1]] 
# 
#  start end 
#[1,]  6 6 
#[2,]  8 11 
#[3,] 15 17 

unlist(lapply(seq_along(ones[, 1]), 
    function(ii) seq.int(ones[ii, "start"], ones[ii, "end"]))) 
#[1] 6 8 9 10 11 15 16 17 
3

Вы можете использовать na.locf из zoo пакета:

library(zoo) 
x <- c(NA,NA,NA,NA,0,1,0,1,NA,NA,NA,0,NA,NA,1,NA,NA,0) 

v <- na.locf(zoo(x)) 
index(v)[v==1] 
#[1] 6 8 9 10 11 15 16 17 
+0

Для табуляции do 'tabulate (result, nbins = length (x))' where' result = index (v) [v == 1] ' –

+3

' which (zoo :: na.locf (c (0L, x)) [- 1L] == 1L) 'примерно на 4 раза быстрее – rawr

+0

@rawr Добавлено ваше решение в мои тесты, отличное дополнение. – bgoldst

3
x <- c(NA,NA,NA,NA,0,1,0,1,NA,NA,NA,0,NA,NA,1,NA,NA,0) 
x[is.na(x)]=2 
x.rle=rle(x) 
val=x.rle$v 
if (val[1]==2) val[1]=0 
ind = (val==2) 
val[ind]=val[which(ind)-1] 
rep(val,x.rle$l) 

Выход:

[1] 0 0 0 0 0 1 0 1 1 1 1 0 0 0 1 1 1 0 
+0

Я также играл с решением, которое использовало 'rle', но я не мог его отсортировать. Мне нравится эта идея. –