본문 바로가기
반치용/문제해결(trouble shooting)

[R]DsigDB 전처리

by Cat.8 2020. 1. 16.

오늘 제 목표는 R을 이용해

이런 데이터에서 compound(d1, d3)와 거기에 해당하는 gene을 추출해 DB화 하는 것입니다.   

Class compound gene
D1 Palbociclib CCND1
D1 Palbociclib CDK4
D3 chelidonine_HL60 CCL2
D3 chelidonine_HL60 CCL3
D3 chelidonine_HL60 TRIM14
... ... ...

(완료시 테이블. (+)- 나 up down 같은 내용도 제거)

DS.txt
5.13MB

이 쪽이 재료 파일입니다.

ds_df.R
0.00MB

그리고 전체 소스코드 입니다. 파일을 받으신 후 경로는 수정하는 것을 권해드립니다.

 

# Env setting
{
  library(data.table)
  library(stringr)
  options(stringsAsFactors = FALSE)
  options(digits = 10)
  options(scipen = 100)
}

사용할 패키지들과 자주 사용하는 옵션입니다. library 부분만 필수라고 생각하시면 됩니다.

 

# Loading
  {
    DS = fread("c:/channy/DS.txt",header=FALSE)
    
    # empty row exclude -> you can use na.omit()
    DS = subset(DS,V1 != "")
  }

사용할 파일을 불러옵니다. fread는 빠릅니다. 특히 ssd를 사용할 때 효과가 극대화 됩니다.
빈 칸이 있어서 제거도 해 주었습니다.

 

  # get compound's location
  main_names = grep(" : ", DS$V1)
  
  # set main dataframe
  temp_df = data.frame()
  
  # variable for iteration
  counter = 1

" : " 이 들어가 있는 줄이 compound가 있는 줄이므로, 이를 이용해서 DS 데이터프레임상의 위치를 추적합니다. 

나중에 내용들을 합칠 빈 데이터프레임을 생성해 둡니다.

다음 컴파운드의 위치를 알아내기 위해 counter 변수를 만들어 둡니다.
( 어디부터 어디까지인지는 compound 위치 사이에 있는 것들이 gene이기 때문에, n번째 compound와 n+1번째 compound의 위치를 이용해 gene들을 구분했습니다.)

Compound 1
gene1
gene2
gene3
...
compound 2
gene4
...

 

 

