머신러닝과 기상 데이터를 이용해 홈런 갯수 예측해보기 (3) 기상 데이터 가져오기

    목차
반응형

이제 각 구장 위치별 기상 데이터를 가져와야한다.

우선 어떤 구장들이 있는지 확인해보자.

import pandas as pd
import os

base_dir = 'C:/Users/Username/Desktop/'
excel_file = "homeruns.xlsx"
excel_dir = os.path.join(base_dir, excel_file)
df = pd.read_excel(excel_dir, encoding="EUC_KR")

locations = df["STADIUM"].unique()
print(locations)
['대구' '문학' '대전' '잠실' '목동' '광주' '사직' '청주' '마산' '제주' '군산' '포항' '울산' '수원'
 '고척' '창원']

 

내가 사용할 정보는 종관기상관측 자료이다. 이 자료는 기상자료개방포털에서 누구나 확인할 수 있다.

https://data.kma.go.kr/data/grnd/selectAsosRltmList.do?pgmNo=36

 

기상자료개방포털[데이터:기상관측:지상:종관기상관측(ASOS)]

자료설명 종관기상관측이란 종관규모의 날씨를 파악하기 위하여 정해진 시각에 모든 관측소에서 같은 시각에 실시하는 지상관측을 말합니다. 종관규모는 일기도에 표현되어 있는 보통의 고기압이나 저기압의 공간적 크기 및 수명을 말하며, 주로 매일의 날씨 현상을 뜻합니다. 자료형태 분, 시간, 일, 월, 연 제공기간 1904년~(지점별, 요소별 다름) 제공지점 102개 제공요소 기온, 강수, 바람, 기압, 습도, 일사, 일조, 눈, 구름, 시정, 지면상태, 지면 ·

data.kma.go.kr

 

그리고 여기서 제공하는 open-api를 이용해서 데이터를 가져올 것이다.

기상자료개방포털

로그인을 하고 활용신청을 한 뒤 마이페이지-오픈 API 현황에서 API KEY를 발급받으면 된다.

 

* 나는 일자료 데이터를 활용했다. 사실 시간자료를 통해 홈런이 나온 시간의 기상 데이터를 이용하는 것이 바람직하고 정확하겠지만 홈런이 나온 시간을 찾는 것은 스포츠투아이의 자료 제공이 필요할 것으로 보인다. 그래서 일자료 데이터로 그 날 평균 온도, 습도, 기압을 이용하기로 했다.

** 바람 데이터가 중요한 것은 당연하다. 그러나 일자료 데이터에서 필요한 바람 데이터를 뽑아낸다는 것은 어려우며 설령 뽑아낸다 하더라도 각 구장별로 외야 위치가 다르기 때문에 바람 방향에 대해 정방향인지 아닌지까지 파악해야 해서 바람 데이터를 제외하기로 했다.

*** 거기다 기상청에서 제공하는 위치의 기상정보가 구장의 기상정보와 일치한다는 보장은 없다. 어느정도 감안해야한다.

 

 

 

기상자료개방포털에서 확인할 수 있는 OpenAPI활용가이드의 예제를 보면

요청
http://data.kma.go.kr/apiData/getData?type=xml&dataCd=ASOS&dateCd=DAY&startDt=20100101&endDt=20100102&stnIds=108&schListCnt=10&pageIndex=1&apiKey=사용자api
응답

...

<body>

<stnIds>108</stnIds>

<info>

<avg_m1_5_te>9.2</avg_m1_5_te>

<n9_9_rn>1.1</n9_9_rn>

<min_ps>1019.4</min_ps>
...

으로 되어있다.

각 항의 의미는 가이드 안의 표를 보면 알 수 있다.

 

그럼 직접 데이터를 가져와보자.

데이터를 불러오는데 필요한 것은 데이터를 가져올 시작일(startDt), 마지막일(endDt), 지점번호(stnlds), apiKey 등이다.

지점번호는 기상자료개방포털에서 확인할 수 있다.

 

 

각 구장마다 구장이 속한 위치의 지점번호를 할당해주고

날짜와 위치 정보를 입력하면 해당 api 주소를 가져오도록 함수를 만들었다.

locations = {"잠실" : 108, "사직" : 159, "대구" : 143, "문학" : 112, "광주" : 156, \
             "대전" : 133, "울산" : 152, "수원" : 119, "청주" : 131, "고척" : 108, \
             "군산" : 140, "목동" : 108, "마산" : 155, "제주" : 184, "포항" : 138, \
             "창원" : 155}

