IDL/Programming

텍스트 파일을 효과적으로 읽는 방법에 관하여 [4]

이상우_IDL 2014. 5. 16. 22:48
728x90

* 3회차 내용에서 바로 이어집니다.

 

그러면 이번에는 이러한 값들을 모아서 별도의 자료값 배열에 넣는 작업을 시도해보고자 합니다. 즉, 각 컬럼마다의 값들을 배열의 형태로 얻겠다는 의미입니다. 예를 들면, 모든 줄에 대하여 첫번째 컬럼의 값들만 모아서 a라는 배열에 넣고, 두번째 컬럼의 값들만 모아서 b라는 배열에 넣고, ...., 다섯번째 컬럼의 값들만 모아서 e라는 배열에 넣겠다는 얘기죠. 결국 a, b, c, d, e 다섯 개의 배열들을 얻게 됩니다. 이런 식으로 각 컬럼별 값들을 배열에 저장하는 과정은 차후 자료값 분석을 위해서 반드시 필요한 과정입니다. 그러면 이런 작업을 어떤 방식으로 이루어져야 할까요? 일단 첫번째 컬럼의 값들만 모아 a라는 배열에 넣는 작업만 하는 것으로 가정할 경우라면, 다음과 같은 방식으로 프로그램을 작성하면 됩니다. 처음에 제시되었던 프로그램의 내용을 약간 수정한 것입니다.

 

PRO test_text

file = 'aaa.txt'

OPENR, lun, file, /GET_LUN; 채널 열기

ss = ''

READF, lun, ss; 헤더 한 줄 읽고 넘기기

a = []; 배열 a의 초기 선언

WHILE ~EOF(lun) DO BEGIN; WHILE문 시작

  READF, lun, ss; 한 줄의 내용을 통째로 읽어 ss라는 문자변수에 저장

  spl = STRSPLIT(ss, ',', /EXTRACT, /PRESERVE_NULL); ss를 분리자인 코마(,)를 단위로 분할하여 분할된 문자값들을 배열로 저장

  a = [a, FIX(spl[0])]; 정수형의 값으로 배열 a에 하나씩 붙여나감

ENDWHILE; WHILE문 종료

FREE_LUN, lun; 채널 닫기

HELP, a

PRINT, a

END

 

앞선 내용과 차이가 나는 부분을 붉은 색으로 표시하였는데요. 반복문이 시작되기에 앞서 먼저 a라는 배열을 그냥 초기 뼈대 형태로만 선언해놓는 과정이 수행됩니다. 여기서 []라고 표시된 것은, 배열이긴 하지만 아직 구성원이 명시되지 않은 상태를 의미합니다. 실제로 이 배열안에 구성요소들을 채워넣는 과정은 반복문안에서 붉은 색으로 표시된 부분에서 이루어집니다. 배열 a의 구성원소들을 매 반복회차마다 하나씩 붙여나가는 과정입니다. 여기서 유의할 부분은 그냥 spl[0]를 바로 붙여넣는 것이 아니라, FIX 함수를 사용하여 정수형으로 변환한 다음에 붙인다는 것입니다. 왜냐하면 spl[0] 자체는 문자값이기 때문입니다. 나중에 얻고자 하는 a가 어떤 자료형(Type)의 값들을 갖는 배열이 되어야 하느냐에 따라, FIX 함수를 사용하여 정수형으로 변환하기도 하고 또는 FLOAT 함수를 사용하여 실수형으로 변환하기도 합니다. 이 부분은 프로그래머가 결정해야 합니다.

 

어쨌든 이러한 방식으로 b, c, d, e 등의 배열들도 만들어낼 수 있다는 것이 아마 짐작이 가능할 겁니다. 그런데 e라는 배열, 즉 매 줄마다 다섯번째 컬럼에 있는 값들을 모아서 얻어진 배열의 값을 위와 같은 방식으로 얻어서 출력해보면 좀 이상한 것을 발견할 수 있습니다.

 

PRO test_text

file = 'aaa.txt'

OPENR, lun, file, /GET_LUN; 채널 열기

ss = ''

