수집한 데이터셋의 관측치들 간에 중복(duplication)이 있는 경우가 있습니다.  혹은 여러개의 흩어져 있는 데이터셋을 특정 기준 변수(key)를 사용해서 병합(merge)하는 전처리 작업을 하다 보면 가끔씩 의도하지 않게 중복데이터가 생기는 수가 있습니다.  서로 다른 행인데 각 변수별 관측값들이 동일한 경우 말이지요.  이럴 경우 저장 공간을 중복되는 만큼 더 잡아먹는고 데이터 처리 속도가 더 걸린다는 문제가 있으며, 더 심각하게는 이러한 데이터셋을 그대로 두고 통계 분석을 진행할 경우 분석 결과에 왜곡이 생길 수도 있으므로 중복 관측값 전처리가 필요합니다. (가령, 평균만 하더라도 중복이 있는 관측치가 여럿 있을 경우 분자의 관측치 합과 분모의 관측치 개수가 중복 제거후와는 서로 달라지겠지요?)

 

중복 데이터가 있을 경우에는 먼저 한개의 유일한 관측치(unique elements)만을 남겨놓고 나머지 중복 데이터들은 제거하는 전처리를 거쳐야 합니다. 이를 위해 R의

 

- {base} 패키지 : unique()

- {base} 패키지 : dataframe[!duplicated(var), ]

- {dplyr} 패키지 : distinct()

 

함수를 사용할 수 있습니다.

 

SAS 사용자라면 datastep에서 특정 변수를 기준으로 sorting을 한 후에 first.variable_name 또는 last.variable_name 명령문을 사용했던 것을 생각하시면 이해하기 쉬울 것입니다.  SAS와는 다르게 R은 sorting을 할 필요가 없습니다. (SAS는 merge 할 때도 먼저 sorting을 해줘야 하는 반면, R은 merge 할 때 sorting 해줄 필요 없습니다)

 

단, unique도 그렇고, merge도 그렇고 크기가 작은 데이터셋에는 별 무리가 없지만 대용량 데이터셋에 사용하기에는 처리 성능에 부담이 아주 많이 되는 함수입니다.  수십 기가가 넘는 대용량 사이즈의 데이터셋이라면 하둡 클러스터 내에서 Hive 등으로 중복 데이터 전처리하여 사용하시길 권합니다.

 

 

 

[ 중복없이 유일한 관측치만 남기기 (extracting unique elements) ]

 

 

 

 

R의 unique() 함수는 base package 함수에 내장되어 있으므로 별도의 패키지를 설치할 필요는 없습니다.  데이터 프레임(data frame), 배열(array), 행렬(matrix), 벡터(vector)에 모두 사용할 수 있는데요, 일반적으로 데이터 프레임을 분석에 많이 사용하므로 아래에는 데이터 프레임을 대상으로 unique() 함수 사용하는 방법을 소개하겠습니다.  

 

 

먼저 R 실습을 위해 관측치들 간에 중복이 포함되어 있는 변수 3개짜리 데이터 프레임을 만들어 보겠습니다.

 

> ##------------------------------------------
> ## extracting unique elements : unique()
> ##------------------------------------------
> 
> a1 <- rep(1:10, each = 2)
> a1
 [1]  1  1  2  2  3  3  4  4  5  5  6  6  7  7  8  8  9  9 10 10
> 
> a2 <- rep(c(1, 3, 5, 7, 9), each = 4)
> a2
 [1] 1 1 1 1 3 3 3 3 5 5 5 5 7 7 7 7 9 9 9 9
> 
> a3 <- c(1, 1, 1, 1, 3, 3, 3, 3, 5, 5, 6, 6, 7, 7, 8, 8, 9, 10, 11, 12)
> a3
 [1]  1  1  1  1  3  3  3  3  5  5  6  6  7  7  8  8  9 10 11 12
> 
> a1a2a3 <- data.frame(cbind(a1, a2, a3))
> a1a2a3
   a1 a2 a3