def getdl(df, i):
    year = df.loc[i,"YEAR"]
    month = df.loc[i, "MONTH"]
    date = df.loc[i, "DATE"]
    location = df.loc[i, "STADIUM"]
    loc_num = locations[location]

    return (year, month, date, loc_num)

def geturl(api, dl) :
    (year, month, date, loc_num) = dl
    year = str(year)
    month = str(month)
    date = str(date)

    if len(month) == 1 :
        month = '0'+month
    if len(date) == 1 :
        date = '0'+date
    
    url = "http://data.kma.go.kr/apiData/getData?type=xml&dataCd=ASOS&dateCd=DAY&startDt="+year+month+date \
          + "&endDt="+year+month+date+"&stnIds="+str(loc_num)+"&schListCnt=30&pageIndex=1&apiKey=" \
          + api
    return url

 

 

api = "본인이 얻은 api 키 입력"
dl = getdl(df, 0)
url = geturl(api, dl)
req = requests.get(url)
html = req.text
print(html)
(생략)
<SUM_RN_DUR>7.17</SUM_RN_DUR>
<MIN_PS>1020.4</MIN_PS>
<MAX_WD>110</MAX_WD>
<TM>2008-03-29</TM>
<AVG_RHM>66.4</AVG_RHM>
<STN_ID>143</STN_ID>
<MIN_RHM_HRMT>1431</MIN_RHM_HRMT>
<MAX_INS_WS_WD>90</MAX_INS_WS_WD>
<AVG_TS>10.5</AVG_TS>
<MAX_INS_WS_HRMT>1517</MAX_INS_WS_HRMT>
<MAX_PS_HRMT>817</MAX_PS_HRMT>
<AVG_PV>7.4</AVG_PV>
<SUM_SS_HR>0.4</SUM_SS_HR>
<MIN_RHM>44</MIN_RHM>
<SS_DUR>12.5</SS_DUR>
<AVG_PS>1022.7</AVG_PS>
(생략)

내가 필요한 자료는 평균 기온, 평균 습도, 평균 기압이다.

우리가 흔히 알고 있는 기온은 지상 1.5m의 온도를 말하는 것이므로 avg_m1_5_te 값이 필요하며

습도는 avg_rhm, 기압은 avg_ps 값이 필요하다.

 

필요한 값을 찾는 코드를 짜서 돌려봤는데, 어떤 지역은 값이 누락인건지 모르겠으나 없었다.

그래서 대체할 수 있는 다른 걸 찾도록 코드를 짰다.

def finddata(url):
    req = requests.get(url)
    html = req.text
    
    if "<AVG_M1_5_TE>" in html :
        te_l = html.find("<AVG_M1_5_TE>")
        te_r = html.find("</AVG_M1_5_TE>")
        te = html[te_l+len("<AVG_M1_5_TE>"):te_r]
    elif "<AVG_TS>" in html :
        te_l = html.find("<AVG_TS>")
        te_r = html.find("</AVG_TS>")
        te = html[te_l+len("<AVG_TS>"):te_r]
    else :
    	te = -100
        
        
    if "<AVG_RHM>" in html :
        rh_l = html.find("<AVG_RHM>")
        rh_r = html.find("</AVG_RHM>")
        rh = html[rh_l+len("<AVG_RHM>"):rh_r]
    elif  "<MIN_RHM>" in html :
        rh_l = html.find("<MIN_RHM>")
        rh_r = html.find("</MIN_RHM>")
        rh = html[rh_l+len("<MIN_RHM>"):rh_r]
    else :
        rh = -100


    if "<AVG_PS>" in html :
        ps_l = html.find("<AVG_PS>")
        ps_r = html.find("</AVG_PS>")
        ps = html[ps_l+len("<AVG_PS>"):ps_r]
    elif "<MIN_PS>" in html :
        ps_l = html.find("<MIN_PS>")
        ps_r = html.find("</MIN_PS>")
        ps = html[ps_l+len("<MIN_PS>"):ps_r]
    elif "<MAX_PS>" in html :
        ps_l = html.find("<MAX_PS>")
        ps_r = html.find("</MAX_PS>")
        ps = html[ps_l+len("<MAX_PS>"):ps_r]
    else :
        ps = -100
        
    return (te, rh, ps)

평균 기온(1.5m)이 없으면 평균 지면온도(0m)를, 평균 상대습도가 없으면 최소 상대습도를, 평균 기압이 없으면 최소, 최대기압을 찾도록 했고 그마저도 없으면 -100을 반환하도록 했다. (결과적으로 -100은 없었다.)

 

이제 엑셀 파일에 저장하는 일만 남았다.

import time

excel_file_2 = "homeruns_weather.xlsx"
data = pd.read_excel(excel_file_2, encoding = "EUC_KR")

for i in range(len(df["YEAR"])):
    dl = getdl(df, i)
    url = geturl(api, dl)
    (te, rh, ps) = finddata(url)
    hr = df.loc[i, "HRS"]

    l = len(data["TE"])

    try :
        ps = float(ps)
        te = float(te)
        rh = float(rh)
    except :
        ps = -100
        te = -100
        rh = -100
    
    if df.loc[i, "STADIUM"] == "고척" :
        data.loc[l] = [25, 50, float(ps), hr]
    else :
        data.loc[l] = [float(te), float(rh), float(ps), hr]

    time.sleep(1)

data.to_excel(excel_file_2, index = False)
print("Saved!")

마찬가지로 엑셀을 열어 첫 행에 TE, RH, PS, HR을 입력하고 homeruns_weather.xlsx로 저장한다.

고척 구장의 경우, 온도 25도, 습도 50%를 유지한다고 한다. (링크)

그래서 STADIUM 값이 고척인 경우에는 기압 데이터만 이용했다.

 

API활용가이드를 보면 평균응답시간이 2000ms (2초)라고 한다.

그러므로 약간의 시간을 주지 않으면 데이터 서버에서 응답하지 않는다고 한다.

그래서 ps, te, rh가 string으로 반환되므로 1차적으로 1초 쉬게끔 time.sleep(1)을 해줬고 그래도 응답하지 않는 경우를 대비하여 -100을 반환하도록 했다.

* 컴퓨터의 성능이나 인터넷 상태에 따라 갈리는 것 같다. 몇 번 오류가 생기더니 마지막으로 돌릴 때는 오류가 없었다.

 

오류 테스트를 위해 몇 번 돌렸는데, 정말 오래 걸렸다. (1초씩 쉬게 했으니 당연한 것인가)

그래서 남은 시간을 확인하기 위해 진행도 체크 코드를 넣어봤다.

 

t = time.time()
m = 3
y = 2008
for i in range(len(df["YEAR"])):
    dl = getdl(df, i)
    url = geturl(api, dl)
    (te, rh, ps) = finddata(url)
    hr = df.loc[i, "HRS"]

    l = len(data["TE"])

    try :
        ps = float(ps)
        te = float(te)
        rh = float(rh)
    except :
        ps = -100
        te = -100
        rh = -100
    
    if df.loc[i, "STADIUM"] == "고척" :
        data.loc[l] = [25, 50, float(ps), hr]
    else :
        data.loc[l] = [float(te), float(rh), float(ps), hr]

    if m != df.loc[i, "MONTH"] :
        print("%d %02d done (%.2f" % (y, m, l*100/len(df["YEAR"])) +'%)', \
              '걸린 시간 :', time.strftime("%H:%M:%S", time.gmtime(int(time.time() - t))), \
              '예상 남은 시간 :',  time.strftime("%H:%M:%S", time.gmtime(int((time.time()-t)*(len(df["YEAR"])/l-1)))))
        m = df.loc[i, "MONTH"]
        y = df.loc[i, "YEAR"]
    time.sleep(1)

print("%d %02d done (%.2f" % (y, m, l*100/len(df["YEAR"])) +'%)', \
        '걸린 시간 :', time.strftime("%H:%M:%S", time.gmtime(int(time.time() - t))), \
        '예상 남은 시간 :',  time.strftime("%H:%M:%S",time.gmtime(int((time.time()-t)*(len(df["YEAR"])/l-1)))))
data.to_excel(excel_file_2, index = False)
print("Saved!")

음... 분명 더 간단히 표현하는 방법이 있을 것 같은데 그냥 떠오르는대로 했다...

내가 봐도 더러운 코드인 건 맞다. 다른 방법이 떠오르지 않았다 ㅜㅜ

 

아무튼 이렇게 했을 때 약 3시간 20분의 시간이 필요했고 마침내 온도, 습도, 기압, 홈런수 엑셀 파일이 완성되었다.

homeruns_weather.xlsx
0.15MB

 

(다음 글에 이어서)

 

 

전체 코드

더보기
import pandas as pd
import os
import requests
import time

t = time.time()
base_dir = 'C:/Users/Username/Desktop/'
excel_file = "homeruns.xlsx"
excel_dir = os.path.join(base_dir, excel_file)
df = pd.read_excel(excel_dir, encoding="EUC_KR")

