중복 duplicated, rleid

Published onesixx on

duplicated(), Unique()

data(iris)
dd <- data.table(iris)

dd %>% distinct()
dd[ , unique(.SD)]

## 행의 모든 컬럼이 (다른 행과) 중복값을 가지는 경우
duplicated(dd) %>% sum()       # 1개
duplicated(dd) %>% which()     # 143번째  (102번째값과 모든 컬럼이 일치)

result1 <- unique(dd)          # 143번째 삭제 
# dim(d0)                 #[1] 149   5

항상 가장 위에 있는 것만 남김.
따라서, 잘 정렬후 중복을 삭제할 필요가 있음.

## 특정 컬럼이 중복값을 가지는 경우
# Species 컬럼의 중복 행수 확인
duplicated(dd$Species) %>% sum()       # 147개
duplicated(dd$Species) %>% which()     # (1, 51, 101번째값 제외)

result2 <- dd[!duplicated(iris$Species), ]
#    Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
#1            5.1         3.5          1.4         0.2     setosa
#51           7.0         3.2          4.7         1.4 versicolor
#101          6.3         3.3          6.0         2.5  virginica
result3 <- dd1[!duplicated(dd1$Species), ]
#   Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
#1           7.9         3.8          6.4         2.0  virginica
#13          7.0         3.2          4.7         1.4 versicolor
#71          5.8         4.0          1.2         0.2     setosa

duplicated() 한계

unique()나 duplicated()는 중복을 제거하는데 유용하지만,
연속된(consecutive) 중복값을 제거하는데 사용할 수 없다.

a <- c(1,1,1,2,2,3,4,4,4,4,5,5,5,5,1,1,1,2,2,3)
a[!duplicated(a)]
# [1] 1 2 3 4 5
원하는 값은
# [1] 1 2 3 4 5 1 2 3  또는  [1] 1 1 2 2 3 4 4 5 5 1 1 2 2 3

순서가 있는 경우는 duplicated()도 유용하게 사용된다.

a <- c(1,1,1,2,2,3,4,4,4,4,5,5,5,5)
       F T T F T F F T T T F T T T  # 앞을 기준으로 중복 
       T T F T F F T T T F T T T F  # 뒤를 기준으로 중복 
       F T F F F F F T T F F T T F  # 중복 시작 끝만 F
       T F T T T T T F F T T F F T  # 중복 시작 끝만 T

duplicated(a)
duplicated(a, fromLast=T)
  duplicated(a) & duplicated(a, fromLast=T)
!(duplicated(a) & duplicated(a, fromLast=T))

DT <- fread("
  A   B   C 
  1   a   1 
  2   a   1 
  3   a   1
  4   a   2
  5   a   3
  6   a   3 
  7   a   1   
  8   a   2   
  9   a   2
 10   a   2
")

DT <- read.table(header=T, stringsAsFactors=F, text="
  A   B   C 
  1   a   1 
  2   a   1 
  3   a   1
  4   a   2
  5   a   3
  6   a   3 
  7   a   1   
  8   a   2   
  9   a   2
 10   a   2
") %>% data.table()
# 원하는 결과가 아님
DT[!duplicated(DT[,c('C')]),]   # 변수명으로 제거
DT[!duplicated(DT[,c(3)]),]     # 인덱스로 제거
#    A B C
# 1: 1 a 1
# 2: 4 a 2
# 3: 5 a 3

rleid() :: run length encoding id

run-length 형태의 group id

rle는 (잘 사용되진 않지만) 압축방법중, 같은값이 연속될때 그 반복수와 반복되는 값만으로 표현하는 방법.
- 원래 문자열 : ("a","a","a","c","c","b","b","b","b")
- 압축 문자열 : a3c2b4 
a가 3개 , c가 2개, b가 4개로 저장
code[] = {a, c, b}, len[]={ 3, 2, 4}

> DT = data.table(grp=c("a","a","a","c","c","b","b","b","b"))
> DT[ , gid:=rleid(grp)]
   grp gid
1:   a   1
2:   a   1
3:   a   1
4:   c   2
5:   c   2
6:   b   3
7:   b   3
8:   b   3
9:   b   3
> rleid(DT$grp)
> rleidv(DT,"grp") # same as above
[1] 1 1 1 2 2 3 3 3 3
> rleid(DT$grp, prefix="grp") # prefix with 'grp'
[1] "grp1" "grp1" "grp1" "grp2" "grp2" "grp3" "grp3" "grp3" "grp3"
> DT = data.table(grp=c("a","a","a","c","c","b","b","b","b"), value=1:9)
> DT[, cumsum(value), by=.(grp, rleid(grp))]
   grp rleid V1
1:   a     1  1
2:   a     1  3
3:   a     1  6
4:   c     2  4
5:   c     2  9
6:   b     3  6
7:   b     3 13
8:   b     3 21
9:   b     3 30
> DT[, sum(value), by=.(grp, rleid(grp))]
   grp rleid V1
1:   a     1  6
2:   c     2  9
3:   b     3 30

rleid() plot에 활용

DT[, rleid(C)]
# [1] 1 1 1 2 3 3 4 5 5 5
> DT[, rleC:=rleid(C)]
> DT
     A B C rleC
 1:  1 a 1    1
 2:  2 a 1    1
 3:  3 a 1    1
 4:  4 a 2    2
 5:  5 a 3    3
 6:  6 a 3    3
 7:  7 a 1    4
 8:  8 a 2    5
 9:  9 a 2    5
10: 10 a 2    5

data.table의 rleid()는 활용해 rle형식의 id를 만들어 주고, 이를 활용해 중복값의 시작id과 마지막id를 찾을 수 있다.

# 중복rleid 첫값
> DT[!(duplicated(rleid(C)))]
   A B C rleC
1: 1 a 1    1
2: 4 a 2    2
3: 5 a 3    3
4: 7 a 1    4
5: 8 a 2    5
# 중복rleid 마지막값
> DT[!(duplicated(rleid(C), fromLast=T))]
    A B C rleC
1:  3 a 1    1
2:  4 a 2    2
3:  6 a 3    3
4:  7 a 1    4
5: 10 a 2    5

그래프를 그리기 위해, 중복 첫값과 마지막 값을 남긴다.

DTdup <- DT[!(duplicated(rleid(C)) & duplicated(rleid(C), fromLast=T)), ]
#     A B C
# 1:  1 a 1
# 2:  3 a 1
# 3:  4 a 2
# 4:  5 a 3
# 5:  6 a 3
# 6:  7 a 1
# 7:  8 a 2

DT    %>% ggplot(aes(x=A, y=C)) + geom_point(size=3) + geom_line()
DTdup %>% ggplot(aes(x=A, y=C)) + geom_point(size=3) + geom_line()

rleid 활용

# using rleid, get max(y) and min of all cols in .SDcols for each consecutive run of 'v'
> DT = data.table(x=rep(c("b","a","c"),each=3), 
                  v=c(1,1,1,2,2,1,1,2,2), 
                  y=c(1,3,6), 
                  a=1:9, 
                  b=9:1)
> DT
   x v y a b
1: b 1 1 1 9
2: b 1 3 2 8
3: b 1 6 3 7
4: a 2 1 4 6
5: a 2 3 5 5
6: a 1 6 6 4
7: c 1 1 7 3
8: c 2 3 8 2
9: c 2 6 9 1

> DT[, c(.(y=max(y)), lapply(.SD, min)), by=rleid(v), .SDcols=v:b]
   rleid y v y a b
1:     1 6 1 1 1 7
2:     2 3 2 1 4 5
3:     3 6 1 1 6 3
4:     4 6 2 3 8 1
 flipTheCoin <- sample(c("H", "T"), 10, replace = TRUE)
 rle(flipTheCoin)
DT <- read.table(header=T, stringsAsFactors=F, text="
  A   B   C 
  1   a   1 
  2   a   1 
  3   a   1
  4   a   2
  5   a   3
  6   a   3 
  7   a   1   
  8   a   2   
  9   a   2
 10   a   2
 11   b   2
 12   b   2 
 13   b   3
 14   b   3
 15   b   3
 16   b   2
 17   b   2
 18   b   2
 19   b   3
") %>% data.table()

DT[, .SD[!(duplicated(rleid(C)) & duplicated(rleid(C), fromLast=T)), ], by=.(B)] 

Signal

> DT = data.table(grp=c(NA,0,0,0,1,1,-1,-1,-1,1), value=1:10)
> DT[, cumsum(value), by=.(grp, rleid(grp))]
    grp rleid V1
 1:  NA     1  1
 2:   0     2  2
 3:   0     2  5
 4:   0     2  9
 5:   1     3  5
 6:   1     3 11
 7:  -1     4  7
 8:  -1     4 15
 9:  -1     4 24
10:   1     5 10
> DT[, sum(value), by=.(grp, rleid(grp))]
   grp rleid V1
1:  NA     1  1
2:   0     2  9
3:   1     3 11
4:  -1     4 24
5:   1     5 10
Categories: R Reshaping

onesixx

Blog Owner

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x