Skip to menu

Radar Chart의 구현

Radar chart 는 세개 이상의 정량적인 변수를 2차원으로 표현하는 그래프이다. 예를 들어 1974년 Motor trend US잡지에 실린 자동차 32종을 수동과 자동 변속기를 구분하여 다음과 같은 그래프를 그려보고자 한다. 

붓꽃(iris)의 세가지 종에 따른 꽃잎과 꽃받침의 길이, 넓이를 비교해 보면 다음과 같다. 

또 다른 예로 저자가 만든 moonBook 패키지에 있는 acs환자의 데이타를 남여로 구분해 보면 다음과 같다. 

그림에서 보는 바와 같이 여자환자의 발병연령이 높고 키와 몸무게는 남자가 크고 몸무게가 많이 나가지만 체질량지수나 혈중콜레스테롤 등은 동일한 것을 한 눈에 알 수 있다.

자료의 전처리

이와 같은 그래프를 그리려면 (1) 먼저 자료를 long form으로 정리해야 하며 (2) 각 군별로 자료의 평균을 구하고 (3) 이 자료를 이용하여 그림을 그려야 한다.

가상의 자료 만들기

먼저 한 고등학교에서 문과학생 100명과 이과학생 100명을 대상으로 영어, 수학, 역사, 과학 시험을 보았다고 하자. 편의상 문과 학생(B)의 영어, 역사 시험 성적이 이과학생에 비해 조금 높은 것으로 가정하고 이과학생(A)의 수학, 과학 성적이 문과 학생에 비해 조금 높은 것으로 가정하고 가상의 자료를 만들어 보았다.

English1=sample(50:90,100,replace=TRUE)
Math1=sample(seq(2.5,5,by=0.1),100,replace=TRUE)
History1=sample(60:80,100,replace=TRUE)
Science1=sample(seq(3,4.5,by=0.1),100,replace=TRUE)

English2=sample(55:95,100,replace=TRUE)
Math2=sample(seq(1.5,4.5,by=0.1),100,replace=TRUE)
History2=sample(70:95,100,replace=TRUE)
Science2=sample(seq(2.5,4,by=0.1),100,replace=TRUE)
Class=c(rep("A",100),rep("B",100))

English=c(English1,English2)
Math=c(Math1,Math2)
History=c(History1,History2)
Science=c(Science1,Science2)
data1=data.frame(English,Math,History,Science,Class)
head(data1)
  English Math History Science Class
1      68  4.6      64     4.0     A
2      52  2.9      61     4.5     A
3      53  3.2      64     3.7     A
4      87  3.6      80     4.2     A
5      73  4.6      79     3.1     A
6      51  3.2      63     4.1     A
summary(data1)
    English           Math          History         Science      Class  
 Min.   :50.00   Min.   :1.500   Min.   :60.00   Min.   :2.500   A:100  
 1st Qu.:62.00   1st Qu.:2.700   1st Qu.:70.00   1st Qu.:3.100   B:100  
 Median :72.00   Median :3.500   Median :74.50   Median :3.500          
 Mean   :72.55   Mean   :3.455   Mean   :76.03   Mean   :3.514          
 3rd Qu.:82.00   3rd Qu.:4.125   3rd Qu.:82.00   3rd Qu.:3.900          
 Max.   :95.00   Max.   :5.000   Max.   :95.00   Max.   :4.500          

자료가 잘 만들어져 있는지 A반과 B반의 성적 평균을 보려면 다음과 같이 한다.

aggregate(.~Class,data1,mean)
  Class English  Math History Science
1     A    70.5 3.837   69.97   3.763
2     B    74.6 3.073   82.09   3.266

넓은 형태를 긴 형태로

이 자료는 넓은 형태(wide form)의 자료이다. 이 자료를 이용하여 그림을 그리려면 먼저 자료를 긴 형태(long form)로 바꾸어 주어야 한다. reshape2 패키지의 melt() 함수를 써서 자료의 형태를 바꿀 수 있다.

longdf=melt(data1,id.vars="Class")
head(longdf)
  Class variable value
1     A  English    68
2     A  English    52
3     A  English    53
4     A  English    87
5     A  English    73
6     A  English    51

반별, 과목별 평균 구하기

이제 이 자료를 각 과목별로 평균을 내보자. plyr패키지의 ddply()함수를 이용하였다.

df=ddply(longdf,.(Class,variable),summarize,mean(value))
colnames(df)[length(df)]="value"
df
  Class variable  value
1     A  English 70.500
2     A     Math  3.837
3     A  History 69.970
4     A  Science  3.763
5     B  English 74.600
6     B     Math  3.073
7     B  History 82.090
8     B  Science  3.266

그래프 그리기

이 자료를 이용하여 점과 선그래프를 그리면 다음과 같다.

ggplot(data=df,aes(x=variable,y=value,group=Class,colour=Class))+
        geom_point()+geom_line()

geom_polygon을 이용하여 그림을 그리고 coord_polar()를 사용하여 원형그래프로 바꾼다.

ggplot(data=df,aes(x=variable,y=value,group=Class,colour=Class,fill=Class))+
        geom_point()+geom_polygon(alpha=0.4)+coord_polar()