locations = {"잠실" : 108, "사직" : 159, "대구" : 143, "문학" : 112, "광주" : 156, \
             "대전" : 133, "울산" : 152, "수원" : 119, "청주" : 131, "고척" : 108, \
             "군산" : 140, "목동" : 108, "마산" : 155, "제주" : 184, "포항" : 138, \
             "창원" : 155}

def getdl(df, i):
    year = df.loc[i,"YEAR"]
    month = df.loc[i, "MONTH"]
    date = df.loc[i, "DATE"]
    location = df.loc[i, "STADIUM"]
    loc_num = locations[location]

    return (year, month, date, loc_num)

def geturl(api, dl) :
    (year, month, date, loc_num) = dl
    year = str(year)
    month = str(month)
    date = str(date)

    if len(month) == 1 :
        month = '0'+month
    if len(date) == 1 :
        date = '0'+date
    
    url = "http://data.kma.go.kr/apiData/getData?type=xml&dataCd=ASOS&dateCd=DAY&startDt="+year+month+date \
          + "&endDt="+year+month+date+"&stnIds="+str(loc_num)+"&schListCnt=30&pageIndex=1&apiKey=" \
          + api
    return url
    

def finddata(url):
    req = requests.get(url)
    html = req.text

    if "<AVG_RHM>" in html :
        rh_l = html.find("<AVG_RHM>")
        rh_r = html.find("</AVG_RHM>")
        rh = html[rh_l+len("<AVG_RHM>"):rh_r]
    elif  "<MIN_RHM>" in html :
        rh_l = html.find("<MIN_RHM>")
        rh_r = html.find("</MIN_RHM>")
        rh = html[rh_l+len("<MIN_RHM>"):rh_r]
    else :
        rh = -1

    if "<AVG_PS>" in html :
        ps_l = html.find("<AVG_PS>")
        ps_r = html.find("</AVG_PS>")
        ps = html[ps_l+len("<AVG_PS>"):ps_r]
    elif "<MIN_PS>" in html :
        ps_l = html.find("<MIN_PS>")
        ps_r = html.find("</MIN_PS>")
        ps = html[ps_l+len("<MIN_PS>"):ps_r]
    elif "<MAX_PS>" in html :
        ps_l = html.find("<MAX_PS>")
        ps_r = html.find("</MAX_PS>")
        ps = html[ps_l+len("<MAX_PS>"):ps_r]
    else :
        ps = -1

    if "<AVG_M1_5_TE>" in html :
        te_l = html.find("<AVG_M1_5_TE>")
        te_r = html.find("</AVG_M1_5_TE>")
        te = html[te_l+len("<AVG_M1_5_TE>"):te_r]
    else :
        te_l = html.find("<AVG_TS>")
        te_r = html.find("</AVG_TS>")
        te = html[te_l+len("<AVG_TS>"):te_r]
        
    return (te, rh, ps)

    

api = "본인 API KEY"
excel_file_2 = "homeruns_weather.xlsx"
data = pd.read_excel(excel_file_2, encoding = "EUC_KR")

m = 3
y = 2008
for i in range(len(df["YEAR"])):
    dl = getdl(df, i)
    url = geturl(api, dl)
    (te, rh, ps) = finddata(url)
    hr = df.loc[i, "HRS"]

    l = len(data["TE"])

    try :
        ps = float(ps)
        te = float(te)
        rh = float(rh)
    except :
        ps = -100
        te = -100
        rh = -100
    
    if df.loc[i, "STADIUM"] == "고척" :
        data.loc[l] = [25, 50, float(ps), hr]
    else :
        data.loc[l] = [float(te), float(rh), float(ps), hr]

    if m != df.loc[i, "MONTH"] :
        print("%d %02d done (%.2f" % (y, m, l*100/len(df["YEAR"])) +'%)', \
              '걸린 시간 :', time.strftime("%H:%M:%S", time.gmtime(int(time.time() - t))), \
              '예상 남은 시간 :',  time.strftime("%H:%M:%S", time.gmtime(int((time.time()-t)*(len(df["YEAR"])/l-1)))))
        m = df.loc[i, "MONTH"]
        y = df.loc[i, "YEAR"]
    time.sleep(1)

print("%d %02d done (%.2f" % (y, m, l*100/len(df["YEAR"])) +'%)', \
        '걸린 시간 :', time.strftime("%H:%M:%S", time.gmtime(int(time.time() - t))), \
        '예상 남은 시간 :',  time.strftime("%H:%M:%S",time.gmtime(int((time.time()-t)*(len(df["YEAR"])/l-1)))))
data.to_excel(excel_file_2, index = False)
print("Saved!")
728x90
반응형