본문 바로가기
파이썬으로 배우는 지구과학

파이썬을 이용하여 해수 경도-깊이별 등수심선(contour line) 그리기(feat. scipy 를 이용한 데이터 보간(interpolatio

by 0대갈장군0 2022. 10. 2.
반응형

위와 같은 그래프는 참고서나, 교과서 등에서 많이들 다루는 그래프 중 하나입니다. x축은 경도(longitude), y축은 깊이(depth)이며, 각 깊이와 경도에 따른 해수온을 바탕으로, 수온이 같은곳을 선으로 연결하면 저런 결과물이 나옵니다. 이렇게 하면, 한 지점이 아닌 여러지점에서의 수온분포를 더욱 정확하게 알 수 있어 꾀나 요긴하게 사용할 수 있는 그림자료가 됩니다.

1. 데이터 가져오기

요 데이터는 국립수산과학원에서 제공하고 있습니다. 아래는 국립수산과학원 홈페이지입니다.

KODC

www.nifs.go.kr

국립수산과학원 홈페이지에 들어가면 아래와 같은 화면이 나옵니다.

가운데의 국립수산과학원 관련서비스라는곳을 보면 정선해양 관측자료라는것이 있습니다. 클릭! 그럼 아래와 같은 화면이 나옵니다.

여기서 정선이라는 것은 일종의 관측 지점입니다. 아래 그림을 보겠습니다.

저 숫자가 정선입니다. 예를들어 정선 104라면, 동해바다의 동일 위도상의 점 11개가 정선 104에 해당합니다. 여기서는 정선 104로 연습을 해 볼 예정입니다. 그러니 아래와 같이 선택해 봅시다.

  • 해역 : 동해
  • 수심 : 전체
  • 정선 : 104
  • 정점 : 전체
  • 관측일시 2021.01.01~2021.12.31


이렇게 하고 검색을 누른 뒤 엑셀 다운로드 클릭!

다운로드한 엑셀파일을 열면

해역, 정선, 정점, 정선-정점 등 많은 데이터가 있는데 우리에게 필요한건 수온입니다. 그러니 필요없는 데이터는 지워주세요. 실습을 위해 저와 동일하게 지우는걸 추천하는데, 저는 해역, 정선-정점, 위도, 수온 QC Flag, 염분 Flag, 용존산소 QC Flag그리고 그 이후 모든 것을 지웠습니다. 그리고 기본적으로 한글은 모두 영문으로 바꾸었습니다. 그리고 정선해양관측정보라는 제목도 지워버렸습니다. 그래서 아래 화면과 같이 정리하였습니다.

뭐 사실 정선, 정점, 다 필요없는데 혹시나 하여 두었습니다. 염분이나 용존산소는 다음번에 그려보겠습니다. 이제 데이터를 정리하였으니 csv형식으로 작업하기 편한 폴더에 저장하면 데이터 가공은 끝!!

2. 라이브러리 호출

지금부터 이걸 어떻게 파이썬으로 구현할 수 있는지를 다루어 보겠습니다.
우선 필요한 라이브러리는 다음과 같습니다.

import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import griddata
import csv

matplotlib야 그래프 그릴때 필수이고 많이 사용하니 문제가 없고,
행렬을 안쓰는거 같은데 numpy가 들어가는건, 행렬이 필요하기 때문이며,
csv역시 csv파일 읽어올 때 필수이니 그러려니 하는데 아마 처음보는 녀석이 있을 것입니다.
scipy와 interpolate, griddata가 바로 문제입니다.

우선 이 데이터의 구조를 좀 아실 필요가 있습니다. 보통의 그래프는 x값과 y값만 있으면 그려지는데, 이런 데이터는 x축 y축과, 해당 좌표에 해당하는 온도, 그러니까 총 3개값의 데이터가 필요합니다. 아래 그림을 보겠습니다.

총 25개의 점이 있습니다. 이 점의 위치는 x,y축으로 표현할 수 있으며, 각 좌표에서 측정한 수온을 점 아래에 썼습니다. 그럼 4,6,8,10,12 로 숫자가 같은 곳을 선으로 잇는다고 하면, 아래와 같이 그릴 수 있습니다.

여기서 우리는 두 가지 필요 조건이 있습니다.

1) 일단 점의 위치를 나타내는 x,y축의 값을 저렇게 행렬 형태로 나열해야 합니다. 보통 주어지는 x,y값은 저렇게 행렬형태로 주어지지 않습니다. 그러니 파이썬이 알아먹지 못하는 거구요. 따라서 일정하게 나열되어있지 않은 x,y값을 행렬 형태로 배열하도록 해야합니다. 그래서 numpy가 필요합니다.
2) 숫자 6을 예를 들어보겠습니다. 6이 있는곳은 6을 따라 선을 그리면 되는데, 6이 없는 곳도 있습니다. 이런 경우 주변값을 기준으로 보간(interpolation)해야합니다. 다시말해 예측해야 하는겁니다. scipy.interpolation import griddata라는 라이브러리가 이 interpolation을 정량적으로 해 주는데, 그래서 이 라이브러리가 필요한 겁니다.
3) 숫자와 숫자 사이는 엄밀히 말하면 아무런 값도 없습니다. 엄밀히 말하면, 이런 곳은 값이 없으니 선으로 연결할 수 없습니다. 따라서 이런 곳도 주변값을 이용해 interpolation 해 줘야 합니다.

반응형

3. 데이터 불러오기

다음으로, csv파일을 읽고, 빈 리스트형 데이터를 불러온 뒤, 헤더 데이터를 빼는 과정을 보겠습니다.

import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import griddata
import csv

f=open("c:\\111\\rowdatafile\\1042021ver3.csv")
data=csv.reader(f)

xlon=[]
ydepth=[]
ztemp=[]
next(data)

f=open이야 데이터를 단지 불러오는 과정이니 제외하고,

xlon, ydepth, ztemp라는 변수를 만들어주고 빈 리스트를 만들었습니다. 나중에 이 빈 리스트 데이터에 값을 하나씩 채워 나갈 예정입니다.
그리고 next라는 명령어는 지난번에도 설명하였는데, 연속적인 값을 한 줄씩 읽는 것입니다. next(data)라고 썼으니 첫 번째 헤더 데이터는 읽어 버렸습니다. 그 다음부터는 헤더 데이터 다음에 나오는 숫자 값을 읽어 나갈 것입니다.

4. 원치 않는 데이터 제외하기

for row in data:
    if row[3].split('-')[1]=='02':
        xlon.append(row[2])
        ydepth.append(row[4])
        ztemp.append(row[5])

이제 for문으로 데이터를 하나씩 읽어올 것입니다. 앞서 데이터를 봐 알 수 있듯, 여러 날짜, 여러 위치가 혼재되어있습니다. 여기서는 2021년 2월 28일에 관측한 데이터만 볼 것입니다. 그래서 숫자로 02월 해당하는 데이터만 골라내야 합니다.

if row[3].split('-')[1]=='02'가 그래서 나온 것입니다.
4번째 열의 데이터가 관측 날짜인데, 이걸 '-' 기준으로 split하면, 다시말해 쪼개면, 2021, 02, 28 04:10이렇게 총 4개 데이터가 나옵니다. 이 중 02는 2번째 데이터이기 때문에 [1]이라고 쓴 거고 이게 '02' 라면
row[2] 그러니까 3번째 열의 데이터인 위도값을 xlat에 추가하고,
row[4] 그러니까 5번째 열의 데이터인 깊이값을 ydepth에 추가하며,
row[5] 그러니까 6번째 열의 데이터인 수온값을 ztemp에 추가하라는 말입니다.

이렇게 함으로써 원하는 데이터인 2월 관측 데이터만 따로 추려내게 됩니다.

5. 데이터를 바둑판 행렬로 정렬하기

xi = np.linspace(129.5,131.6,200)
yi = np.linspace(0,500,200)
zi = griddata((xlon,ydepth), ztemp, (xi[None,:], yi[:,None]), method='cubic')

