2017년 12월 14일 목요일

[하루에 한 R] 매트릭스에서 특정 값을 갖는 row 추출하기

[작성 후 추가한 글] 제목이 잘못되었다. 엄밀히 말해서 매트릭스가 아니라 데이터프레임이다.

해들리 위컴(Hardley Wickham)은 뉴질랜드의 통계학자 및 데이터 사이언티스트이자 저명한 R 개발자이다. RStudio의 Chief Scientist이기도 한 그는 ggplot2(), dplyr 등 데이터 처리와 시각화에 탁월한 R 패키지를 다수 개발하였고 활발한 저술과 강연활동도 펼치고 있다. 그가 개발한 패키지들은 GitHub 사이트에서 볼 수 있다. Quora에는 이런 질문도 있었다. '어떻게 해들리 위컴은 패키지 개발을 통해 그렇게 R에 많이 기여할 수 있었나요?'

이 질문에 대해서 위컴 자신이 답을 달았다. 다른 사람이 쓴 답이 무슨 소용이 있으랴.

  • 나는 많이 씁니다. 침대에서 일어나자마자 매일 아침 60-90분 정도를 씁니다(코드? 아티클?)
  • 나는 많이 읽습니다. 300개 정도의 블로그를 팔로우하고, 트위터와 스택 오버플로우에서 R이라는 태그가 달린 글을 계속 봅니다. 물론 모든 글을 깊이있게 읽지는 못하고 훑어보는 수준이지만 나에게 큰 도움이 됩니다.
  • Chunking(덩이 짓기?). '컨텍스트 스위칭'은 비용이 많이 듭니다(여기에서 context switching이란 소프트웨어 개발에 관한 용어가 아니라 어떤 일을 하다가 다른 일로 전환하는 것을 의미하는 것으로 보인다). 동시에 여러 패키지를 작업하면 아무것도 되지 않습니다. 새로운 기능에 대한 문제점과 아이디어를 꾸준히 축적하고 있다가 임계 질량에 다다르면 이 패키지에 며칠을 보내는 식으로 일을 합니다.
한마디로 말해서 참 멋진 사람이다. dplyr 패키지에 대한 국문 소개는 양우성님의 웹사이트를 참조하면 좋다. 내가 여기까지 흘러들어오게 된 것은 인하대 유동현 교수님 덕분이다.

오늘은 데이터프레임에서 특정 조건에 맞는 행을 뽑아오는 방법에 대해 공부해 보려고 한다. 숫자를 가지고 조건에 맞는 행을 뽑는 것이 아니라, 캐릭터 값을 가지고 일을 하려는 것이다. 몇 천 유전자에 대한 blast 결과를 테이블로 출력하여 R로 읽어들여 데이터프레임으로 만든 다음 내가 관심을 갖고 있는 몇십, 혹은 몇백개의 유전자에 해당하는 hit row를 쏙 빼내려는 것이다. Genes of interest는 별도의 벡터에 저장된 상태라고 가정하자. Perl에서는 hash를 사용하여 if (exists $seen{$item}) {...}으로 늘 하던 일이다. 여담이지만 내가 Perl애소 이 기법을 처음 접한 것은 phred/phrap/consed 패키지의 phredPhrap 스크립트를 뜯어보면서였다.

dplyr 패키지를 쓰면 오늘 논하는 것을 포함하여 더욱 정교한 작업을 할 수 있을 것이다. 그러나 이 글을 작성하는 목적은 R base package의 기본 기능을 최대한 활용해 보자는 것이다. 핵심이 되는 것은 which() 함수와 %in% 연산자이다. 이 특수 연산자에 대한 도움말을 띄워 보자. 함수에 대한 도움말을 찾을 때만 help() 또는 ? 명령어를 쓰라는 법은 없다.
> ?"%in%"   # 또는 help("%in%")
a %in% b라고 입력하면 a 벡터의 모든 원소에 대해서 이 값이 b 벡터에도 존재하는지를 TRUE or FALSE로 출력하는 것이다. 실제 사례를 보자.
> a = c("A","B","C","D","E")
> b = c("C","D","E","F","G")
> a %in% b
[1] FALSE FALSE  TRUE  TRUE  TRUE
which() 함수를 이용하면 a 벡터의 원소 중 b에도 있는 것, 그리고 a에만 있는 것을 다음과 같이 출력할 수 있다.
> a[which(a %in% b)]
[1] "C" "D" "E"
> a[-which(a %in% b)]
[1] "A" "B"
벡터에 대하여 방법을 알아보았으니 어제 글에서 활용한 샘플, 즉 blast 결과 파일을 파싱한 데이터프레임에 대해서 이 일을 해 보자. 'd'라고 명명한 데이터프레임의 구조는 다음과 같다. head() 함수를 이용하여 앞부분만 발췌하였다.


추출할 유전자는 벡터 e(for extract)에 넣어두었다. 몇 개 되지 않으면 손으로 입력해도 되지만, 그 수가 많다면 파일로부터 읽어들이는 것이 현명할 것이다.
> e
[1] "BfmR" "CsuB" "PgaB" "EntA" "PlcD"
d$V1 컬럼의 값 중 e 벡터에 존재하는 것을 찾아서 그 row를 추출하면 된다. 코드는 다음의 한 줄이다.
> d[which(d$V1 %in% e), ]
실제 실행 화면을 보자.


너무나 간단해서 내가 다 미안할 지경이다. 다음에는 데이터프레임의 row 혹은 column을 그룹 단위로 조작하는 방법에 대해서 공부해 보련다.

댓글 없음: