지난번에 작성해서 올렸던 "텍스트 파일을 효과적으로 읽는 방법에 대하여"라는 게시물의 내용을 이번 2회에서 계속 이어서 다루고자 합니다. 지난 회 내용에서 다음과 같은 내용을 담은 aaa.txt라는 파일을 예제 파일로 삼아서 얘기를 했었습니다.
a b c d e
1 1 1 1
2 2 2
3 3 3 3 3
4 4 4
5 5 5 5
그런데 사실 이런 파일의 경우 내용상으로 좀 진지하게 짚고 넘어가야 할 부분이 있습니다. 원칙적으로는 각 줄(Line)마다 a, b, c, d, e 다섯 개의 컬럼에 해당되는 데이터 값들을 갖고 있는 것으로 보입니다. 그런데 예를 들어서 세번째 줄에 있는 2라는 값 세 개만 있는 줄의 경우를 보면, 이 내용의 의미가 정확히 어떤 것인지가 모호합니다. 물론 그 줄의 세 값들이 a, b, c에 해당되고, 나머지 d, e에 해당되는 값들은 존재하지 않는 소위 결측(missing)값들일 가능성이 높습니다. 하지만 과연 이것이 a, b, c는 있고 d, e가 없다는 의미인지 아니면 예를 들면 a, c, d는 있고 b, e가 없다는 의미인지가 솔직히 좀 모호합니다. 물론 이런 자료 파일을 만든 원작자측에서 어차피 처음부터 순서대로 따지면 된다라는 단서를 준다면 이러한 모호함은 사라질 수 있습니다. 하지만, 혹시라도 그런 부연설명이 없는 상태에서 받은 자료 파일이라면, 결측값이 과연 어느 컬럼에 해당되는가를 명확히 판단할 수 있을까요? 저는 그렇지 않다고 생각합니다. 그리고 그렇게 컬럼 대응 관계가 모호한 상황이라면 이런 자료 파일은 아예 제대로 활용하기도 어려워질 가능성이 높습니다.
사실 이러한 자료 파일들의 경우, 특히 결측값이 종종 나타날 수 있는 그런 경우라면 이런 결측값들에 대한 명확한 표시가 필요합니다. 예를 들어 이 자료 파일의 내용이 아래와 같았다면, 결측값을 파악하기가 훨씬 쉬웠을 것입니다.
a b c d e
1 1 1 1 -999
2 2 2 -999 -999
3 3 3 3 3
4 4 4 -999 -999
5 5 5 5 -999
위와 같은 경우라면 각 줄마다 무조건 다섯 컬럼씩의 값들이 존재합니다. 그 중에서 -999로 표시된 경우는 결측값에 해당된다라는 약속이 명시되면 더 이상 모호할 부분이 전혀 없습니다. 아주 명확하게 자료값들을 읽을 수 있습니다. 특히나 컬럼 갯수가 각 줄마다 일정하기 때문에, 읽는 입장에서도 프로그램을 작성하기가 매우 수월해집니다. 이런 파일이라면 READCOL로 바로 해결이 되죠. 자료 파일의 내용이 아래와 같은 경우라도 괜찮습니다.
a b c d e
1, 1, 1, 1,
2, 2, 2, ,
3, 3, 3, 3, 3
4, 4, 4, ,
5, 5, 5, 5,
위의 내용에서 실제 자료값들이 있는 각 줄을 보면 다섯 컬럼의 값들이 코마(,)라는 분리자(Delimiter)로 나누어져 있습니다. 물론 여기서는 결측에 해당되는 값이 그냥 코마와 코마 사이의 공백으로 되어 있기는 합니다만, 이렇게 분리자라도 있으면 값의 내용이 공백이라 하더라도 그 내용을 읽는 것은 그리 어렵지 않습니다. 물론 READCOL을 사용해서 해결이 가능한데, 다만 주의해야 할 부분들이 좀 있습니다. 예를 들어, 파일 aaa.txt가 바로 위의 내용처럼 되어 있고 READCOL로 이 파일의 내용을 읽는다고 한다면 다음과 같은 방법을 생각해볼 수 있습니다.
READCOL, 'aaa.txt', a, b, c, d, e, DELIMITER=','
하지만 결론을 먼저 얘기하면, 이렇게만 하면 절대 안됩니다. 파일의 내용에서 분리자인 코마(,) 사이에도 빈 공백이 있는 경우들이 분명히 있고, 이러한 값들을 결측값으로서 인식을 해야만 하기 때문입니다. 따라서 위의 READCOL은 일단 아래와 같은 내용으로 바뀌어야 합니다.
READCOL, 'aaa.txt', a, b, c, d, e, DELIMITER=',', /PRESERVE_NULL
여기서 사용된 PRESERVE_NULL이라는 키워드의 역할은, 분리자 사이에 공백이나 null만 존재할 경우 즉 실제값이 분리자 사이에 존재하지 않는 경우에도 이 분리자 사이의 값을 일단 하나의 값으로 읽으란 의미입니다. 이 키워드를 사용하지 않으면 READCOL은 분리자 사이에 실제값이 없을 경우 아예 읽지도 않습니다. 즉 분리자 사이에 뭔가 있었다는 흔적 자체도 전혀 남지 않게 된다는 얘기입니다. 그러면 결측값을 파악하고자 하는 우리의 의도는 무용지물이 되는 것입니다. 물론 분리자 사이의 결측값 부분이 -999와 같은 값으로 뭔가 실체가 항상 있는 경우라면 PRESERVE_NULL 키워드를 굳이 사용할 필요는 없습니다. 하지만 지금의 예에서처럼 결측일 경우 분리자 사이에 아무것도 없는 경우라면 이 키워드는 반드시 필요합니다. 어쨌든 이러한 키워드를 사용해서 읽으면 a, b, c, d, e는 각 컬럼별로 다섯 개의 원소값들을 갖는 배열이 됩니다. 실제 자료값들이 존재하는 줄 수가 다섯 줄이니까요.
그런데 좀 귀찮을 수 있겠지만, 여기서도 주의해야 할 부분이 또 있습니다. 일단 위와 같은 READCOL 명령으로 파일을 읽은 후, 배열 a와 d의 값을 PRINT 명령으로 출력해봅시다. 그러면 아래와 같이 나올겁니다.
A FLOAT = Array[5]
1.00000 2.00000 3.00000 4.00000 5.00000
D FLOAT = Array[5]
1.00000 0.00000 3.00000 0.00000 5.00000
보기에는 잘 읽은 것 같은데 과연 뭐가 문제일까요? 네번째 컬럼의 값들을 담게 되는 d라는 배열의 값들을 보면 0.0이란 값이 두번 나옵니다. 이 값들이 실제 의미있는 값들일까요? 아닙니다. 사실 다섯 개의 줄(Line)들 중에서 이 네번째 컬럼에 의미있는 값들이 있는 경우는 세번 뿐입니다. 나머지 두번은 분리자 사이에 아무 값도 없었습니다. 하지만 이렇게 아무 값도 없는데도 불구하고, READCOL이 각 컬럼을 실수형 배열로 가정하고 작업을 진행하다보니 없는 값은 그냥 0으로 처리해버린 경우입니다. 이런 일은 바람직하지 않겠죠. 이렇게 되묜 실제 자료값이 0이 있는 경우와 혼동이 될 수 밖에 없습니다. 좀 더 명확한 처리를 위해서는 다음과 같이 READCOL의 NAN이라는 키워드를 함께 사용하는 것이 바람직합니다.
READCOL, 'aaa.txt', a, b, c, d, e, DELIMITER=',', /PRESERVE_NULL, /NAN
이렇게 작업을 하면 그 결과는 다음과 같이 출력됩니다.
A FLOAT = Array[5]
1.00000 2.00000 3.00000 4.00000 5.00000
D FLOAT = Array[5]
1.00000 NaN 3.00000 NaN 5.00000
잘 보면 값이 없는 부분에 해당되는 값들이 NaN(Not a Number)로 인식이 되어 있는 것을 확인할 수 있습니다. 그러면 값이 없는 부분들은 이렇게 NaN이라고 명시가 된 상태이기 때문에 결측값을 파악하기가 더 편해집니다. 물론 NaN을 포함하는 배열에 대한 연산을 하게 되면, 이런 NaN값은 제외하고 계산을 수행해야 하겠지요. 이렇게 NaN을 제외한 연산 기능은 IDL 내장함수가 포함하는 경우도 있고, 인위적으로 NaN을 제외한 다음에 연산을 해야 할 수도 있습니다.
오늘은 여러가지 경우에 대하여 자료값들을 읽는 방법들을 주로 READCOL 위주로 설명을 드렸는데요. 사실 위에서 등장했던 PRESERVE_NULL같은 키워드는 원래는 지난 회에 소개했던 STRSPLIT라는 함수에 포함된 키워드입니다. 원래 READCOL의 소스코드를 보면 내부적으로 STRSPLIT 함수가 들어가 있습니다. 물론 NaN 값의 처리 부분은 별도의 기능들을 사용하긴 했지만, 어쨌든 READCOL의 내부를 보면 STRSPLIT를 비롯해서 정말 다양한 문자처리용 내장함수들이 활용되고 있음을 확인할 수 있습니다. 따라서 위에서 읽었던 예제 텍스트 파일의 내용도 결국은 지난 회 게시물에서 소개했던 READF, STRSPLIT 등의 기능들을 사용해도 얼마든지 읽을 수 있습니다. 그래서 다음 회차 게시물에서는 이와 관련된 내용을 소개해보도록 하겠습니다.
'IDL > Programming' 카테고리의 다른 글
텍스트 파일을 효과적으로 읽는 방법에 관하여 [3] (0) | 2014.05.16 |
---|---|
바이트 순서, Little Endian, Big Endian (0) | 2014.05.15 |
텍스트 파일을 효과적으로 읽는 방법에 관하여 [1] (0) | 2014.05.07 |
배열내 마지막 원소값을 조회하는 요령 (0) | 2014.04.02 |
날짜와 관련된 문제 해결 사례를 소개합니다 (예고) (0) | 2014.02.24 |