1   1  1  1
2   1  1  1
3   2  1  1
4   2  1  1
5   3  3  3
6   3  3  3
7   4  3  3
8   4  3  3
9   5  5  5
10  5  5  5
11  6  5  6
12  6  5  6
13  7  7  7
14  7  7  7
15  8  7  8
16  8  7  8
17  9  9  9
18  9  9 10
19 10  9 11
20 10  9 12
> 
> str(a1a2a3)
'data.frame':	20 obs. of  3 variables:
 $ a1: num  1 1 2 2 3 3 4 4 5 5 ...
 $ a2: num  1 1 1 1 3 3 3 3 5 5 ...
 $ a3: num  1 1 1 1 3 3 3 3 5 5 ...

 

 

 

 

[예제 1]

변수가 3개 있는 데이터 프레임인데요, a1과 a2의 두 개 변수를 기준으로 중복여부를 체크한 후에, 중복이 있을 경우에는 1개만 선택하고 나머지 중복된 관측치는 제거하는 방법에 대한 예시는 아래와 같습니다.

 

>

> # extracting unique elements by 2 variables > aqa2a3_uniq_var2 <- unique(a1a2a3[, c("a1", "a2")]) > aqa2a3_uniq_var2 a1 a2 1 1 1 3 2 1 5 3 3 7 4 3 9 5 5 11 6 5 13 7 7 15 8 7 17 9 9 19 10 9

 

 

 

 

위의 예제에서 "a1"변수와 "a2" 변수를 기준으로 중복 관측치를 제거하고 유일한 관측치만 남기는 처리 개념을 풀어서 설명하면 아래와 같습니다.  참고로, a1a2a3[, c("a1", "a2")] 은 a1a2a3 데이터 프레임에서 변수 "a1"과 "a2"만 select 하라는 뜻입니다.

 

 

 

 


 

 

[예제 2]

다음으로, "a1", "a2", "a3" 세 개의 변수를 기준으로 중복된 관측치가 있다면 유일한 관측치만 남기고 나머지 중복 관측치는 제거하는 예를 들어보겠습니다.

 

> 

> # extracting unique elements by 3 variables > aqa2a3_uniq_var3 <- unique(a1a2a3[, c("a1", "a2", "a3")]) > aqa2a3_uniq_var3 a1 a2 a3 1 1 1 1 3 2 1 1 5 3 3 3 7 4 3 3 9 5 5 5 11 6 5 6 13 7 7 7 15 8 7 8 17 9 9 9 18 9 9 10 19 10 9 11 20 10 9 12

 

 

 

 

위 R 함수의 처리 과정을 풀어보면 아래와 같습니다.

 

 

 

 

"a1", "a2"의 두 개 변수만을 기준으로 했을 때 대비, "a1", "a2", "a3"의 세 개변수를 기준으로 unique() 함수를 적용했을 때 18번째와 20번째 행이 중복없는 유일한 행으로서 추가로 살아남았음을 알 수 있습니다.

 

 

 


 

[예제 3]

unique(x, fromLast = TRUE) 옵션을 적용하면 중복된 관측치들 중에서 남길 관측치(행, row)를 선택할 때 가장 밑에 있는 관측치(from Last observation)를 기준으로 선택합니다. default 는 fromLast = FALSE 입니다.

 

> 
> # identical element will be kept from the last : fromLast = TRUE 
> # defaulu : fromLast = FALSE
> aqa2a3_uniq_var3_fromLast <- unique(a1a2a3[, c("a1", "a2", "a3")], fromLast = TRUE) 
> aqa2a3_uniq_var3_fromLast  # differnt order
   a1 a2 a3
2   1  1  1
4   2  1  1
6   3  3  3
8   4  3  3
10  5  5  5
12  6  5  6
14  7  7  7
16  8  7  8
17  9  9  9
18  9  9 10
19 10  9 11
20 10  9 12

 

 

 

 

 

[예제2]의 fromLast = FALSE (default 이므로 특별히 명기할 필요는 없음) 일 때와 [예제3]의 fromLast = TRUE 일 때의 차이가 무엇인지 잘 이해가 안 갈 수도 있는데요, 아래 [예제2]와 [예제3]의 데이터프레임들의 row names 의 번호가 서로 다름을 알 수 있습니다. 

 

1번째 행과 2번째 행이 중복된 것의 처리에 대해서만 설명드리자면, [예제2]의 fromLast = FALSE의 경우1번 행(first row)을 가져왔고, [예제3]의 fromLast = TRUE 의 경우 2버 행(second row)를 유일한 행으로 가져왔습니다.

 

 

 

 

