2017년 5월 24일 수요일

[R] prop.table()을 이용한 비율 계산

지극히 초보적인 수준의 R 코드를 가지고 잠시 혼란에 빠졌다가 해결책은 찾은 이야기를 기록으로 남기고자 한다. 다음 그림의 (A)와 같은 가상 데이터를 조작하여 열(B) 혹은 행(C) 합계에 대한 proportion으로 나타내는 것이 원래의 작업 목표였다. 각각의 계산에서 분모로 들어갈 수치들은 표 (A)의 노랑색(column 합) 혹은 파랑색(row 합) 마진에 표시되어 있다. 실제 데이터는 shotgun sequencing 방식으로 얻은 metagenome read의 taxonomic composition이다. 참고한 사이트는 Making heatmaps with R for microbiome analysis이다. 이 예제에서는 각 샘플의 데이터를 row로 나타나는 반면 나는 column으로 표현하고자 하였다. Microarray data analysis 사례에서 흔히 나타나듯 서로 다른 샘플을 컬럼으로 나타내는 것이 매우 일반적이다.


먼저 데이터 매트릭스를 만들어 보겠다.
> x = cbind(c(2,5,3), c(9,7,4), c(4, 3, 13))
> colnames(x) = c("A", "B", "C")
> rownames(x) = c("John", "Tom", "Bart")
> x
     A B  C
John 2 9  4
Tom  5 7  3
Bart 3 4 13
내가 원하는 proportion 수치는 각 셀의 값을 컬럼 합으로 나누는 것이다. 따라서 위의 표 (B)의 결과를 얻는 것이 목표였다. 예제 사이트에서는 샘플이 서로 다른 row로 배열되어 있으므로 row 합으로 나누는 것을 기준으로 설명하였다. 여기에서 사용된 명령은 data.prop <- all.data="" colsums="" div="" rowsum="" rowsums="">
> x.prop = x/colSums(x)
> x.prop
        A    B    C
John 0.20 0.90 0.40
Tom  0.25 0.35 0.15
Bart 0.15 0.20 0.65
> x.prop.2 = x/rowSums(x)
> x.prop.2
             A         B         C
Jone 0.1333333 0.6000000 0.2666667
Tom  0.3333333 0.4666667 0.2000000
Bart 0.1500000 0.2000000 0.6500000
전혀 의도하지 않은 결과가 나온다. 처음에는 예제가 잘못 짜여진 것이 아닌가 의심을 했었다. 일단은 인터넷 검색을 통해 prop.table() 함수가 (B) 및 (C) 모두의 경우에 쓰일 수 있음을 알았다. margin=2로 설정하면 column 합에 대한 비율을, margin=1로 두면 row 합에 대한 비율이 나온다.
> x.prop = prop.table(x, margin=2)
> x.prop
       A    B    C
John 0.2 0.45 0.20
Tom  0.5 0.35 0.15
Bart 0.3 0.20 0.65
> x.prop = prop.table(x, margin=1)
> x.prop
             A         B         C
John 0.1333333 0.6000000 0.2666667
Tom  0.3333333 0.4666667 0.2000000
Bart 0.1500000 0.2000000 0.6500000
물론 이보다 훨씬 미련한 방법을 이용하여 계산하는 것이 가능하다. 그러나 prop.table()이 가장 완벽한 해답을 제공한다. 그러면 왜 x/colSums(x)가 원하는 결과를 내지 못했는지를 알아보자. 매트릭스에 대한 연산을 왜 조심스럽게 해야 하는지도 이번 논의를 통해 알 수 있을 것이다.

매트릭스의 모든 셀에 같은 값을 더하거나 빼는 것은 대단히 쉽다.  그러나 row 혹은 column 단위로 계산을 할 때에는 매우 조심해야 한다. 원래 목표했던 것은 (10, 20, 20)을 각 컬럼의 값에 대해 나누는 것이었다. 즉 첫번째 컬럼A/10, 컬럼B/20, 컬럼C/20이 계산되기를 바랐던 것이다. 그러나 매트릭스를 벡터로 나누면 R은 그렇게 행동하지 않는다. John-A는 10으로 나누고,  Tom-A는 20으로 나누고, Bart-A는 20으로 나눈다. 그 다음으로는 컬럼 B로 넘어가서 각 셀에 대해서 같은 값이 아닌 (10, 20, 20)을 분모로 할당하여 나누고 또 컬럼 C로 넘어가는 것이다.  다시 말하자면 A, B, C 컬럼의 값을 일렬로 세운 다음 (10, 20, 20), (10, 20, 20), (10, 20, 20)..의 값을 순차적으로 대입하여 나눗셈을 한 것이다. rowSums() 함수가 원하는 값이 나왔던 것은 값을 대입하는 순서와 잘 맞아떨어졌기에 가능했던 것이다. 기억을 돌이켜보니 예전에 수강했던 R 강좌에서 이러한 내용을 다루었던 것 같다. 병합된 table은 low frequency row를 제거한 뒤 hclust2로 heatmap을 그리면 된다.

비록 시간이 많이 걸리더라도 실수를 통해 배우는 것이 가장 확실한 방법이 된다. 왜냐하면 같은 실수를 다시 반복하지 않을테니까 말이다.

댓글 없음: