오늘은 IDL에서 바이너리 파일을 읽는 방법에 관한 내용을 지난 회에 이어서 계속 진행해보도록 하겠습니다. 이번에 소개할 예제에서는 여러 종류의 데이터들이 혼재되어 있는 바이너리 파일을 다뤄보고자 합니다. 다만 IDL 설치 디렉토리에서 제공되는 바이너리 파일들 중에는 이런 형태의 것은 없기 때문에, 제가 외부에서 직접 제공받은 예제 파일을 사용하고자 합니다. 이 파일은 천문학 분야에서 실제로 사용중인 것으로서, 우주론 시뮬레이션에서 추출된 여러 종류의 데이터들이 수록된 바이너리 파일입니다. 다만 파일내에 수록된 값들은 실제 값들은 아니고 일종의 가상값들로 수록되어 있습니다. 하지만 파일 내 데이터들의 형태(배열구조, 자료형)는 실제 사용되는 것과 같습니다. 이 예제 바이너리 파일은 한국천문연구원에 계시는 홍성욱 박사, 그리고 고등과학원에 계시는 김주한, 황호성 박사께서 제공해주셨습니다. 이 자리를 빌어 세 분 박사님들께 감사의 말씀을 드립니다. 이 파일은 아래 링크로 받으시면 됩니다.
그러면 먼저 이 바이너리 파일이 어떤 데이터들로 구성되어 있는가를 먼저 보겠습니다. 이 파일 내에는 수록되어 있는 데이터들의 목록은 다음과 같습니다.
The format of each binary snapshot file is a list of data structure, each structure contains information of each mock galaxies as follows:
* Galaxy ID (8 bytes)
* X/Y/Z position in real space [cMpc/h] (8 bytes per each)
* X/Y/Z peculiar velocity [km/s] (4 bytes per each)
* Mass proxy (similar to subhalo mass) [Msun/h] (4 bytes)
이 파일은 헤더(header) 부분 없이 바로 위와 같은 데이터들로 구성된 셋트(Set)들이 다수로 존재하는 형태를 띄고 있습니다. 즉 위에 명시된 8종의 데이터들이 한 라인(line)에 해당되며, 이러한 라인들이 여러 개 존재한다고 보면 됩니다. 이해를 돕기 위하여 실제 결과만 먼저 살짝 보면 한 라인은 다음과 같이 8종의 데이터값들로 구성되어 있습니다.
1801912635 -2.2341291 26.148242 34.220965 -515.356 -234.307 334.688 3.78689e+13
그리고 한 라인에 존재하는 8종의 데이터값들은 각각 그 형태가 다릅니다. 좀 더 구체적으로는 다음과 같습니다. 물론 이러한 정보는 파일 제공자측으로부터 전달받은 내용을 근거로 하였습니다.
Galaxy ID : 8 byte integer
X/Y/Z position : 8 byte real (for each)
X/Y/Z velocity : 4 byte real (for each)
Mass proxy : 4 byte real
결국 이 바이너리 파일을 읽는다는 것은 한 라인씩 순차적으로 읽어나가는 방식이 될 수 밖에 없습니다. 따라서 우리가 앞서 살펴봤던 내용을 토대로 하면, READ_BINARY 함수를 사용하는 방식보다는 OPENR, READU를 사용하는 방식이 더 효율적인 경우라고 할 수 있습니다. 그러므로 OPENR, READU를 사용하여 읽어보기로 하겠습니다. 먼저 이 파일이 총 몇 바이트인지를 확인하기 위하여 다음과 같이 READ_BINARY를 사용해서 파일 전체를 한번 읽고 그 크기를 출력하여 확인해 봅시다.
data = READ_BINARY(file)
nbytes = N_ELEMENTS(data); number of bytes
PRINT, nbytes
그러면 이 파일에 수록된 데이터의 총량이 48000바이트임을 알 수 있습니다. 그런데 한 라인에는 8+8*3+4*3+4=48바이트의 데이터가 존재하기 때문에, 이 파일안에 존재하는 라인의 갯수는 48000/48=1000개임을 확인할 수 있습니다.
nr = nbytes/48; number of data sets
PRINT, nr
따라서 한 라인씩 데이터를 읽는 작업을 반복문의 형태로 구성할 수 있으며, 그 반복횟수는 1000회가 되어야 합니다. 그래서 이를 위한 반복형 구문을 작성하고자 합니다. 다만 이를 위해서는 한 라인에 존재하는 8종의 데이터값 각각에 해당되는 변수를 다음과 같이 생성해서 활용하는 것이 좋습니다.
id = 0LL; 8-byte integer
x = 0D; 8-byte float (double precision)
y = 0D; 8-byte float (double precision)
z = 0D; 8-byte float (double precision)
vx = 0.; 4-byte float (single precision)
vy = 0.; 4-byte float (single precision)
vz = 0.; 4-byte float (single precision)
mass = 0.; 4-byte float (single precision)
이 내용은 각 데이터의 자료형에 맞는 초기값을 부여함으로써, 각 변수에 대한 자료형을 선언하는 것으로 생각하시면 됩니다. 참고로 언급한다면, 8바이트 정수는 흔히 사용되는 2바이트 정수(integer) 또는 4바이트 정수(long integer)보다 더 큰 값들을 커버하기 위한 자료형이며, 8바이트 실수는 2배 정밀도 실수라고도 하며 4바이트 실수보다 더 정밀한 값을 표현하기 위하여 사용되는 자료형입니다. 이제 반복형 구문을 사용하여 각 라인의 데이터를 읽고, 읽을 때마다 바로 출력을 하도록 해보겠습니다. 그 내용은 다음과 같습니다.
OPENR, lun, file, /GET_LUN
FOR j = 0, nr-1 DO BEGIN
READU, lun, id, x, y, z, vx, vy, vz, mass
PRINT, id, x, y, z, vx, vy, vz, mass
ENDFOR
FREE_LUN, lun
바로 지난 회 게시물에서 언급했던 방식을 토대로 하되, 반복형 구문이 추가된 형태입니다. 이 내용을 실행하면 총 1000개의 라인이 출력됩니다. 처음 10개의 라인만 보면 다음과 같습니다.
282853160 23.296147 28.597097 4.1891561 -827.270 -654.685 -485.708 4.94454e+12
1553781855 -61.677831 40.748156 -44.918620 327.896 523.124 259.006 1.63534e+12
823954980 54.199192 -67.748244 -0.027390337 -771.576 -333.441 -740.175 3.15502e+12
2122392499 42.235766 -29.573382 -77.639613 -696.002 384.791 598.546 3.69339e+12
663306948 -11.548342 60.931740 -20.951701 229.832 -876.391 484.609 4.75231e+12
544205867 -10.790976 22.771799 83.472716 -584.786 -513.276 -951.664 1.52259e+13
30833455 -62.914639 -81.769167 -74.285972 37.4122 -557.867 -243.858 1.03287e+13
2001981709 46.040802 23.764804 -83.151094 -154.801 -163.806 -264.085 2.90370e+13
1847598551 -65.476771 -74.656499 -4.9457693 -531.158 738.044 627.447 9.45524e+12
676601151 -14.483552 -18.959982 4.5348283 341.888 -141.264 -772.069 2.34749e+13
1320262035 -58.976115 62.774462 -73.299803 -147.628 -616.114 281.046 5.21030e+12
이렇게 하면 바이너리 파일의 내용을 읽는 작업은 별다른 문제없이 마무리된 셈입니다. 하지만 이렇게 데이터를 읽고 출력만 하고 끝나기보다는, 각 데이터 종류별로 각각 배열의 형태로 얻어야 진정한 마무리라고 볼 수 있습니다. 따라서 8종의 데이터들 중 4종(id, x, y, z)만 배열의 형태로 얻을 수 있도록 위의 반복형 구문을 수정해보면 그 내용은 다음과 같습니다.
id_arr = !null
x_arr = !null
y_arr = !null
z_arr = !null
OPENR, lun, file, /GET_LUN
FOR j = 0, nr-1 DO BEGIN
READU, lun, id, x, y, z, vx, vy, vz, mass
PRINT, id, x, y, z, vx, vy, vz, mass
id_arr = [id_arr, id]
x_arr = [x_arr, x]
y_arr = [y_arr, y]
z_arr = [z_arr, z]
ENDFOR
FREE_LUN, lun
HELP, id_arr, x_arr
여기서는 id, x, y, z 값에 대응되는 배열을 각각 id_arr, x_arr, y_arr, z_arr로 정의하고, 반복형 구문 내에서 라인을 읽을 때마다 각 배열에 해당값을 붙여서 누적시켜 나가는 방식을 사용하였습니다. 예를 들어 x_arr의 경우 처음에는 !null로 정의를 해둔 다음, 반복문 내에서새로운 값을 읽을 때마다 그 값을 뒤에 계속 붙여나감으로써, 처음에는 null값만 존재했던 x_arr은 나중에 1000개의 값들로 구성되는 배욜이 되어가는 방식입니다. 다른 데이터의 경우도 배열로 만들려면 같은 방식을 사용하면 됩니다.
ID_ARR LONG64 = Array[1000]
X_ARR DOUBLE = Array[1000]
바이너리 파일에 수록된 데이터를 읽어 배열의 형태로 얻는 과정을 살펴보았습니다. 이렇게 얻어진 배열은 사용자의 목적에 맞게 처리 및 분석 과정을 거치게 될 겁니다. 예를 들어 x_arr, y_arr, z_arr 데이터를 사용하여 3차원 공간상에 데이터 포인트들을 위치에 맞게 표출하는 작업은 다음과 같이 구현해 볼 수 있습니다. 그 결과는 다음 그림과 같습니다.
win = WINDOW(DIMENSIONS=[600, 500], /NO_TOOLBAR)
rng = [-100, 100]
scp = SCATTERPLOT3D(x_arr, y_arr, z_arr, SYM_OBJECT=orb(), $
SYM_COLOR='green', XRANGE=rng, YRANGE=rng, ZRANGE=rng, $
AXIS_STYLE=2, /CURRENT)
물론 이것은 하나의 가시화 예제입니다. 여기서 사용된 SCATTERPLOT3D를 사용한 가시화 방법에 관해서는 제가 전에 올린 게시물의 내용을 참조하시면 됩니다.
그러면 바이너리 파일 읽기에 관한 게시물 연재는 일단 여기서 마치도록 하겠습니다. 물론 다른 좋은 예제 파일을 찾게 된다면 이 내용은 또 이어질 수 있음을 밝혀둡니다.
'IDL > Data Type & Format' 카테고리의 다른 글
GRIB 포맷의 파일을 읽고 데이터를 표출하기 [2] (0) | 2018.07.03 |
---|---|
GRIB 포맷의 파일을 읽고 데이터를 표출하기 [1] (2) | 2018.06.29 |
IDL에서 바이너리(Binary) 파일 읽기 [2] (0) | 2017.12.27 |
IDL에서 바이너리(Binary) 파일 읽기 [1] (0) | 2017.12.26 |
실수를 정수로 변환하는 방법 (FIX, ROUND, CEIL, FLOOR) (0) | 2017.10.26 |