이제 데이터를 순서대로 바둑판 행렬로 정리할 차례입니다.
xi에서 linspace를 이용해 129.5에서 131.6까지 200개 숫자에 대한 1차원 행렬을 만들었고,
yi에서 linspace를 이용해 0부터 500까지 200개 숫자에 대한 2차원 행렬을 만들었습니다.
그리고 zi에서 griddata를 이용해, xlat와 ydepth값을 바둑판식으로 정렬했고, 각 점에 해당하는 ztemp값을 부여해 주었습니다. 이렇게 함으로써 정렬되어있지 않던 데이터를 아래 그림처럼 정렬해 주었습니다.

그 다음에, 이제 점과 점사이를 interpolation해 줄 차례입니다. 이거는 xi에서 x축 방향으로 점과 점 사이를 몇개로 쪼개서 보간(내삽,interpolation) 할건지를 정하는건데, 아까 200개 숫자로 쪼개겠다고 하였으니 점의 맨 왼쪽 시작점과 맨 오른쪽 끝점까지 총 200개의 점에 대하여 보간값을 넣게 됩니다. y축 방향도 0에서 500까지 숫자를 200개로 쪼개어 모두 보간하게 됩니다. 이 과정은 (xi[None,:], yi[:,None])라는 코드를 통해 수행하게 되고, 마지막으로 method='cubic'으로 보간 방법을 정하게 됩니다. 이렇게 3차원의 값에서는 보통 cubic을 많이 씁니다. cubic 말고도 linear등 몇 가지가 더 있습니다만, 여기서는 사용하지 않으니 이렇게만 하겠습니다.

6. 등수온선(contour line)그리기

바둑판 형식으로 데이터 정렬은 모두 되었으니 이제 등수온선(contourline)을 그릴 차례입니다. coutourline은 아래 명령어로 간단하게 그릴 수 있습니다.

CS=plt.contour(xi,yi,zi, linewidths=1, colors='black', linestyles="--", levels=[0.7,2,4,6,8,10,12])
CS.clabel(fmt='%3.2f', fontsize=10)

xi,yi,zi값은 아까 다 지정해 준 변수이고, linewidth는 선의 폭, colors는 색, linestyle은 선 종류, levels는 등 수온선을 그릴 숫자들 입니다.
CS.clabel은 선마다 값을 넣어주는 겁니다.
여기까지 해서 그림을 그리면

오.. 뭔가 그럴싸한 그림이 나왔습니다. 그런데 좀 이상합니다. y축을 뒤집어야 합니다. 그리고, 색이 없으니 좀 뭔가 심심합니다. 색을 입히는 과정까지 쓰면 글이 너무 길어질 것 같으니 우선 그래프를 뒤집고, x,y축 제목을 다는 것 까지만 해 보겠습니다.


지난 포스팅에서도 글을 썼지만 축을 반전하는건 아래 코드로 간단하게 할 수 있습니다.
plt.gca().invert_yaxis()

그런데 이건 코드가 좀 깁니다. 이렇게 하는것도 가능하죠
plt.ylim(500,0)
그리고 나머지는 축 이름달기, 그래프 제목달기 등입니다. 이렇게 코드를 완성하면

import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import griddata
import csv

f=open("c:\\111\\rowdatafile\\1042021ver3.csv")
data=csv.reader(f)

xlon=[]
ydepth=[]
ztemp=[]
next(data)

for row in data:
    if row[3].split('-')[1]=='02':
        xlon.append(row[2])
        ydepth.append(row[4])
        ztemp.append(row[5])
xi = np.linspace(129.5,131.6,200)
yi = np.linspace(0,500,200)
zi = griddata((xlon,ydepth), ztemp, (xi[None,:], yi[:,None]), method='cubic')
CS=plt.contour(xi,yi,zi, linewidths=1, colors='black', linestyles="--", levels=[0.7,2,4,6,8,10,12])
CS.clabel(fmt='%3.2f', fontsize=10)
plt.xlabel('long(deg)')
plt.ylabel('depth(m)')
plt.ylim(500,0)
plt.title('East sea temp. distribution')
plt.savefig('C:\\111\\seatemp.jpg',bbox_inches='tight', pad_inches=0.1, dpi=300)
plt.show()

이 되고, 이렇게 한 결과는 바로, 짠!!


이제 다음번 파이썬 포스팅에서는 수온별로 color를 다르게 입히는 방법에 관한 포스팅을 작성해 보겠습니다.

반응형

댓글