오늘은 CSV 포맷의 파일을 읽는 방법으로서 IDL의 READ_CSV라는 기능을 소개해보고자 합니다. 아실 분들은 이미 다 아시겠지만, 데이터 파일들이 CSV 포맷의 형태로 교환되는 경우가 굉장히 많습니다. 특히 마이크로소프트 엑셀(Excel)로 편집된 자료가 원래의 엑셀 포맷인 XLS 또는 XLSX 등의 포맷으로 저장되는 것이 일반적이지만, CSV 포맷의 파일로 저장되는 경우도 매우 흔한 일입니다. 그 이유는 XLS 또는 XLSX 포맷으로 저장된 파일은 엑셀(또는 다른 스프레드쉬트 앱)에서만 읽을 수 있지만, CSV 포맷은 기본적으로는 그냥 아스키(ASCII) 또는 텍스트 형태이기 때문에 활용의 폭이 훨씬 더 넓다는 장점이 있기 때문입니다.
CSV 파일 내에서는 각 컬럼이 코마(,)로 분리된 형태로 값들이 저장됩니다. 오늘 예제로 사용할 ScatterplotData_mod.csv라는 CSV 파일의 예를 들어보겠습니다. 이 파일은 원래 IDL 설치 디렉토리의 examples/data 하위 디렉토리에 있는 ScatterplotData.csv라는 파일을 제가 이번 예제를 위하여 약간 편집한 버전입니다. 이 파일은 아래에 첨부하였습니다.
이 파일을 엑셀에서 열어보면 전반부는 다음과 같이 보입니다. 간단한 설명 문구가 처음 두 라인에 걸쳐 적혀있고, 바로 다음 라인에는 각 컬럼별 값의 명칭이 적혀 있습니다. 여기까지는 일종의 헤더(Header)에 해당되는 부분이라고 볼 수 있습니다. 그 다음에서야 실제 데이터 값들이 컬럼별로 시작됩니다. 컬럼은 기본적으로 총 3개입니다. 다만 설명 문구가 적혀 있는 맨 위 두 라인만 컬럼 구분이 없이 그냥하나의 컬럼입니다. 각 컬럼별 값 명칭이 적혀 있는 세번째 라인부터 3개 컬럼의 형태가 시작됩니다.
그리고 제일 뒷부분은 다음과 같이 보입니다.
그런데 엑셀이 아닌 그냥 일반 텍스트 편집기에서 읽으면 다음과 같이 보입니다. 이와 같이 각 컬럼이 코마(,)로 구분되어 있음을 알 수 있습니다. 다만 처음 두 라인에는 코마(,)는 존재하지 않고 세번째 라인에서부터 코마(,)가 존재하는 것을 볼 수 있습니다.
이와 같이 3개 컬럼별 값들이 수록되어 있는데 전체적으로는 157개의 라인들이 존재하지만, 실제 데이터값들은 총 154개의 라인들(라인4부터 라인 157까지)에 걸쳐 존재합니다. 결국은 이 파일을 제대로 읽는다면 3종의 데이터가 각각 154개의 값을 갖는 배열이 되어야 할 것입니다. 이 파일을 IDL에서 읽으려면 제가 이 블로그에서도 예전에 소개했던 아스키 파일 읽기 관련 기능들을 사용할 수도 있습니다. 즉 READCOL과 같은 외부 라이브러리 기능을 사용해도 되고, OPENR/READF 등을 사용하는 프로그래밍 기법을 사용해도 됩니다. 하지만 오늘은 CSV라는 포맷 전용으로 제공되는 READ_CSV라는 내장함수를 이용하는 방법을 소개하고자 합니다. 참고로 이 기능에 관하여 도움말에서 정보를 확인해보면, IDL 7.1 버전에서부터 지원되기 시작했으며 8.0 및 8.5에서 기능 업데이트가 있었던 것으로 나옵니다. 유저들께서는 이러한 점을 염두에 두시기 바랍니다. 그러면 먼저 다음과 같이 파일명을 변수로 담고 READ_CSV를 사용하여 바로 한번 읽어봅시다.
file = 'ScatterplotData_mod.csv'
data = READ_CSV(file)
HELP, data
여기서는 READ_CSV 함수를 사용하여 CSV 파일을 읽은 다음 그 결과를 담은 data라는 항목에 관한 정보를 HELP 명령을 이용하여 먼저 확인해보았습니다. 이 때 출력된 결과는 다음과 같습니다.
** Structure <2ede5878>, 1 tags, length=2512, data length=2512, refs=1:
FIELD1 STRING Array[157]
일단 READ_CSV 함수는 그 결과를 항상 구조체(Structure)의 형태로 되돌려주게 되어 있다는 점을 유의해야 합니다. 일단 이 내용의 의미는 CSV 파일을 읽어서 그 안에 담긴 데이터를 FIELD1이라는 하나의 항목으로 인식했고 이 FIELD1은 157개의 문자값들로 구성된 배열의 형태를 띄고 있다는 얘기입니다. 결론부터 말하면, 이것은 우리가 원했던 결과는 아닙니다. 뭔가 잘못된 것이죠. 그 이유는 헤더 부분에 대한 처리를 제대로 하지 않았기 때문입니다. 제대로 작업하려면 위의 READ_CSV가 사용된 라인은 다음과 같이 추가적인 키워드들이 사용되는 내용으로 수정되어야 합니다.
file = 'ScatterplotData_mod.csv'
data = READ_CSV(file, HEADER=hdr, N_TABLE_HEADER=2, TABLE_HEADER=thdr)
HELP, data
여기서 HELP에 의하여 출력된 내용은 다음과 같습니다.
** Structure <2f35dd48>, 3 tags, length=3080, data length=3080, refs=1:
FIELD1 LONG Array[154]
FIELD2 DOUBLE Array[154]
FIELD3 DOUBLE Array[154]
이 내용을 보면 앞의 경우와 달리 FIELD1, FIELD2, FIELD3 등 세 종류의 항목들이 있고 각각은 154개의 값들을 담은 배열로 인식되었음을 알 수 있습니다. 또한 각 항목은 담고 있는 값들의 특성에 따라 LONG 및 DOUBLE 자료형의 배열로 정의되어 있다는 것도 확인할 수 있습니다. 앞서 제가 "실제 데이터값들은 총 154개의 라인들에 걸쳐 존재"한다고 언급했던 것과 일치합니다. 그렇다면 추가적으로 사용된 키워드들의 의미는 무엇일까요? 먼저 HEADER 키워드는 우리가 값을 부여하는 것이 아니라 이 키워드에 빈 변수를 하나 지정해주면 유저는 이 변수를 통해서 어떤 결과를 돌려받게 됩니다. 여기서는 hdr이라는 변수로 지정을 했는데 이 변수에 대한 정보를 다음과 같이 HELP 및 PRINT로 출력해봅시다.
HELP, hdr
PRINT, hdr
출력된 내용은 다음과 같습니다.
HDR STRING = Array[3]
Distance from Terminus (meters) Mean Particle size (mm) Sedimentation Rate (g/cm2yr)
이와 같이 hdr은 3개의 문자값들로 구성된 문자형 배열임을 일단 알 수 있습니다. 그리고 PRINT에 의하여 출력된 내용은 좀 장황한데, 다음과 같이 반복문을 사용하여 구성원소 하나하나를 별도의 라인에 출력하는 것이 더 나을 것 같습니다.
FOR j = 0, N_ELEMENTS(hdr)-1 DO PRINT, hdr[j]
이렇게 출력된 hdr 배열의 각 구성원소 값들은 다음과 같습니다.
Distance from Terminus (meters)
Mean Particle size (mm)
Sedimentation Rate (g/cm2yr)
이 내용은 원본 파일에서 3개의 각 컬럼별 값 명칭에 해당되는 문자열입니다. 이렇게 하면 HEADER 키워드의 의미와 역할이 좀 더 명확해질 것 같은데, IDL 도움말에서는 이 HEADER 키워드를 통하여 컬럼 헤더(Column Header)에 해당되는 정보를 문자형 배열로 돌려받을 수 있다고 되어 있습니다. 컬럼 헤더는 말 그대로 실제 데이터 각 컬럼의 명칭에 해당됩니다. 따라서 그 갯수는 컬럼의 갯수와 같습니다. 그렇다면 TABLE_HEADER 키워드의 의미는 무엇일까요? 이 키워드가 사용된 모습을 보면 방식 자체는 HEADER 키워드와 동일합니다. 즉 이 키워드에 빈 변수를 지정하면 이 변수를 통하여 어떤 결과를 돌려받게 됩니다. 여기서는 thdr이라는 변수로 정의하였는데, 다음과 같이 이 변수에 관한 정보를 HELP 및 PRINT로 출력해봅시다.
HELP, thdr
PRINT, thdr
출력된 내용은 다음과 같습니다.
2012 Simulated Sediment Distribution at the terminus of SE Alaskan Tidewater Glaciers Edited by SW Lee
일단 tdhr이 두 개의 문자값들로 구성된 배열임을 알 수 있습니다. 다만 출력된 thdr의 내용이 좀 장황해 보이기 때문에 앞서와 마찬가지로 반복문을 사용하여 하나씩 순서대로 출력해봅시다.
FOR j = 0, N_ELEMENTS(thdr)-1 DO PRINT, thdr[j]
이렇게 출력된 thdr의 각 구성원소 값들은 다음과 같습니다.
2012 Simulated Sediment Distribution at the terminus of SE Alaskan Tidewater Glaciers
Edited by SW Lee
이 내용은 보시면 알 수 있겠지만 결국 이 CSV 파일의 첫머리에 있던 헤더입니다. IDL 도움말을 보면 테이블 헤더(Table Header)라고 부릅니다. 이러한 테이블 헤더는 컬럼 헤더와는 달리 실제 데이터의 컬럼 갯수와 전혀 관계없는, 말 그대로 일반적인 정보 문구들을 담고 있는, 어떻게 보면 좀 "쓸데없는(?)" 부분이라고 볼 수도 있습니다. 어쨌든 이 CSV 파일을 사용하는 유저는 이 파일의 앞부분에 이러한 헤더가 두 줄에 걸쳐 존재한다는 것을 인지해두는 것이 좋습니다. 여기서 또 중요한 키워드가 바로 N_TABLE_HEADER이고 이 키워드에 바로 테이블 헤더의 라인 갯수인 2를 부여하는 것이 매우 중요하기 때문입니다. 따라서 CSV 파일을 읽어야 할 때에는 일단 헤더 부분이 어떻게 구성되어 있는지를 미리 파악해두는 것이 좋습니다. 헤더 관련 키워드를 사용하지 않을 경우에는 디폴트로 알아서 작업이 되기도 하겠지만, 이렇게 방치할 경우에는 유저가 원하는 방향으로 읽지 못할 가능성이 더 큽니다.
자 그러면 이제는 제대로 인지된 3종의 각 항목별 데이터를 배열화하는 작업이 필요합니다. 이를 위해서는 다음과 같이 data라는 구조체 내의 각 항목을 추출하여 배열로 지정하면 됩니다.
arr1 = data.field1
arr2 = data.field2
arr3 = data.field3
HELP, arr1, arr2, arr3
여기서는 3종의 항목들을 각각 arr1, arr2, arr3이라는 배열로 생성하였습니다. 그리고 HELP에 의하여 출력된 각 배열별 정보는 다음과 같습니다.
ARR1 LONG = Array[154]
ARR2 DOUBLE = Array[154]
ARR3 DOUBLE = Array[154]
이 내용을 보면 CSV 파일로부터 인지된 3종의 데이터 항목들이 각각 LONG, DOUBLE, DOUBLE 자료형의 배열로 인식되어 있음을 확인할 수 있습니다. 즉 긴 정수(Long Integer) 및 2배 정밀도 실수(Double Precision) 자료형으로 인식된 상태인데요. 이대로 사용할 수도 있겠지만, 각 항목별로 자료형을 유저가 직접 명시하고자 할 경우도 있습니다. 이러한 경우에는 다음과 같이 TYPE 키워드를 사용하여 원하는 자료형을 직접 명시할 수 있습니다.
data = READ_CSV(file, HEADER=hdr, N_TABLE_HEADER=2, TABLE_HEADER=thdr, $
TYPES=['int', 'float', 'float'])
여기서는 일반 정수(Integer) 및 일반 실수(Float) 등의 자료형으로 인지되도록 하고 있습니다. 이러한 수정 사항을 반영하여 처리하면 최종적으로 얻어지는 arr1, arr2, arr3에 대한 정보는 다음과 같습니다.
ARR1 INT = Array[154]
ARR2 FLOAT = Array[154]
ARR3 FLOAT = Array[154]
그러면 이제 각 배열의 값을 한번 확인해봅시다. 다음과 같이 반복문을 사용하여 전반부 5개 라인의 값들을 출력해봅시다.
FOR j = 0, 4 DO PRINT, arr1[j], arr2[j], arr3[j]
출력된 내용은 다음과 같습니다. 실제로 CSV 파일에 담긴 내용과 같음을 알 수 있습니다.
0 0.0620000 32.5000
100 0.0740000 34.9000
200 0.0170000 24.6000
300 0.0110000 14.2000
400 0.00800000 8.80000
이번엔 마지막 5개 라인의 값들도 다음과 같이 반복문을 사용하여 출력해봅시다.
FOR j = -5, -1 DO PRINT, arr1[j], arr2[j], arr3[j]
출력된 내용은 다음과 같습니다.
590 0.0142560 16.3964
680 0.00855360 10.5405
760 0.00570240 7.41741
880 0.00570240 3.51351
1010 0.00285120 1.56156
그런데 이 내용이 실제로 CSV 파일에 담긴 내용과 일치할까요? 거의 일치하긴 하는데 미세하게 차이가 나는 경우도 있습니다. 예를 들어 출력 내용에서 16.3964로 나온 값은 원본 CSV 파일에서는 16.39638로 되어 있습니다. 이러한 차이가 후속 처리에 있어서 별 지장이없다면 이렇게 가도 됩니다. 하지만 그러면 안되는 경우라면 자료형이 일반 실수형이 아닌 2배 정밀도 실수형(DOUBLE)이 되도록 해줘야 할 수도 있습니다. 그래서 다음과 같이 두번째 및 세번째 컬럼 데이터의 자료형을 DOUBLE로 명시해봅시다.
data = READ_CSV(file, HEADER=hdr, N_TABLE_HEADER=2, TABLE_HEADER=thdr, $
TYPES=['int', 'double', 'double'])
이렇게 수정된 처리 과정을 거쳐 얻어진 arr1, arr2, arr3의 후반부 내용을 다시 한번 출력해서확인해보면 다음과 같습니다. 원본 데이터 값과 좀 더 정확히 일치함을 알 수 있습니다.
590 0.014256000 16.396380
680 0.0085536000 10.540530
760 0.0057024000 7.4174100
880 0.0057024000 3.5135100
1010 0.0028512000 1.5615600
어차피 이러한 부분은 유저의 판단에 따라 지정하면 됩니다. 오늘은 CSV 파일을 IDL에서 읽는 기능인 READ_CSV 함수에 관하여 살펴보았습니다. 데이터 파일들이 CSV 포맷으로 제공되는 경우가 꽤 많기 때문에, 이 기능을 잘 알고 활용하시면 좋을 것 같습니다.
'IDL > Data Type & Format' 카테고리의 다른 글
HDF 파일 읽고 쓰기에 관한 문서 자료 (0) | 2020.05.13 |
---|---|
netCDF 관련 루틴들의 사용에 관하여 (0) | 2020.04.21 |
실수의 소수점 이하 자릿수 변경 방법 (0) | 2018.07.30 |
netCDF 포맷의 데이터를 읽고 표출하기 [2] (0) | 2018.07.16 |
netCDF 포맷의 데이터를 읽고 표출하기 [1] (0) | 2018.07.11 |