혈통 파일 트레이스(trace) 하기

 

혈통 파일이 주어 졌을 때 유전능력 평가에 모든 혈통을 사용하는 것은 아니다. 그 중에서 능력을 가지고 있는 개체의 조상만을 유전평가에 사용한다. 혈통 파일 트레이스라는 것은전체 혈통에서 주어진 개체의 조상만을 뽑아오는 것을 말한다.

 

여기서 중요한 것은 수십만에서 수천만의 전체 혈통 자료를 검색해서 아비와 어미를 찾는 일이다. 여기서는 이진 검색(binary search) 방법을 이용하여 구현하였다. 해쉬를 이용할 수도 있겠으나 pointer, linked list, type, array of pointer 등 이해해야할 것이 많아이해하기 쉬운 이진 검색을 이용하였다. 이진 검색의 가장 큰 단점은 자료 파일이 미리 정렬되어 있어야 한다는 점이다. 그러나 유닉스 소트(sort) 명령을 이용하면 되므로 큰 걱정은 없을 것으로 보인다. 또한 숫자가 아니라 문자열에 대하여 정렬하고, 이진 검색을 실시하였다. 개체 ID라는 것이 항상 숫자로 되어 있다고 보기 힘들기 때문이다.

 

다음은 프로그램 소스

 

!===============================================================================
! 혈통 트레이스 하기
! - 특정 개체의 조상 찾기 프로그램
! 프로그래머 : 박병호
! 사용 로직 : 이진 검색 이용
! 날짜 : 2012.1.21.
!===============================================================================
PROGRAM trace1 IMPLICIT NONE

INTEGER, PARAMETER :: TEMP_SIZE = 1024
CHARACTER(16), ALLOCATABLE :: pedi(:,:), key(:)
CHARACTER(16), DIMENSION(TEMP_SIZE) :: temp
INTEGER :: no_of_pedi, no_of_key
INTEGER :: i, j, insert
INTEGER :: position
INTEGER :: status
CHARACTER(LEN = 256), PARAMETER :: pedi_filename = 'trace_pedi.dat' ! pedigree file name
CHARACTER(LEN = 256), PARAMETER :: key_filename = 'trace_key.dat' ! key file name
CHARACTER(LEN = 256), PARAMETER :: out_filename = 'trace_out.dat' ! output file name
CHARACTER(LEN = 40) :: error_msg ! error message

WRITE(*,*) '혈통 파일 : trace_pedi.dat'
WRITE(*,*) '키 파일 : trace_key.dat'
WRITE(*,*) '출력 파일 : trace_out.dat'
WRITE(*,*) ''

no_of_pedi = 0
no_of_key = 0

! 자료 파일 열기
OPEN(UNIT = 20, FILE = pedi_filename, STATUS = 'OLD', ACTION = 'READ', IOSTAT = status, IOMSG = error_msg)

IF (status /= 0) THEN ! file open failed
WRITE (*,'(1X, A, A)') 'data file open failed -- error message : ', error_msg
STOP
END IF
! key 파일 열기
OPEN(UNIT = 30, FILE = key_filename, STATUS = 'OLD', ACTION = 'READ', IOSTAT = status, IOMSG = error_msg)

IF (status /= 0) THEN ! file open failed
WRITE (*,'(1X, A, A)') 'data file open failed -- error message : ', error_msg
STOP
END IF
! output 파일 열기
OPEN(UNIT = 40, FILE = out_filename, STATUS = 'REPLACE', ACTION = 'WRITE', IOSTAT = status, IOMSG = error_msg)

IF (status /= 0) THEN ! file open failed
WRITE (*,'(1X, A, A)') 'data file open failed -- error message : ', error_msg
STOP
END IF
! pedi 파일 자료 개수 알아내기
DO
READ(UNIT = 20, FMT = *, IOSTAT = status)
IF (status /= 0) EXIT ! reach end of file
no_of_pedi = no_of_pedi + 1
END DO
! 배열 할당
ALLOCATE(pedi(no_of_pedi,4))
! 혈통 자료를 다시 읽어, 배열에 저장
REWIND(20)
DO i = 1, no_of_pedi
READ(UNIT = 20, FMT = *, IOSTAT = status) pedi(i,1), pedi(i,2), pedi(i,3)
IF (status /= 0) EXIT ! reach end of file
IF (i > 2 .AND. pedi(i-1,1) >= pedi(i,1)) THEN
WRITE(*,*) '혈통 파일은 문자열 정열이 되어 있어야 합니다. : ', pedi(i-1,1), pedi(i,1)
STOP
END IF
pedi(i,4) = "0"
END DO
CLOSE(20) ! key 파일 자료 개수 알아내기
DO
READ(UNIT = 30, FMT = *, IOSTAT = status)
IF (status /= 0) EXIT ! reach end of file
no_of_key = no_of_key + 1
END DO
! 배열 할당
ALLOCATE(key(no_of_key))
! key 파일을 다시 읽어, 배열에 저장
REWIND(30)
DO i = 1, no_of_key
READ(UNIT = 30, FMT = *, IOSTAT = status) key(i)
IF (status /= 0) EXIT ! reach end of file
END DO
CLOSE(30) ! key 파일의 개체를 하나씩 처리
DO i = 1, no_of_key

temp = '0'
temp(1) = key(i)
j = 1
insert = 2

! temp 배열을 하나씩 읽으면서 처리

DO WHILE (j <= (insert - 1))
position = binary_search(pedi(:,1), temp(j))

! pedi 배열에서 찾았을 때
IF (position /= 0 .AND. pedi(position,4) /= "1") THEN

! 찾았다는 표시를 하자.
pedi(position,4) = "1"

! sire가 있을 때
IF (pedi(position, 2) /= '0') THEN
temp(insert) = pedi(position,2)
insert = insert + 1
IF (insert > TEMP_SIZE) THEN
WRITE(*,*) 'Fatal Error! Increase the size of temp array.'
STOP
END IF
END IF

! dam이 있을 때
IF (pedi(position, 3) /= '0') THEN
temp(insert) = pedi(position, 3)
insert = insert + 1
IF (insert > TEMP_SIZE) THEN
WRITE(*,*) 'Fatal Error! Increase the size of temp array.'
STOP
END IF
END IF
END IF

j = j + 1
END DO
END DO
! 네째 값이 1인 자료만 출력
DO i = 1, no_of_pedi
IF (pedi(i,4) == "1") WRITE(40,*) pedi(i,1), pedi(i,2), pedi(i,3)
END DO
CLOSE(40) CONTAINS

FUNCTION binary_search(a, value)
INTEGER :: binary_search
CHARACTER(16), INTENT(in) :: a(:), value
INTEGER :: low, high, mid

low = 1
high = size(a)
binary_search = 0

DO WHILE (low <= high)
mid = (low + high) / 2
IF (a(mid) > value) THEN
high = mid - 1
ELSE IF (a(mid) < value) THEN
low = mid + 1
ELSE
binary_search = mid
EXIT
END IF
END DO
END FUNCTION binary_searchEND PROGRAM trace1

 

위 소스를 trace.f95로 저장

 

다음은 혈통 파일을 정렬하고, 프로그램을 실행하는 화면

 

 

 

 

 

관련 파일

1327497692_trace.zip
다운로드

 

 

+ Recent posts