IDL에서 JSON 형식의 파일을 읽는 방법에 관해서는 꽤 오래전에 관련 게시물을 올린 바 있습니다. 일반적으로 JSON 형식으로 배포되는 파일들은 내부에 많은 양의 데이터를 담고 있는 경우가 많으며, 그 데이터를 읽어서 배열의 형태로 가져오는 것이 최우선적인 과제가 됩니다. 그래야 실제로 처리 및 표출 등의 작업으로 이어질 수 있기 때문입니다. 앞서 언급한 과거의 관련 게시물에서도 언급을 한 바 있지만, 실제로 배포되는 JSON 파일들의 내용을 잘 보면 내부적으로 포함된 데이터의 형태가 리스트(List)인 경우 또는 해쉬(Hash)인 경우로 나눠집니다. 실제로 배포되는 JSON 파일들 중 한 파일의 모습을 일부만 살펴보면 다음과 같습니다.
[["time_tag","dst"],["2024-02-14 08:00:00","-20"],["2024-02-14 09:00:00","-15"], .............................]
이와 같이 전체적으로는 꺽쇠괄호 [ ]로 둘러싸여 있습니다. 즉 전체적으로는 하나의 리스트(List) 형태입니다. 그런데 그 안에 포함된 개별 항목들도 각각 [ ]로 둘러싸여 있습니다. 즉 전체 리스트 내에 포함된 각 항목 역시 리스트의 형태로 저장된 경우입니다. 결국 이것은 리스트 내에 리스트(List within List)의 형태로 데이터가 저장된 경우에 해당됩니다. 그러면 이번에는 또 다른 JSON 파일의 모습 일부를 살펴봅시다.
[{"time-tag": "1749-01", "ssn": 96.7}, {"time-tag": "1749-02", "ssn": 104.3}, {"time-tag": "1749-03", "ssn": 116.7}, {"time-tag": "1749-04", "ssn": 92.8}, {"time-tag": "1749-05", "ssn": 141.7}, {"time-tag": "1749-06", "ssn": 139.2}, {"time-tag": "1749-07", "ssn": 158.0}, {"time-tag": "1749-08", "ssn": 110.5}, {"time-tag": "1749-09", "ssn": 126.5}, {"time-tag": "1749-10", "ssn": 125.8}, ............................................]
이 파일의 경우는 전체적으로는 [ ]로 둘러싸인 하나의 리스트 형태라는 점은 앞선 예제 파일과 동일합니다. 하지만 그 안에 포함된 개별 항목들의 경우는 각각 집합괄호 { }로 둘러싸여 있습니다. 이것은 일종의 해쉬(Hash)의 형태입니다. 즉 전체 리스트 내에 포함된 각 항목은 해쉬의 형태로 저장된 경우로서, 결국 이것은 리스트 내에 해쉬(Hash within List)의 형태로 데이터가 저장된 경우가 됩니다. 앞선 예제 파일과는 엄연히 다른 형태입니다.
따라서 JSON 파일을 읽어서 그 안의 데이터를 배열의 형태로 얻기 위한 과정에 있어서는 각각의 경우에 대하여 읽기 및 처리 방법이 서로 다르다는 것에 주목해야 합니다. 앞서 언급한 과거의 게시물에서도 JSON 파일을 읽어보긴 했는데, 그 파일의 경우는 리스트 내에 리스트의 형태로 데이터가 저장된 경우에 해당됩니다. 어쨌든 이번 기회에 두가지 경우 모두에 대하여 해당 JSON 파일을 읽어서 배열로 가져오는 방법을 각각 살펴보고, 더 나아가서 그 배열 데이터를 처리 및 표출하는 과정까지도 살펴보고자 합니다. 그래서 이번 게시물은 2회에 걸쳐서 이어질 예정이며, 이번 첫번째 회차에서는 리스트 내 리스트 그리고 두번째 회차에서는 리스트 내 해쉬인 경우를 차례로 살펴보도록 하겠습니다.
< 1 > 리스트 내 리스트 (List within List)
먼저 리스트 내 리스트의 형태로 데이터가 수록된 JSON 파일부터 처리해봅시다. 앞서 이러한 예제 파일의 일부만 제시했었는데, 실제 파일은 아래에 첨부하였습니다.
참고로 이 파일은 Dst 지수라는 데이터를 수록한 JSON 파일이며 실제 수신할 수 있는 링크는 아래와 같습니다. 다만 이 파일은 항상 최신의 데이터로 갱신되기 때문에 지금 받아본다면 제가 위에 첨부한 파일과는 내용이 다를 것입니다.
services.swpc.noaa.gov/products/kyoto-dst.json
그러면 이 파일을 지금부터 읽어봅시다. 먼저 IDL에서 JSON 파일을 읽으려면 JSON_PARSE 함수를 사용해야 합니다. 이 함수를 사용하여 JSON 파일을 읽고 그 내용을 살펴보는 과정부터 시작하면 다음과 같습니다.
file = 'kyoto-dst.json'
result = JSON_PARSE(file)
HELP, result
여기서는 JSON_PARSE 함수를 사용하여 JSON 파일을 읽고 그 결과를 result라는 항목에 담았습니다. HELP에 의하여 출력된 내용을 보면 다음과 같습니다.
RESULT LIST <ID=72673 NELEMENTS=168>
즉 result 자체는 하나의 리스트입니다. 그리고 그 안에는 총 168개의 세부 항목들이 포함되어 있다는 의미입니다. 그러면 그렇게 포함되어 있는 항목들 중 하나에 대해서만 정보를 출력해봅시다.
HELP, result[0]
이렇게 168개 중 첫번째 항목에 대한 정보를 출력하도록 해보면 내용은 다음과 같습니다.
<Expression> LIST <ID=72674 NELEMENTS=2>
즉 세부 항목 역시 하나의 리스트이고 그 내부에는 2개의 세부 요소들이 포함되어 있다는 의미입니다. 다만 그 2개의 요소들은 실제 파일의 내용을 보면 각각 " "로 둘러싸인 문자형 단일값들로 존재합니다. 결국 이러한 2종의 값들을 168개의 리스트들로부터 모두 추출하여 각각 배열의 형태로 가져오는 과정이 필요합니다. 그러한 작업을 위해서는 전체 리스트인 result에 대하여 ToArray 메서드를 적용하면 됩니다. 즉 다음과 같은 과정이 됩니다.
arr = result.ToArray()
HELP, arr
이렇게 하면 arr이라는 배열을 얻게 되는데 HELP에 의하여 출력된 정보를 보면 다음과 같습니다.
ARR STRING = Array[168, 2]
이와 같이 arr은 168x2의 구조를 갖는 2차원 배열로 얻어집니다. 여기서 168과 2라는 값의 의미는 앞서 알아낸 정보들을 토대로 쉽게 알 수 있습니다. 결국은 2개씩의 값들로 구성된 세부 항목들이 총 168개가 존재한다는 의미라고 보면 됩니다. 실제 파일의 내용을 보면 각 항목이 거느리는 2종의 값들 중 첫번째는 날짜이고 두번째는 데이터값(여기서는 Dst 지수라는 값입니다)이 됩니다. 그러면 각 종류의 값들을 개별적인 1차원 배열로 나눠서 얻는 것이 이후의 처리를 위해서도 반드시 필요해집니다. 그 과정은 다음과 같이 처리해봅시다.
tjs_str = arr[1:-1, 0]
dst_str = arr[1:-1, 1]
HELP, tjs_str, dst_str
여기서는 날짜에 해당되는 문자값들로 구성된 배열인 tjs_str 그리고 데이터값에 해당되는 문자값들로 구성된 배열인 dst_str을 각각 얻었습니다. 다만 여기서는 원래 배열인 arr로부터 첫번째 차원에 해당되는 168개를 모두 추출하지 않고 두번째부터 마지막까지 추출하라는 의미로 1:-1과 같이 인덱싱을 하였음에 유의해야 합니다. 왜냐하면 첫번째 요소의 경우는 그냥 설명 문자들인 "time_tag"와 "dst"라는 문자값들이며 실제 데이터는 아니기 때문입니다. HELP에 의하여 출력된 정보는 다음과 같습니다.
TJS_STR STRING = Array[167]
DST_STR STRING = Array[167]
이와 같이 각 데이터는 총 167개의 값들로 구성됩니다. 여기까지 하면 JSON 파일을 읽어서 데이터를 배열의 형태로 가져오는 과정까지는 완료된 셈입니다. 그러면 그 다음은 배열 데이터를 처리하여 원하는 결과를 얻는 과정이 될 것입니다. 실제 이 데이터의 특성을 감안한다면, 날짜 기반으로 데이터값을 플롯의 형태로 표출하는 작업으로 가면 될 것 같습니다. 다만 그 목적을 달성하기 위해서는 문자형 값으로 존재하는 데이터를 숫자 형태로 변환하는 것이 필요합니다. 일단 데이터값 배열인 dst_str의 경우는 다음과 같이 자료형 변환을 해주면 됩니다.
dst = FIX(dst_str)
이 Dst 지수라는 데이터값 자체가 원래 정수형으로 산출되기 때문에 여기서도 FIX 함수를 사용하여 정수형으로 변환하였습니다. 그 다음은 날짜값들로 구성된 tjs_str인데요. 이와 같이 문자 형태인 날짜값들을 실제 사용 가능하도록 하려면 Julian Date의 형태로 변환해야 합니다. 실제 이 값들 중 하나를 보면 다음과 같은 모습입니다.
"2024-02-14 08:00:00"
이 내용을 보면 "년-월-일 시:분:초"의 형태이기 때문에 좀 귀찮지만 STRMID 함수를 사용하여 년, 월, 일, 시, 분, 초에 해당되는 개별 문자를 추출하고 정수로 변환하여 JULDAY 함수에 투입하는 것이 필요합니다. 그 과정은 다음과 같습니다.
tjs = JULDAY(FIX(STRMID(tjs_str, 5, 2)), $
FIX(STRMID(tjs_str, 8, 2)), $
FIX(STRMID(tjs_str, 0, 4)), $
FIX(STRMID(tjs_str, 11, 2)), $
FIX(STRMID(tjs_str, 14, 2)))
이와 같은 과정에 의하여 얻어진 tjs 및 dst에 관한 정보를 출력해보면 다음과 같습니다.
HELP, tjs, dst
TJS DOUBLE = Array[167]
DST INT = Array[167]
Julian Date는 그 특성상 이와 같이 2배 정밀도 실수형이 됩니다. 이제는 표출 과정으로 넘어가면 됩니다. 일단 다음과 같이 tjs 및 dst를 그대로 PLOT 함수에 투입하여 표출해봅시다.
win = WINDOW(DIMENSIONS=[600, 500], /NO_TOOLBAR)
p = PLOT(tjs, dst, YRANGE=[-50, 50], COLOR='tomato', $
MARGIN=0.1, FONT_SIZE=10, /CURRENT)
이렇게 간단하게 처리해본 결과는 다음과 같습니다.
이 그림을 보면 X축이 날짜 데이터임에도 불구하고 아직은 그냥 Julian Date 값 자체인 상태이기 때문에 우리가 알아볼 수 있는 날짜의 형태는 아닙니다. 따라서 우리가 알아볼 수 있는 "년/월/일/시/분/초"와 유사한 형태로 표시되도록 처리하는 것이 필요합니다. 이러한 처리 방법에 관해서는 예전에 몇몇 관련 게시물들을 올린 적이 있지만 오늘은 좀 다른 방법을 사용해보겠습니다. 바로 LABEL_DATE 함수를 사용하는 것입니다. 위의 내용을 다음과 같이 변경해봅시다.
win = WINDOW(DIMENSIONS=[600, 500], /NO_TOOLBAR)
dummy = LABEL_DATE(DATE_FORMAT=['%M %D!C%H:%I'])
p = PLOT(tjs, dst, YRANGE=[-50, 50], COLOR='tomato', $
XTICKUNITS='day', XTICKINTERVAL=1, XMINOR=3, $
XTICKFORMAT='label_date', $
MARGIN=0.1, FONT_SIZE=10, /CURRENT)
여기서는 LABEL_DATE 함수 내에서 날짜 표시의 형식을 정의하고 이것을 PLOT 함수 내에서 XTICKFORMAT 속성에 반영하면서 눈금들에 대한 속성들도 함께 설정한 것입니다. LABEL_DATE 함수에 관해서는 나중에 기회가 되면 따로 소개하기로 하고 여기서는 더 깊이 들어가지는 않겠습니다. 어쨌든 이와 같이 처리하면 표출 결과는 다음 그림과 같습니다.
이와 같이 처리하면 JSON 파일에 수록된 데이터를 배열의 형태로 추출하여 가시화하는 작업까지 진행할 수 있습니다. 오늘은 JSON 파일 내에 수록된 데이터가 리스트 내 리스트인 경우를 살펴보았습니다. 이어지는 다음 회차에서는 리스트 내 해쉬인 경우를 살펴보도록 하겠습니다.
'IDL > Data Type & Format' 카테고리의 다른 글
JSON 파일의 읽기 및 처리 (Hash within List) (1) | 2024.03.05 |
---|---|
클래스 기반 문법에 의한 변수 및 배열 처리 [3] (5) | 2024.01.03 |
클래스 기반 문법에 의한 변수 및 배열 처리 [2] (1) | 2023.12.26 |
클래스 기반 문법에 의한 변수 및 배열 처리 [1] (0) | 2023.12.19 |
ASDF 형식 파일의 생성 및 읽기 (0) | 2023.11.02 |