Соединение двоичных интервалов заданного расстояния - PullRequest
1 голос
/ 17 апреля 2020

У меня есть ряд зон, определенных значением 1, и мне нужно объединить зоны, которые имеют пространство меньше двух ячеек, заменив эти 0 значений на 1. Например, для ячейки df [11,1] нужно должны быть заменены на 1, а ячейки df [15: 16,1] должны быть заменены на 1, в то время как ячейки df [21: 23,1] должны оставаться как 0.

> df <-  data.frame("Zone" = 1:25)
> df[1] <- 0
> df <-  data.frame("Zone" = 1:25)
> df[1] <- 0
> df[4:10,1] <- 1
> df[12:14,1] <- 1
> df[17:20,1] <- 1
> df[24:25,1] <- 1
> df
   Zone
1     0
2     0
3     0
4     1
5     1
6     1
7     1
8     1
9     1
10    1
11    0
12    1
13    1
14    1
15    0
16    0
17    1
18    1
19    1
20    1
21    0
22    0
23    0
24    1
25    1

Ответы [ 2 ]

1 голос
/ 17 апреля 2020

Мы можем использовать rleid из data.table, чтобы получить идентификатор длины кодировки, основанный на разнице в соседних элементах столбца, а затем получить индекс строки (.I), где выполняется условие, и обновите «Зону», указав индекс в i при обновлении «Зоны» на 1

library(data.table)
i1 <- setDT(df)[, grp := rleid(Zone)][, .I[Zone == 0 & .N <=2], grp]$V1

df[i1, Zone := 1][, grp  := NULL][]
#    Zone
# 1:    0
# 2:    0
# 3:    0
# 4:    1
# 5:    1
# 6:    1
# 7:    1
# 8:    1
# 9:    1
#10:    1
#11:    1
#12:    1
#13:    1
#14:    1
#15:    1
#16:    1
#17:    1
#18:    1
#19:    1
#20:    1
#21:    0
#22:    0
#23:    0
#24:    1
#25:    1

Или используя dplyr, создав группу, сравнивающую соседние элементы, а затем заменив значение с помощью case_when

library(dplyr)
df %>%
   group_by(grp = cumsum(Zone != lag(Zone, default = first(Zone)))) %>%
    mutate(Zone = case_when(Zone == 0 & n() <=2 ~ 1, TRUE  ~Zone)) %>%
    ungroup %>%
    select(-grp) 

Или с использованием rle/inverse.rle из base R

inverse.rle(within.list(rle(df$Zone), values[values== 0 & lengths <=2] <- 1))
#[1] 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1

Или его можно сделать слегка компактным с помощью

with(rle(df$Zone), +(rep((!values & lengths <=2)|values, lengths)))

данные

df <- structure(list(Zone = c(0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 
1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1)), row.names = c(NA, -25L),
   class = "data.frame")
1 голос
/ 17 апреля 2020

Используя rle из базы R, мы можем проверить 0 значений в Zone и изменить их на 1, если их длина меньше, чем 2.

df$Zone[with(rle(df$Zone == 0), rep(values & lengths <= 2, lengths))] <- 1
df

#   Zone
#1     0
#2     0
#3     0
#4     1
#5     1
#6     1
#7     1
#8     1
#9     1
#10    1
#11    1
#12    1
#13    1
#14    1
#15    1
#16    1
#17    1
#18    1
#19    1
#20    1
#21    0
#22    0
#23    0
#24    1
#25    1

data

df <- structure(list(Zone = c(0L, 0L, 0L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
       0L, 1L, 1L, 1L, 0L, 0L, 1L, 1L, 1L, 1L, 0L, 0L, 0L, 1L, 1L)), 
       class = "data.frame", row.names = c(NA, -25L))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...