READF, lun, ss; 헤더 한 줄 읽고 넘기기

e = []; 배열 e의 초기 선언

WHILE ~EOF(lun) DO BEGIN; WHILE문 시작

  READF, lun, ss; 한 줄의 내용을 통째로 읽어 ss라는 문자변수에 저장

  spl = STRSPLIT(ss, ',', /EXTRACT, /PRESERVE_NULL); ss를 분리자인 코마(,)를 단위로 분할하여 분할된 문자값들을 배열로 저장

  e = [e, FIX(spl[4])]; 정수형의 값으로 배열 a에 하나씩 붙여나감

ENDWHILE; WHILE문 종료

FREE_LUN, lun; 채널 닫기

HELP, e

PRINT, e

END

 

위와 같은 방식으로 프로그램을 작성하여 배열 e의 값들이 출력된 결과를 보면 0, 0, 3, 0, 0이라는 값들로 나타납니다. 이것이 맞을까요? 아닙니다. 사실 여기서 0이라고 된 값들은 원래 파일상에서는 결측에 해당되던 값들이었습니다. 그러나 분할에 의하여 spl을 얻는 과정에서 이 부분의 값은 null이라는 문자값으로 얻어지는데 이런 null값을 그냥 바로 FIX 함수를 사용해서 정수형으로 변환하게 되면 0으로 얻어지게 되어 있습니다. 이런 현상은 에러는 아니지만 묵시적으로(implicitly) 벌어진 현상입니다. 이렇게 되면, 정말 값이 0인 경우와 결측(null)인 경우의 구분이 불가능해집니다. 따라서 spl로 분할된 문자의 내용이 null인 경우에는 결측값으로 간주하고 이에 해당되는 약속된 값(예를 들면, -999와 같은)으로 대체하도록 해야 합니다. 따라서 위의 프로그램의 내용은 아래와 같이 수정되어야 더 완전해집니다.

 

PRO test_text

file = 'aaa.txt'

OPENR, lun, file, /GET_LUN; 채널 열기

ss = ''

READF, lun, ss; 헤더 한 줄 읽고 넘기기

e = []; 배열 e의 초기 선언

WHILE ~EOF(lun) DO BEGIN; WHILE문 시작

  READF, lun, ss; 한 줄의 내용을 통째로 읽어 ss라는 문자변수에 저장

  spl = STRSPLIT(ss, ',', /EXTRACT, /PRESERVE_NULL); ss를 분리자인 코마(,)를 단위로 분할하여 분할된 문자값들을 배열로 저장

  IF spl[4] EQ '' THEN tmp = -999 ELSE tmp = FIX(spl[4]); spl[4]가 null이면 -999, 그렇지 않으면 그대로 정수로 변환

  e = [e, tmp]; 정수형의 값으로 배열 a에 하나씩 붙여나감

ENDWHILE; WHILE문 종료

FREE_LUN, lun; 채널 닫기

HELP, e

PRINT, e

END

 

이제는 결측에 해당되는 값들이 -999로 저장된 것을 확인할 수 있습니다. 그러면 이렇게 얻어진 배열에 대한 각종 연산을 나중에 하게 될 때, WHERE 등의 함수를 사용하여 결측에 해당되는 값들을 제외하는 것이 쉬워지게 됩니다. 따라서 이런 상태에서라면 배열에 대한 각종 연산 결과들이 정상적으로 얻어지게 될 것입니다.

 

작성을 하다보니 내용이 꽤 길어지게 되어서, 이번에 작성된 내용은 둘로 쪼개어서 3회차 및 4회차 게시물로 올립니다. 5회차도 있을거냐고 물으신다면, 그럴 수도 있고 아닐 수도 있습니다. 만약 있게 된다면 좀 더 고약한(?) 텍스트 자료를 공략하는 내용이 될 것 같습니다. 물론 저도 준비에 좀 시간이 걸릴 것 같습니다. 그 사이에 다른 내용들이 블로그상에 게시될 수도 있겠지만, 이 내용의 다음 회차도 사정이 되는대로 계속 이어나가도록 노력해보겠습니다.

LIST