여기서 문제가 발생한다. 영어와 역사는 100점 만점으로 채점이 되어 있고 수학과 과학은 5.0 만점으로 채점이 되어 있어 그래프르르 그리게 되면 수학, 과학은 영어, 역사에 비해 값이 적으므로 차이가 나지 않는 것 처럼 보인다. 이를 해결하기 위해 전체 자료를 rescale 해서 최저값 0, 최고값을 1로 바꾼후 그래프를 그리면 좋을 것이다. 이 용도로 scales패키지의 rescale()함수를 사용하면 된다. 다음의 rescaledf() 함수는 데이타 프레임 중 숫자형인 것만 골라 rescale해준다.

rescale_df=function(data,groupvar=NULL){
        if(is.null(groupvar)) df=data
        else df=data[,-which(names(data) %in% groupvar)]
        
        select=sapply(df,is.numeric)
        df[select]=lapply(df[select], scales::rescale)
        if(!is.null(groupvar)) {
                df=cbind(df,data[[groupvar]])
                colnames(df)[length(df)]=groupvar
        }        
        df
}

rescaled=rescale_df(data1)
head(rescaled)
     English      Math    History Science Class
1 0.40000000 0.8857143 0.11428571    0.75     A
2 0.04444444 0.4000000 0.02857143    1.00     A
3 0.06666667 0.4857143 0.11428571    0.60     A
4 0.82222222 0.6000000 0.57142857    0.85     A
5 0.51111111 0.8857143 0.54285714    0.30     A
6 0.02222222 0.4857143 0.08571429    0.80     A

이 데이타 프레임을 이용하여 긴 형태로 바꾸고 다시 각 반과 과목별로 평균을 냐보면 다음과 같다.

longdf2=melt(rescaled,id.vars="Class")
head(longdf2)
  Class variable      value
1     A  English 0.40000000
2     A  English 0.04444444
3     A  English 0.06666667
4     A  English 0.82222222
5     A  English 0.51111111
6     A  English 0.02222222
df2=ddply(longdf2,.(Class,variable),summarize,mean(value))
colnames(df2)[length(df2)]="value"
df2
  Class variable     value
1     A  English 0.4555556
2     A     Math 0.6677143
3     A  History 0.2848571
4     A  Science 0.6315000
5     B  English 0.5466667
6     B     Math 0.4494286
7     B  History 0.6311429
8     B  Science 0.3830000

geom_polygon을 이용하여 그림을 그리고 coord_polar()를 사용하여 원형그래프로 바꾼다.

ggplot(data=df2,aes(x=variable,y=value,group=Class,colour=Class,fill=Class))+
        geom_point()+geom_polygon(alpha=0.4)+coord_polar()

새로운 좌표계의 정의

Radial chart는 직선으로 그래프가 그려지나 geom_polygon()은 coord_polar()로 변형할 경우 원호로 그려진다. 원형좌표계에서 직선으로 polygon을 그리는 새로운 좌표계를 하나 만든다.

coord_radar <- function (theta = "x", start = 0, direction = 1) 
{
        theta <- match.arg(theta, c("x", "y"))
        r <- if (theta == "x") 
                "y"
        else "x"
        ggproto("CoordRadar", CoordPolar, theta = theta, r = r, start = start, 
                direction = sign(direction),
                is_linear = function(coord) TRUE)
}

이제 이 좌표계를 적용하여 그래프를 그린다.

ggplot(data=df2,aes(x=variable,y=value,group=Class,colour=Class,fill=Class))+
        geom_point()+geom_polygon(alpha=0.4)+coord_radar()

y축 범위의 재설정

y축의 범위가 0.3부터 0.6까지로 되어 있어 그래프가 왜곡되어 보인다. y축 범위를 0부터 1까지 설정한다.

ggplot(data=df2,aes(x=variable,y=value,group=Class,colour=Class,fill=Class))+
        geom_point()+geom_polygon(alpha=0.4)+coord_radar()+ylim(0,1)+
        theme(legend.position="bottom")+xlab("")+ylab("")

이 그래프를 보면 문과학생의 영어, 역사 점수가 높고 이과 학생의 수학 과학 점수가 높은 것을 한눈에 알수 있다.

ggRadar()함수의 구현

이상의 과정을 한꺼번에 해주는 함수를 만들어 사용하면 편리할 것이다. 저자가 만든 ggRadar()함수는 wide form의 데이타를 내부적으로 long form으로 바꾸어주고 group변수를 정해주는 경우 그룹별로 평균을 구해 그래프를 그려준다. autorescale인수를 TRUE로 주는 경우 rescale도 해준다.

ggRadar(data=mtcars,groupvar="am",autorescale = TRUE)

ggRadar(data=mtcars,groupvar="cyl",autorescale = TRUE)

다음과 같이 사용할 수도 있습니다.

 mtcars$model=rownames(mtcars)
 ggRadar(mtcars[1:9,],autorescale=TRUE,groupvar="model",legend.position="none")+
         ylim(0,1)+facet_wrap(~model)

ggRadar()함수의 소스는 web-R.org의 게시판에 공개되어 있습니다.