[R] 최빈값을 구하는 사용자 정의 함수 뜯어보기

BeomHwan Roh
5 min readOct 27, 2021

--

카이제곱 분포에서의 평균과 중앙값, 최빈값

R 프로그램에는 벡터를 대입했을 때 평균(mean)중앙값(median)을 출력하는 기본 내장 함수가 들어있다.

> # 자료 생성
> set.seed(82)
> x = sample(c(1, 2, 3, 4), 15, replace=TRUE)
> x
[1] 4 1 3 3 3 2 2 1 1 3 3 4 4 4 2
> # 평균 구하기
> mean(x)
[1] 2.666667
> # 중앙값 구하기
> median(x)
[1] 3

하지만 최빈값(mode)의 경우 기본 내장함수가 제공되지 않는데, 이 경우 function 문을 이용하여 직접 최빈값을 구하는 사용자 정의 함수를 만들어야 한다. 결론부터 보자면 최빈값을 구하는 사용자 정의 함수는 다음과 같이 만들 수 있다.

> # 최빈값 사용자 정의 함수 만들기
> getmode = function(t){
+ u = unique(t)
+ u[which.max(tabulate(match(t, u)))]
+ }
> # 최빈값 구하기
> getmode(x)
[1] 3

R의 기본 내장 함수인 mode 는 객체의 내부 타입을 출력하는 함수이므로 함수명이 겹치지 않게 getmode 로 이름 지었다.

최빈값 사용자 정의 함수를 구성하는 함수들(unique , which.max , tabulate , match)의 원리를 위주로 코드를 해석해보고자 한다.

unique 함수

unique 함수는 입력 받은 벡터(또는 데이터프레임 · 배열)에서 중복되는 원소(또는 행)을 제거하는 함수다.

> x
[1] 4 1 3 3 3 2 2 1 1 3 3 4 4 4 2
> u = unique(x)
> u
[1] 4 1 3 2

match 함수

match 함수는 첫번째 벡터의 원소가 두번째 벡터의 몇번째 원소와 같은지를 출력하는 함수다.

> x
[1] 4 1 3 3 3 2 2 1 1 3 3 4 4 4 2
> u
[1] 4 1 3 2
> match(x, u)
[1] 1 2 3 3 3 4 4 2 2 3 3 1 1 1 4

위의 예에서 벡터 x 의 원소 4 1 3 3 3 … 가 벡터 u 의 몇번째 원소와 같은지를 보면 된다. (첫번째- 4 , 두번째- 1 , 세번째- 3 , 네번째- 2 )

예를 들어보자. 첫번째 벡터 x 의 첫번째 원소 4 는 두번째 벡터 u 의 첫번째 원소와 같다. 따라서 match 함수 값은 1 이다. 마찬가지로 x 의 네번째 원소 3u 의 세번째 원소와 같다. 따라서 match 함수 값의 네번째 원소는 3 이다.

벡터 u 의 몇번째 원소와 같은지를 보는 것은 벡터 u 의 인덱스를 대응하는 것과 연관이 있다.

tabulate 함수

tabulate 함수는 입력한 벡터의 원소별 빈도를 출력해주는 함수다.

> match(x, u)
[1] 1 2 3 3 3 4 4 2 2 3 3 1 1 1 4
> tabulate(match(x, u))
[1] 4 3 5 3

함수의 입력값이 벡터 x = c(4, 1, 3, 3, …) 가 아닌 match(x, u) 이므로 출력값의 원소 4 3 5 3이 각각 1 2 3 4 의 개수를 나타내는 것임에 주의하자.

which.max 함수

which.max 함수는 입력된 벡터의 원소 중 최대값의 인덱스를 출력하는 함수다.

> tabulate(match(x, u))
[1] 4 3 5 3
> which.max(tabulate(match(x, u)))
[1] 3

함수의 입력값 c(4, 3, 5, 3) 의 원소 중 최대값은 5 이고 해당하는 인덱스는 3이므로 출력값으로 3 을 출력한다.

종합 및 해석

> x
[1] 4 1 3 3 3 2 2 1 1 3 3 4 4 4 2
> u = unique(x)
> u
[1] 4 1 3 2
> match(x, u)
[1] 1 2 3 3 3 4 4 2 2 3 3 1 1 1 4
> tabulate(match(x, u))
[1] 4 3 5 3
> which.max(tabulate(match(x, u)))
[1] 3
>u[which.max(tabulate(match(x, u)))]
[1] 3

tabulate(x) 를 하지 않은 이유는 tabulate 함수는 입력한 벡터의 원소 중에서 오름차순으로 빈도를 표시해주기 때문이다. tabulate(x) 를 실행하면 다음과 같다.

> x
[1] 4 1 3 3 3 2 2 1 1 3 3 4 4 4 2
> tabulate(x)
[1] 3 3 5 4

출력값의 3 3 5 4 는 각각 x의 원소 (1, 2, 3, 4)의 개수지, 벡터 x의 원소 순서인 (4, 1, 3, 2 )의 개수가 아니다.

tabulate 함수의 출력값인 각 원소의 빈도수를 u = unique(x) 의 원소 4 1 3 2 의 순서로 출력되게 하기 위해 중간에 match(x, u) 함수를 추가함으로써 (4, 1, 3, 2)를 각각 (1, 2, 3, 4)에 대응하여 변환하였다.

tabulate(match(x, u)) 의 출력값 4 3 5 3u의 값인 c(4, 1, 3, 2)의 빈도수이므로( 4 4개, 1 3개, 3 5개, 2 3개), which.max 함수로 4 3 5 3의 최대값의 인덱스를 구하고 다시 u 에 참조한 값( u[3] = 3)은 x 의 원소 중 가장 많은 빈도수의 원소인 최빈값을 나타낸다.

다시 한 번 최빈값을 구하는 사용자 정의 함수 코드를 적으며 포스트를 마무리한다.

> # 자료
> x
[1] 4 1 3 3 3 2 2 1 1 3 3 4 4 4 2
> # 최빈값 사용자 정의 함수 만들기
> getmode = function(t){
+ u = unique(t)
+ u[which.max(tabulate(match(t, u)))]
+ }
> # 최빈값 구하기
> getmode(x)
[1] 3

--

--

BeomHwan Roh
BeomHwan Roh

Written by BeomHwan Roh

Math, Statistics, Data Science

No responses yet