# iteration for every compound
  for (i in main_names){

네. 반복구문이 시작됩니다. 컴파운드들이 있는 번호 각각을 불러옵니다. 1, 4, 10번째 줄에 " : " 가 있으면, i가 1일 때, 4일 때, 10일 떄를 기준으로 반복문이 작동합니다.

 

# variables for sub iteration
    start = i
    if(counter != 27370){
      end = main_names[counter+1]-1 # next compound's location - 1 
    }else{
      end = length(DS$V1)
      
    }

start(compound 위치)와 end(해당 컴파운드의 마지막 진 위치)를 정하는 부분입니다.
제가 사용한 방법은 n번째와 n+1번째 compound 위치사이의 gene을 리스팅 하는 방법인데, 마지막 compound는 다음 compound가 없습니다. 고로 예외 처리를 해서, 마지막인 경우는 다음 순서를 생각하지 않고 끝까지 가는 것으로 해 두었습니다.

 

    temp_compound_name = strsplit(DS$V1[i]," : ")[[1]][2] # split by " : ". and pick second value
    class_d = strsplit(DS$V1[i]," : ")[[1]][1]
    class_d = str_extract(pattern = "(?<=\\().*?(?=\\))",class_d) # get sub_database's name. (d1, d3)

1. DS$V1[i] 는 i번째 comound가 있는 줄 내용입니다.
해당 줄을 " : " 를 기준으로 자르면 {Compound(D1) , compound명}으로 나뉩니다. 함수 리턴값이 조금 특이해서 [[1]][2]라고 해야 저 둘 중 뒷 값을 뽑아낼 수 있습니다.
 정리하자면, temp_compound_name은 현재 for문 내에 있는 compound 이름이 들어갑니다.

2. Compound(D1) 부분에서는 D1 부분을 뽑아내야 합니다. 그래서 일단 Compound(D1) 전체를 가져왔습니다.

3. str_extract는 특정한 패턴의 문자열을 뽑아냅니다. class_d 의 내용은 "Compound(D1)"이고,
(?<=\\().*?(?=\\))
이 요상한 부분은 정규표현식 이라는 것입니다. 문자열을 (상대적으로) 쉽고 빠르게 다룰 수 있는 방법이죠.
(?<=\\() 부분에서 (?<=  A )는 뽑아내려는 내용 앞에 A 가 있는 걸 찾아라. 라는 뜻입니다.
(?=\\)) 부분은 뒤에 "\\)"가 있는 걸 찾으라는 뜻이죠. 앞에 \\가 붙는 것은 문법상 R이 헷갈리지 않기 위해 표시를 해준 것입니다.
.*? 에서 "."은 줄바꿈을 제외한 아무거나, "*"은 몇 번이 반복되어도 된다(0포함), "?"는 가능한 적은 글자를 찾으라는 뜻입니다.
(아)이)스) 에서 같은 일을 실행할 때, "?"가 없이 실행하면 "아)이)스" 를 출력합니다. ?가 있으면 처음 만난")"에서 끝내서 "아"만 출력합니다.

 

    # dataframe for each compound
    temp_line = data.frame()
    
    # each compound's iteration
    # I reccomend to use 'papply'
    for (j in seq(start+1,end)){ # +1 means extract compound name.

temp_line 에는 개별 compound에 대한 각 gene들이 리스팅 됩니다.
j는 start+1(컴파운드 다음 줄, 첫 gene), 부터 end(다음 컴파운드 직전 gene/혹은 마지막 gene)까지 각각 들어갑니다.
참고로 R 사용에 익숙하신 분이라면 papply 함수를 이용하시는 것을 권해드립니다. 멀티스레딩을 통해 속도가 향상될 수 있습니다.

 

      # add compound name & gene ... to temp_line
      temp_line = rbind(temp_line,c(class_d,temp_compound_name,DS$V1[j]))
      colnames(temp_line) = c("class","compound","gene") # prevent error
  }

temp_line에 하나의 compound에 속한 gene들을 전부 리스팅 합니다.
열이름을 정해주는 것은 rbind를 할 시 열 이름이 다르면 오류가 나면서 정상적으로 합쳐지지 않기 때문에 매 번 지정해 줍니다.

 

    # add each compound's gene name ... to temp_df
    temp_df = rbind(temp_df,temp_line)
    
    #print(temp_compound_name) 
    print(paste0("progress... ", counter , " / ", length(main_names), " = " , counter/length(main_names)*100 , "%"

temp_df는 각 컴파운드별로 정리된 gene들 리스트를 다시 한 번 모아서 합칩니다.
print되는 부분은 실행하고 진행이 얼마나 되는지 중간중간 확인하기 위한 코드입니다.

 

    ))
    counter = counter + 1 # next variable
  }
}

반복을 끝내고, counter를 1 증가시켜서 다음 compound를 타겟으로 삼을 수 있게 해 줍니다.

 

# After shave
{
  final_table <- temp_df
  
  # upper character
  final_table$compound <- toupper(final_table$compound)
  # replace _down & _UP & (+)-
  final_table$compound <- gsub("_DOWN","",final_table$compound)
  final_table$compound <- gsub("_UP","",final_table$compound)
  final_table$compound <- gsub("(+)-","",final_table$compound)
  # get my data
  real_final_talbe = subset(final_table, class == "D1" | class == "D3")
}

테이블이 정리되고 난 뒤, 추가요청이 있어 내용을 추가했습니다.

toupper는 대문자로 바꾸는 역할입니다. compound를 대문자로 바꾸었습니다.

gsub는 gsub(A,B,C)일 떄,
C에서 A를 찾아서 B로 바꾸는 함수입니다. _UP과 _DOWN 그리고 (+)- 같은 필요없는 내용은 다 제거했습니다.

subset을 이용해 D1과 D3만 걸러냈습니다.

결과물!

 

댓글