지난 회차에서는 리스트 내 리스트(List within List)의 형태로 데이터가 수록된 JSON 파일을 읽고 처리하는 방법 및 예제를 소개하였습니다. 오늘은 해쉬 내 리스트(Hash within List)의 형태로 데이터가 수록된 JSON 파일을 읽고 처리하는 방법에 관하여 예제와 함께 알아보겠습니다.
< 2 > 리스트 내 해쉬 (Hash 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}, ............................................]
이와 같이 전체적으로는 [ ]로 둘러싸인 하나의 리스트이지만 내부에 포함된 개별 항목들 각각은 { }로 둘러싸인 해쉬의 형태인 경우입니다. 그러면 이 JSON 파일을 지금부터 읽고 처리해봅시다. 실제 파일은 아래에 첨부합니다.
참고로 이 파일은 1749년부터 최근까지의 태양 월별 흑점수 데이터가 수록된 JSON 파일이며 실제로 수신할 수 있는 링크는 아래와 같습니다.
services.swpc.noaa.gov/json/solar-cycle/sunspots.json
그러면 이 파일을 읽어봅시다. JSON_PARSE 함수를 이용해야 한다는 것은 마찬가지니다. 이 함수를 사용하여 JSON 파일을 읽고 그 내용을 살펴보는 과정부터 다음과 같이 시작합니다.
file = 'sunspots.json'
result = JSON_PARSE(file)
HELP, result
이와 같이 JSON_PARSE 함수로 JSON 파일을 읽고 그 결과를 result라는 항목에 담았습니다. HELP에 의하여 출력된 내용을 보면 다음과 같습니다.
RESULT LIST <ID=172232 NELEMENTS=3301>
즉 result 자체는 하나의 리스트입니다. 그리고 그 안에는 총 3301개의 세부 항목들이 포함되어 있다는 의미입니다. 그러면 이 3301개의 항목들 중 하나에 대해서만 정보를 출력해봅시다.
HELP, result[0]
이렇게 8636개 중 첫번째 항목에 대한 정보를 출력하도록 해보면 내용은 다음과 같습니다.
<Expression> ORDEREDHASH <ID=172233 NELEMENTS=2>
이와 같이 개별 항목은 하나의 해쉬(Hash) 형태이며 엄밀히 따지면 Ordered Hash라는 형태이긴 하지만 근본적으로는 해쉬의 특성을 갖습니다. 그리고 그 내부에는 2개의 세부 요소들이 사전(Dictionary)의 형태로 포함되어 있습니다. 여기서 사전(Dictionary)이라는 것은 key:value 형태의 쌍(pair)을 의미합니다. 즉 3301개의 항목들 각각은 해쉬의 형태인데, 각각의 개별 항목은 2개의 데이터 쌍들로 구성되어 있다고 보면 됩니다. 그 중 하나를 예로 들면 다음과 같습니다.
{"time-tag": "1749-05", "ssn": 141.7}
이렇게 해쉬 형태로 존재하는데 그 내부를 보면 :로 묶인 key:value 형태의 쌍들이 총 2종이 존재합니다. 그러면 이러한 데이터를 추출하여 배열로 가져오고자 한다면 어떻게 해야 할까요? 즉 예를 들면 time_tag에 해당되는 날짜 정보 문자값들이 총 3301개 있을 것이고, ssn에 해당되는 실수형 값들도 총 3301개 있을 것입니다. 이와 같은 세부 요소들을 배열로 끄집어내는 것이 필요합니다. 이전 회차의 리스트 내 리스트인 경우를 보면 이 시점에서 ToArray 메서드를 사용했었습니다. 하지만 여기서는 전체 리스트에 대하여 ToArray 메서드를 적용해도 그 안에 포함된 3301개의 해쉬들에까지는 적용되지 않습니다. 즉 ToArray 메서드 자체는 리스트(List)에서만 지원될 뿐이고 해쉬(Hash)에서는 지원되지 않는다는 것을 유의해야 합니다. 따라서 여기서는 좀 다른 접근 방식이 필요한데 바로 개별 해쉬에 대하여 ToStruct 메서드를 사용하는 것입니다. 이 메서드는 말 그대로 해쉬를 구조체(Structure)라는 형태로 변환하는 역할을 합니다. 다만 ToStruct 메서드의 경우는 3301개의 모든 해쉬들에 대하여 한꺼번에 적용이 안되고, 각각의 항목에 대하여 개별적으로 적용해야 합니다. 그러므로 여기서는 반복형 구문을 사용하여 개별 항목 각각에 대하여 ToStruct 메서드를 적용하여 구조체(Structure)의 형태로 가져온 다음 원하는 값을 추출하여 해당 배열에 누적시키는 과정으로 처리해야 합니다. 사실 이게 좀 복잡한 과정이긴 하지만 제가 알기로는 더 간단한 방법은 없는 것 같습니다. 어쨌든 그러한 과정의 예를 보면 다음과 같습니다.
tjs = !null
ssn = !null
FOR j = 0, N_ELEMENTS(result)-1 DO BEGIN
tmp = result[j].ToStruct()
yr = FIX(STRMID(tmp.time_tag, 0, 4))
mo = FIX(STRMID(tmp.time_tag, 5, 2))
tjs = [tjs, JULDAY(mo, 1, yr)]
ssn = [ssn, FLOAT(tmp.ssn)]
ENDFOR
HELP, tjs, ssn
여기서는 3301개의 항목들에 대하여 반복 작업을 수행하게 됩니다. 먼저 개별 항목인 result[j]에 대하여 ToStruct 메서드를 적용하여 tmp라는 구조체로 가져옵니다. 그리고 구조체 내에서 time_tag 및 ssn 필드에 해당되는 값을 가져오면서 문자 추출 및 자료형 변환까지 모두 수행하도록 하였습니다. 참고로 구조체(Structure)에 포함된 개별 항목을 흔히 필드(Field)라고 부릅니다. 그리고 반복문의 시작에 앞서 tjs, ssn을 !null로 정의하였는데, 이들은 반복형 구문 내에서 해당 값이 계속 누적되면서 궁극적으로는 반복 종료후 각각이 배열이 되도록 한 것입니다. HELP에 의하여 출력된 내용을 보면 다음과 같습니다.
TJS LONG = Array[3301]
SSN FLOAT = Array[3301]
이와 같이 각 데이터는 3301개의 값들로 구성됩니다. 결국 여기서 중요한 것은 JSON 파일 내에서 전체 리스트의 내부에 포함된 다수의 항목들 각각이 해쉬 형태인 경우에는 이들을 추출하여 배열화하는 과정에 있어서 이와 같이 반복형 구문 기반으로 작업을 설계하고 각 반복 회차마다 ToStruct 메서드를 사용하는 것이 필요하다는 것임을 유념할 필요가 있습니다.
이제 나머지 작업은 표출 과정이 될 것입니다. 지난 회차와 마찬가지로 날짜별 데이터 값들을 플롯의 형태로 표출하는 작업이 되며, 이 때 X축의 Julian Date 값들이 우리가 알아보기 쉬운 날짜의 형태가 되도록 해주는 것도 필요합니다. 그리고 Y축의 흑점수 값의 경우 실제 값들을 감안하여 범위를 0~400으로 설정하였습니다. 이와 같이 데이터 자체의 특성을 감안하여 플롯의 형태로 표출하는 과정은 다음과 같습니다.
win = WINDOW(DIMENSIONS=[800, 400], /NO_TOOLBAR)
dummy = LABEL_DATE(DATE_FORMAT=['%Y'])
p = PLOT(tjs, ssn, YRANGE=[0, 400], $
XTICKUNITS='year', XTICKINTERVAL=50, XMINOR=4, $
XTICKFORMAT='label_date', XTITLE='Year', COLOR='tomato', $
MARGIN=[0.05, 0.1, 0.05, 0.1], FONT_SIZE=10, /CURRENT)
지난 회차와 비교하면 수백년에 걸친 데이터이기 때문에 X축의 날짜 스케일 년(Year) 단위가 된 것 외에는 별다른 차이는 없습니다. 이와 같이 처리하면 다음과 같은 표출 결과를 얻게 됩니다.
이와 같이 해쉬 내 리스트의 형태로 JSON 파일에 수록된 데이터를 배열의 형태로 추출하여 처리 및 가시화까지 수행하는 과정을 살펴보았습니다.
그래서 오늘까지 총 2회에 걸쳐서 JSON 파일 내에 데이터가 수록된 방식이 리스트 내 리스트(List within List)인 경우 및 리스트 내 해쉬(Hash within List)인 경우를 차례로 예제와 함께 소개해보았니다. 물론 완전히 또 다른 형태로 데이터가 수록된 JSON 파일도 있을 수도 있겠지만, 일단 제 경험상으로는 주로 이와 같은 두가지 형태가 주로 많은 것 같아서 이번 기회에 나눠서 소개해보았습니다. 여러분이 실제로 IDL에서 처리해야 하는 JSON 파일이 있다면 이번에 소개된 내용을 한번 참조해보시면 좋을 것 같습니다.
'IDL > Data Type & Format' 카테고리의 다른 글
JSON 파일의 읽기 및 처리 (List within List) (0) | 2024.02.27 |
---|---|
클래스 기반 문법에 의한 변수 및 배열 처리 [3] (5) | 2024.01.03 |
클래스 기반 문법에 의한 변수 및 배열 처리 [2] (1) | 2023.12.26 |
클래스 기반 문법에 의한 변수 및 배열 처리 [1] (0) | 2023.12.19 |
ASDF 형식 파일의 생성 및 읽기 (0) | 2023.11.02 |