2015년 4월 23일 목요일

[하루에 한 R] 매트릭스 형태의 텍스트 파일 입력과 헤더의 처리

최근 세균 유래의 RNA-seq 데이터 분석 처리를 의뢰받으면서 1년 넘게 손을 놓고 있었던 R 관련 자료를 뒤적이게 되었다. 세균 RNA-seq 데이터 처리에 특화된 Rockhopper라는 도구가 있어서 비교적 편하게 expression 수치와 fold change 값은 뽑아내었다. 그러나 의뢰자에게 제공할 QC 등과 관련한 시각화 자료를 제공하기에는 R만큼 좋은 것이 없다. 다시 기초 자료를 뒤적이면서 R에서 행렬(matrix) 형태의 자료를 입력하여 다루는 기본적인 사항을 블로그에 정리해 보기로 하였다.

우선 다음과 같이 간단한 자료 파일(data1.txt)이 있다고 가정하자. 설명은 전혀 없고 숫자와 이들을 구분하는 콤마로만 구성된 텍스트 파일이다.
$ cat data1.txt
255.6,243610,62262000
111,547030,63601002
233,357021,81799600
513,100210,51098531
200,120538,25051706
337,377873,127387000
R에서는 아무런 옵션 없이 다음과 같이 단순하게 읽어들여 보자.
> data = read.table("data1.txt",sep=",")
> data 
     V1     V2        V3
1 255.6 243610  62262000
2 111.0 547030  63601002
3 233.0 357021  81799600
4 513.0 100210  51098531
5 200.0 120538  25051706
6 337.0 377873 127387000
데이터의 row가 매우 길다면 head() 함수를 써서 데이터의 앞부분 일부를 볼 수 있다. 더욱 바람직하게는 View() 함수를 쓰면 별도의 창이 뜨면서 데이터가 표시된다. view()가 아니고 View()임에 유의하라. 아무튼 데이터를 표시해 보면 원본 파일에는 없었던 V1, V2, V3가 맨 위에 나온다. R에서는 이를 variable이라고 한다. 반면 맨 왼쪽에는 1, 2, 3...라는 일련번호가 붙어서 각 레코드(row)를 구분해주고 있다. View(data)를 실행해 보자.



이제 데이터 파일을 살짝 바꾸어서 맨 윗줄이 헤더역할을 하게 하였다. 즉 첫줄은 이제 각 컬럼이 수록한 값을 설명하는 표지가 되는 것이다.
$ cat data2.txt
Pop.density, Area, Population
UK,255.6,243610,62262000
France,111,547030,63601002
Germany,233,357021,81799600
Korea,513,100210,51098531
N.Korea,200,120538,25051706
Japan,337,377873,127387000
여기서 유의할 점은, 첫 줄(variable name 수록)의 요소는 3개이지만 두번째 줄부터는 4개의 요소가 담겨있다는 점이다. 즉 헤더 줄을 제외한 나머지 줄의 첫번째 요소는 row name이라는 암묵적인 선언인 것이다. 이를 R에서 읽어보자. 다음과 같이 variable(=column name)과 row name이 자동적으로 인식이 되었다. 이제 데이터 파일이 수록한 수치가 무엇인지를 알게 되었을 것이다. 우리나라를 포함한 5개 국가의 인구밀도, 면적, 그리고 총 인구수를 나태낸 것이다.
> data = read.table("data2.txt",sep=",")
> data
        Pop.density   Area Population
UK            255.6 243610   62262000
France        111.0 547030   63601002
Germany       233.0 357021   81799600
Korea         513.0 100210   51098531
N.Korea       200.0 120538   25051706
Japan         337.0 377873  127387000

위에서 빨강색 상자로 둘러친 부분(row.names)은 data[,1]를 입력하여 반환할 수 있는 일반적인 컬럼이 아니다.

만약 헤더라인의 첫번째 요소에 'Nation'이 들어가는 것이 좀 더 완벽하다고 생각한다면?

$ cat data3.txt
Nation,Pop.density, Area, Polulation
UK,255.6,243610,62262000
France,111,547030,63601002
Germany,233,357021,81799600
Korea,513,100210,51098531
N.Korea,200,120538,25051706
Japan,337,377873,127387000

이 파일을 R에서 읽어보자.

> data = read.table("data3.txt",sep=",")
> data
       V1          V2     V3          V4