[예제2]와 [예제3]의 결과는 동일합니다. 그런데 왜 굳이 이런것이 필요하지 싶을 것입니다. ^^?

위의 예제는 설명의 편의를 위해서 변수 3개 짜리 중복 데이터셋을 초간단으로 만들어본 것인데요, 실전에서 사용하는 데이터셋은 변수가 수십, 수백개, 관측치(행의 개수)도 수천, 수만, 수백만개인 경우가 다반사입니다.  이때 특정 변수를 기준으로 중복인 경우 유일한 관측치를 선별하고, 나머지 변수들은 그대로 사용해야 하는 경우 중복된 관측치의 first observation을 살려둘지 아니면 last obsetvation을 살려둘지에 따라서 중복 제거 기준 변수 이외의 타 변수들의 관측치 값들이 다른 경우에는 고민 좀 해야겠지요?  이럴 경우에 fromLast = FALSE/TRUE 옵션이 필요합니다.  (이럴 경우 SAS처럼 미리 특정 변수를 기준으로 정렬해놔야 겠군요)

 

 


 

 

{base} package에 내장되어 있는 duplicated() 함수를 사용해서 중복값을 제거하고 유일한 값만 선별할 수도 있습니다.  duplicated() 함수를 사용하면 아래 예시처럼 중복되는 행에 대해서 TRUE, FALSE boolean 값을 반환합니다.  이 논리형 값을 가지고 dataframe에서 indexing을 해오는 방식으로 중복값을 처리하고 유일한 값만 남겨놓을 수 있습니다.

 

 

> ##-----------------------
> ## duplicated() function
> ##-----------------------
> 
> # original dataset
> a1a2a3
   a1 a2 a3
1   1  1  1
2   1  1  1
3   2  1  1
4   2  1  1
5   3  3  3
6   3  3  3
7   4  3  3
8   4  3  3
9   5  5  5
10  5  5  5
11  6  5  6
12  6  5  6
13  7  7  7
14  7  7  7
15  8  7  8
16  8  7  8
17  9  9  9
18  9  9 10
19 10  9 11
20 10  9 12
> 
> 
> # returning TRUE for duplicated value
> duplicated(a1a2a3$a1) 
 [1] FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE
[12]  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE
> 
> 
> # indexing unduplicated rows using dataframe[!duplicated(dataframe$var1), ]
> a1a2a3_not_duplicated_var1 <- a1a2a3[!duplicated(a1a2a3$a1),]
> a1a2a3_not_duplicated_var1
   a1 a2 a3
1   1  1  1
3   2  1  1
5   3  3  3
7   4  3  3
9   5  5  5
11  6  5  6
13  7  7  7
15  8  7  8
17  9  9  9
19 10  9 11
> 
> # another exmaple
> a1a2a3[!duplicated(a1a2a3$a2),]
   a1 a2 a3
1   1  1  1
5   3  3  3
9   5  5  5
13  7  7  7
17  9  9  9

 

 

 


 

dplyr 패키지의 distinct() 함수도 중복이 없는 유일한 값을 반환합니다.  dplyr 패키지의 distinct() 가 깔끔하기도 하고, dplyr 패키지 내의 여러 데이터 전처리 함수를 함께 이용할 수 있어서 알아두시면 좋겠습니다.

 

 

 



10억 개의 정수 값에 대해 base 패키지의 unique(), duplicated() 함수와 dplyr패키지의 distinct() 함수를 적용해서 수행 시간 (elapsed time)을 비교해보니 dplyr의 distinct() 함수가 근소하게 빠르기는 합니다만, 차이가 그리 크지는 않네요. 


> a1 <- sample(1:99, 1000000000, replace=TRUE)

> system.time(unique(a1))

   user  system elapsed 

 13.676   7.327  22.999 

> system.time(!duplicated(a1))

   user  system elapsed 

 13.558   5.049  20.198 

> library(dplyr)

> system.time(dplyr::distinct(data.frame(a1)))

   user  system elapsed 

 17.602   1.645  19.267

 



많은 도움이 되었기를 바랍니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

 

 

728x90
반응형
Posted by Rfriend
,