Хранить последовательные дубликаты - PullRequest
0 голосов
/ 21 октября 2019

У меня есть фрейм данных, в котором один столбец содержит несколько последовательных дубликатов. Я хочу сохранить строки с последовательными дубликатами (любая длина> 1). Я бы предпочел решение в dplyr или data.table.

Пример данных:

a <- seq(10,150,10)
b <- c("A", "A", "B", "C", "C", "A", "B", "B", "B", "C", "A", "C", "D", "E", "E")

df <- tibble(a, b)

Данные:

# A tibble: 15 x 2
       a b    
   <dbl> <chr>
 1    10 A    
 2    20 A    
 3    30 B    
 4    40 C    
 5    50 C    
 6    60 A    
 7    70 B    
 8    80 B    
 9    90 B    
10   100 C    
11   110 A    
12   120 C    
13   130 D    
14   140 E    
15   150 E 

Поэтому я бы хотел сохранитьстроки с последовательными дубликатами в столбце b.

Ожидаемый результат:

# A tibble: 9 x 2
       a b    
   <dbl> <chr>
 1    10 A    
 2    20 A    
 4    40 C    
 5    50 C    
 7    70 B    
 8    80 B    
 9    90 B          
14   140 E    
15   150 E 

Спасибо!

Ответы [ 7 ]

2 голосов
/ 21 октября 2019

Используя входные данные data.table, показанные в конце заметки, установите N равным количеству элементов в каждой группе последовательных элементов, а затем сохраните группы, для которых оно больше 1.

DT[, N :=.N, by = rleid(b)][N > 1, .(a, b)]

предоставление:

     a b
1:  10 A
2:  20 A
3:  40 C
4:  50 C
5:  70 B
6:  80 B
7:  90 B
8: 140 E
9: 150 E

Примечание

Мы предполагаем, что ввод в воспроизводимой форме:

library(data.table)
a <- seq(10,150,10)
b <- c("A", "A", "B", "C", "C", "A", "B", "B", "B", "C", "A", "C", "D", "E", "E")
DT <- data.table(a, b)
0 голосов
/ 22 октября 2019

Вот еще один вариант (который должен быть быстрее):

D[-D[, {
    x <- rowid(rleid(b)) < 2
    .I[x & shift(x, -1L, fill=TRUE)]
}]]

код времени:

library(data.table)
set.seed(0L)
nr <- 1e7
nb <- 1e4
DT <- data.table(b=sample(nb, nr, TRUE))
#DT <- data.table(b=c("A", "A", "B", "C", "C", "A", "B", "B", "B", "C", "A", "C", "D", "E", "E"))
DT2 <- copy(DT)

mtd1 <- function(df) {
    df[-cumsum(rle(b)$lengths)[rle(b)$lengths==1],]
}

mtd2 <- function(D) {
    D[, N :=.N, by = rleid(b)][N > 1, .(b)]
}

mtd3 <- function(D) {
    D[-D[, {
        x <- rowid(rleid(b)) < 2
        .I[x & shift(x, -1L, fill=TRUE)]
    }]]
}

bench::mark(mtd1(DT), mtd2(DT2), mtd3(DT), check=FALSE)

время:

# A tibble: 3 x 13
  expression      min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result             memory          time    gc            
  <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list>             <list>          <list>  <list>        
1 mtd1(DT)       1.1s     1.1s     0.908    1.98GB    10.9      1    12       1.1s <df[,1] [2,014 x ~ <df[,3] [59 x ~ <bch:t~ <tibble [1 x ~
2 mtd2(DT2)     2.88s    2.88s     0.348  267.12MB     0        1     0      2.88s <df[,1] [2,014 x ~ <df[,3] [23 x ~ <bch:t~ <tibble [1 x ~
3 mtd3(DT)   639.91ms 639.91ms     1.56   505.48MB     4.69     1     3   639.91ms <df[,1] [2,014 x ~ <df[,3] [24 x ~ <bch:t~ <tibble [1 x ~
0 голосов
/ 21 октября 2019

В другом решении используются lead() и lag():

library(tidyverse)

a <- seq(10,150,10)
b <- c("A", "A", "B", "C", "C", "A", "B", "B", "B", "C", "A", "C", "D", "E", "E")

df <- tibble(a, b)

df %>% filter(b == lead(b) | b == lag(b))
#> # A tibble: 9 x 2
#>       a b    
#>   <dbl> <chr>
#> 1    10 A    
#> 2    20 A    
#> 3    40 C    
#> 4    50 C    
#> 5    70 B    
#> 6    80 B    
#> 7    90 B    
#> 8   140 E    
#> 9   150 E

Создано в 2019-10-21 пакетом Представить (v0.3.0)

0 голосов
/ 21 октября 2019

Используйте rle, чтобы получить длину пробега.

Если предположить df <- data.frame(a=a,b=b), то следующее может сделать это

df[-cumsum(rle(b)$lengths)[rle(b)$lengths==1],]
0 голосов
/ 21 октября 2019

Вы хотите удалить дубликаты, за исключением случаев, когда они последовательны: следующий код помечает дубликаты и последовательные значения, а затем сохраняет только строки, которые не являются дубликатами или являются частью последовательного набора дубликатов.

df %>%
  mutate(duplicate = duplicated(b), 
         consecutive = c(NA, diff(as.integer(factor(b)))) == 0) %>%
  filter(!duplicate | consecutive) %>%
  select(-duplicate, -consecutive)
0 голосов
/ 21 октября 2019

В dplyr мы можем использовать lag для создания групп и выбора групп с более чем 1 строкой.

library(dplyr)

df %>%
  group_by(group = cumsum(b != lag(b, default = first(b)))) %>%
  filter(n() > 1) %>%
  ungroup() %>%
  select(-group)

#     a  b    
#  <dbl> <chr>
#1    10 A    
#2    20 A    
#3    40 C    
#4    50 C    
#5    70 B    
#6    80 B    
#7    90 B    
#8   140 E    
#9   150 E  

В базе R мы можем использовать строки rle и ave до subset от df

subset(df, ave(b, with(rle(b), rep(seq_along(values), lengths)), FUN = length) > 1)  
0 голосов
/ 21 октября 2019

Так как у вас также есть тег data.table, мне нравится использовать функцию data.table::rleid для таких задач, то есть

library(dplyr)

df %>% 
 group_by(grp = data.table::rleid(b), b) %>% 
 filter(n() > 1)

, которая дает,

# A tibble: 9 x 3
# Groups:   grp, b [4]
      a b       grp
  <dbl> <chr> <int>
1    10 A         1
2    20 A         1
3    40 C         3
4    50 C         3
5    70 B         5
6    80 B         5
7    90 B         5
8   140 E        10
9   150 E        10
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...