1  Nation Pop.density   Area  Population
2      UK       255.6 243610    62262000
3  France         111 547030    63601002
4 Germany         233 357021    81799600
5   Korea         513 100210    51098531
6 N.Korea         200 120538    25051706
7   Japan         337 377873   127387000

그렇다. 데이터 파일의 모든 줄이 같은 수의 요소를 갖고 있다면, R은 모든 줄을 다 데이터 자체로 생각하는 것이다! 첫 줄의 요소 수가 나머지 줄보다 하나 적다면, 자동적으로 첫 줄을 헤더라인, 즉 variable 명을 담고 있는 것으로 인식한다. 그리고 더욱 중요한 것은, 두번째 줄부터 나타나는 첫번째 요소는 row name으로 인식한다는 것.

따라서 data3.txt파일처럼 모든 라인이 같은 수의 요소로 이루어졌다 하더라도 첫 줄이 헤더임을 명시하려면 다음과 같이 하면 된다.

> data = read.table("data3.txt",sep=",",header=TRUE)
> data
   Nation Pop.density   Area Population
1      UK       255.6 243610   62262000
2  France       111.0 547030   63601002
3 Germany       233.0 357021   81799600
4   Korea       513.0 100210   51098531
5 N.Korea       200.0 120538   25051706
6   Japan       337.0 377873  127387000

그러나 아직 완벽하지 않다. 나라 이름이 아직 row name으로 인식되지 않고 있기 때문이다. 첫번째 컬럼이 row name에 해당한다는 것을 알려주려면 다음과 같이 해야 한다.

> data = read.table("data3.txt",sep=",",header=TRUE, row.names=1)
> data
        Pop.density   Area Population
UK            255.6 243610   62262000
France        111.0 547030   63601002
Germany       233.0 357021   81799600
Korea         513.0 100210   51098531
N.Korea       200.0 120538   25051706
Japan         337.0 377873  127387000

row.name=2라고 쓰면 데이터 파일의 두번째 줄을  row name으로 인식한다.

헤더라인의 첫번째 요소를 'Nation'이라고 명시적으로 나타내는 것이 옳은가? 즉 Nation도 하나의 variable로 봐야 할 것인가? 이건 철학적인 문제라서 무엇이 정답이라고 할 수는 없다. 단, 첫 줄의 요소 수가 나머지 줄과 동일한가 혹은 그렇지 않은가(하나 적은가)에 따라서 R의 read.table() 기본 작동이 달라진다는 것, header=TRUE라고 선언하는 것은 오직 variable의 인식에만 관여할 뿐 row name과는 상관이 없다는 것을 기억하는 것이 중요하다.

row.names라는 파라미터를 모른다면 다음과 같이 추가적인 단계를 거치면 된다. 첫번째 컬럼을 row.names(data)에 할당하고, 그 컬럼을 없애면 된다.

> data = read.table("data3.txt",sep=",",header=TRUE)
> row.names(data) = data[,1]
> data = data[,-1]
> data
        Pop.density   Area Population
UK            255.6 243610   62262000
France        111.0 547030   63601002
Germany       233.0 357021   81799600
Korea         513.0 100210   51098531
N.Korea       200.0 120538   25051706
Japan         337.0 377873  127387000

이상의 사례에서 보았듯이 새로운 데이터(여러개의 variable로 구성)는 하나의 줄(row)로 추가되는 것이 자연스럽다. 그러나 생명과학에서 흔히 다루는 발현수치, 즉 microarray나 RNA-seq 자료는 어떠한가? 여기에서는 기본적으로 한번의 실험에서 얻어진 모든 유전자의 발현 수치가 하나의 컬럼으로 구성된다. 대신 row는 각 유전자에 해당한다. 따라서 매트릭스 형태의 발현 수치 데이터를 읽어들인 후 계층적 클러스터링을 하려면 t() 함수를 써서 트랜스포즈를 해야 하는 것이다. 즉 행과 열을 바뀌치기 하는 것이 필요하다.

매우 기본적인 사항이지만 평소에 눈여겨보지 않았던 것을 이렇게 정리하고 나니 머리속이 좀 맑아지는 기분이다...

2017년 12월 15일에 추가한 글: R 매뉴얼을 통해서 header 관련한 동작을 비로소 이해하였다. read.table() 함수에서는 기본적으로 header=F이지만 첫 줄의 데이터 필드가 나머지 줄보다 하나 적을 때 자동으로 T가 된다. 반면에 read.csv(), read.delim() 등 다른 계열의 함수에서는 기본적으로 header=T이다.



댓글 없음: