find_hospital 함수

## tabulate library가 없는 경우 설치 필요
## pandas.to_markdown() 시 tabulate library가 필요함
!pip install tabulate 

import pandas as pd
from urllib.parse import quote
from haversine import haversine
import ssl
from urllib.request import urlopen
import numpy as np

context=ssl.create_default_context()
context.set_ciphers("DEFAULT")

#########################################
# 1) 함수 선언하기                       #
#########################################
# 함수명 : find_hospital
# 매개변수 : special_m (중증질환명), lati, long (환자 위치)

def find_hospital(special_m, lati, long ):
    #  [국립중앙의료원 - 전국응급의료기관 조회 서비스] 활용을 위한 개인 일반 인증키(Encoding) 저장

    key = "키값" 

    # city = 대구광역시, 인코딩 필요
    city = quote("대구광역시")

    ############################################################
    # 2) 병원 리스트 csv 파일 불러오기 (daegu_hospital_list.csv) #
    ############################################################
    solution_df = pd.read_csv('./daegu_hospital_list.csv')

    ############################################################
    # 3) 병원 실시간 정보 가져오기                               #
    ############################################################

    # 응급실 실시간 가용병상 

    url_realtime = "https://apis.data.go.kr/B552657/ErmctInfoInqireService/getEmrrmRltmUsefulSckbdInfoInqire?serviceKey=" + key + "&STAGE1=" + city + "&numOfRows=100&pageNo=1"

    result = urlopen(url_realtime, context=context)
    emrRealtime_big = pd.read_xml(result, xpath='//item')

    ## 응급실 실시간 가용병상 정보에서 기관코드(hpid), 응급실 병상수('hvec'), 수술실 수('hvoc') 정보만 추출하여 emRealtime_small 변수에 저장
    ## emrRealtime_big 중 [hpid, hvec, hvoc] 컬럼 활용

    emrRealtime_small = emrRealtime_big[['hpid', 'hvec', 'hvoc']] ## emrRealtime_big 중 [hpid, hvec, hvoc] 컬럼 활용

    # solution_df와 emrRealtime_small 데이터프레임을 결합하여 solution_df에 저장
    solution_df =  pd.merge(solution_df, emrRealtime_small, on = 'hpid',how='inner')

    # 실시간 중증질환자 수용 가능 병원 조회

    url_acpt = 'https://apis.data.go.kr/B552657/ErmctInfoInqireService/getSrsillDissAceptncPosblInfoInqire?serviceKey=' + key + "&STAGE1=" +city + "&numOfRows=100&pageNo=1"

    result = urlopen(url_acpt, context=context)
    emrAcpt_big =  pd.read_xml(result, xpath='.//item')

    ## 다른 API함수와 다르게 기관코드 컬럼명이 다름 (hpid --> dutyName)
    ## 기관코드 컬렴명을 'hpid'로 일치화시키기 위해, 컬럼명을 변경함

    emrAcpt_big.rename(columns = {'dutyName' : 'hpid'}, inplace = True)

    ## 실시간 중증질환자 수용 가능 병원정보에서 필요한 정보만 추출하여 emrAcpt_small 변수에 저장
    ## emrAcpt 중 [hpid, MKioskTy1, MKioskTy2, MKioskTy3, MKioskTy4, MKioskTy5, MKioskTy7,MKioskTy8, MKioskTy10, MKioskTy11] 컬럼 확인

    emrAcpt_small = emrAcpt_big[['hpid', 'MKioskTy1', 'MKioskTy2', 'MKioskTy3', 'MKioskTy4', 'MKioskTy5', 'MKioskTy7','MKioskTy8', 'MKioskTy10', 'MKioskTy11']]

    # solution_df와 emrAcpt_small 데이터프레임을 결합하여 solution_df에 저장
    solution_df = pd.merge(solution_df, emrAcpt_small, on = 'hpid',how = 'inner')

    ############################################################
    # 4) 자료 정비하기                                          #
    ############################################################

    # solution_df의 컬럼명 변경하기

    column_change = {  'hpid' : '병원코드',
                    'dutyName' : '병원명',
                    'dutyAddr' : '주소',
                    'dutyTel3' : '응급연락처',
                    'wgs84Lat' : '위도', 
                    'wgs84Lon' : '경도',
                    'hperyn' : '응급실수',
                    'hpopyn' : '수술실수',
                    'hvec' : '가용응급실수',
                    'hvoc' : '가용수술실수',
                    'MKioskTy1' : '뇌출혈', 
                    'MKioskTy2' : '뇌경색',
                    'MKioskTy3' : '심근경색',
                    'MKioskTy4' : '복부손상',
                    'MKioskTy5' : '사지접합',
                    'MKioskTy7' : '응급투석',
                    'MKioskTy8' : '조산산모',
                    'MKioskTy10' : '신생아',
                    'MKioskTy11' : '중증화상',                   
                    }

    solution_df = solution_df.rename(columns = column_change)

    # 중증질환 수용 가능 여부 데이터 중 정보 미제공, 불가능은 N로 변경 : replace

    solution_df = solution_df.replace("정보미제공","N")
    solution_df = solution_df.replace("불가능","N")

    ## 응급실수/가용응급실수, 수술실수/가용수술실 수가 0보다 작은 경우는 비정상 데이터로 추정
    ## 0보다 작은 수는 0으로 변경

    solution_df.loc[solution_df['응급실수']<0, '응급실수'] = 0
    solution_df.loc[solution_df['수술실수']<0, '수술실수'] = 0
    solution_df.loc[solution_df['가용응급실수']<0, '가용응급실수'] = 0
    solution_df.loc[solution_df['가용수술실수']<0, '가용수술실수'] = 0

    # 응급실 가용율을 구하여 새로운 컬럼으로 추가하기
    # 컬렴명 : '응급실가용율'
    # 산식 : 가용 응급실수 / 응급실 수
    # 소수 둘째 자리까지 구하기 round() 활용
    solution_df['응급실가용율'] = round(solution_df['가용응급실수'] / solution_df['응급실수'], 2)
    

    # 응급실 가용율이 1이 넘는 경우는 1로 대체
    solution_df.loc[solution_df['응급실가용율'] > 1, '응급실가용율'] = 1

    # 응급실 가용율에 따라 포화도 분류
    # 응급실 가용율 구분 단계 : ~0.1, 0.1 ~ 0.3, 0.3 ~ 0.6, 0.6 ~ 
    # 포화도 명칭 : ['불가', '혼잡', '보통', '원활']
    # pd.cut() 활용
    bins = [-np.inf, 0.1, 0.3, 0.6, np.inf]
    labels = ['불가', '혼잡', '보통', '원활']
    solution_df['응급실포화도'] = pd.cut(solution_df['응급실가용율'], bins=bins, labels=labels)

    
    ############################################################
    # 5) 환자 수용 가능한 병원 구하기                            #  
    ############################################################
    
    # 매개변수 special_m로 받은 중증질환이 중증질환 리스트에 포함될 경우
    # 중증질환 리스트 :  ['뇌출혈', '뇌경색', '심근경색', '복부손상', '사지접합', '응급투석', '조산산모', '신생아','중증화상' ]
        
    severe_diseases = ['뇌출혈', '뇌경색', '심근경색', '복부손상', '사지접합', '응급투석', '조산산모', '신생아','중증화상']

    if special_m in severe_diseases:
        # 조건1 : special_m 중증질환자 수용이 가능하고
        # 조건2 : 응급실 포화도가 불가가 아닌 병원
        condition1 = (solution_df[special_m] == 'Y') & (solution_df['가용수술실수']>=1)
        condition2 = (solution_df['응급실포화도'] != '불가')
        
        # 조건1, 2에 해당되는 응급의료기관 정보를 distance_df에 저장하기
        distance_df = solution_df[condition1 & condition2].copy()
    
    # 매개변수 special_m 값이 중증질환 리스트에 포함이 안되는 경우        
    else:
        # 조건1 : 응급실 포화도가 불가가 아닌 병원
        condition1 = solution_df['응급실포화도'] != '불가'
        
        # 조건1에 해당되는 응급의료기관 정보를 distance_df에 저장하기
        distance_df = solution_df[condition1].copy()


    ############################################################
    # 6) 환자와 병원간 거리 구하기                               # 
    ############################################################
    distance = []
    patient = (lati, long)

    for idx, row in distance_df.iterrows():
        distance.append(round( haversine(patient,(row['위도'], row['경도']), unit='km') , 2))

    distance_df.loc[:,'거리'] = distance.copy()

    
    
    
    ############################################################
    # 7) 거리 구간 구하기                                       #
    ############################################################
    bins = [-np.inf, 2, 5, 10, np.inf]
    labels = ['2km이내', '5km이내', '10km이내', '10km이상']
    
    distance_df.loc[:, '거리구분'] = pd.cut(distance_df['거리'], bins=bins, labels=labels)

    ############################################################
    # 8) 결과값 반환하기                                        #
    ############################################################

    return distance_df

 

# 중증환자 수용 가능한, 가까운 병원 조회 #1
# 환자 정보 
#   - 중증 질환 : 응급투석
#   - 환자 위치 : 대구역 근처 (35.8765167, 128.5972922))
# 거리순, 응급실 포화도 순으로 결과 출력하기 (pandas의 sort_values(), to_markdown() 활용)
print(find_hospital('응급투석', 35.8765167, 128.5972922).sort_values(['거리구분', '응급실포화도','거리'], ascending=[True, False, True]).to_markdown())

 

 

네이버 지도 API 사용을 위한 헤더 정보

 

import requests
from urllib.parse import quote

# 네이버 지도 API 사용을 위한 헤더 정보
headers = {
    'X-NCP-APIGW-API-KEY-ID': '키',
    'X-NCP-APIGW-API-KEY': '키'
}

# 특정 위치 검색 (예: 서문시장)
def get_lat_lng(location):
    url = f"https://naveropenapi.apigw.ntruss.com/map-geocode/v2/geocode?query={quote(location)}"
    response = requests.get(url, headers=headers)
    print(response.text)
    if response.status_code == 200:
        data = response.json()
        if 'addresses' in data and len(data['addresses']) > 0:
            lat = data['addresses'][0]['y']
            lng = data['addresses'][0]['x']
            return (float(lat), float(lng))
    return None

# 예제
location_name = "서울특별시 중구 명동길"
patient = get_lat_lng(location_name)
print(patient)

실시간 중증질환자 수용 가능 병원 조회

 

# 실시간 중증질환자 수용 가능 병원 조회
# url : https://apis.data.go.kr/B552657/ErmctInfoInqireService/getSrsillDissAceptncPosblInfoInqire
# 인증키 지정 : serviceKey = key
# 도시 지정 : STAGE1=city
# 출력 행수 : numOfRows=100 
# page 번호 : pageNo = 1

url_acpt = 'https://apis.data.go.kr/B552657/ErmctInfoInqireService/getSrsillDissAceptncPosblInfoInqire?serviceKey=' + key + "&STAGE1=" +city + "&numOfRows=100&pageNo=1"

result = urlopen(url_acpt, context=context)
emrAcpt_big = pd.read_xml(result, xpath='.//item')

## 다른 API함수와 다르게 기관코드 컬럼명이 다름 (hpid --> dutyName)
## 기관코드 컬렴명을 'hpid'로 일치화시키기 위해, 컬럼명을 변경함

# emrAcpt_big = 
emrAcpt_big.rename(columns = {'dutyName' : 'hpid'}, inplace = True)

emrAcpt_big.head()

 

 

## 실시간 중증질환자 수용 가능 병원정보에서 필요한 정보만 추출하여 emrAcpt_small 변수에 저장
## emrAcpt 중 [hpid, MKioskTy1, MKioskTy2, MKioskTy3, MKioskTy4, MKioskTy5, MKioskTy7,MKioskTy8, MKioskTy10, MKioskTy11] 컬럼 확인
# 'MKioskTy1' : '뇌출혈수술', 
# 'MKioskTy2' : '뇌경색의재관류',
# 'MKioskTy3' : '심근경색의재관류',
# 'MKioskTy4' : '복부손상의수술',
# 'MKioskTy5' : '사지접합의수술',
# 'MKioskTy7' : '응급투석',
# 'MKioskTy8' : '조산산모',
# 'MKioskTy10' : '신생아',
# 'MKioskTy11' : '중증화상'

emrAcpt_small = emrAcpt_big[['hpid', 'MKioskTy1', 'MKioskTy2', 'MKioskTy3', 'MKioskTy4', 'MKioskTy5', 'MKioskTy7','MKioskTy8', 'MKioskTy10', 'MKioskTy11']]
emrAcpt_small.head()

 

 

데이터프레임을 결합 후 solution_df에 저장

 

# solution_df와 emrAcpt_small 데이터프레임을 결합하여 solution_df에 저장
# 결합 : merge 함수 활용

solution_df = pd.merge(solution_df, emrAcpt_small, on = 'hpid',how = 'inner')
solution_df.head()

 

대구광역시내 응급의료기관 목록정보 조회

 

# 대구광역시내이 응급의료기관 목록정보 조회하기
# url : https://apis.data.go.kr/B552657/ErmctInfoInqireService/getEgytBassInfoInqire
# 인증키 지정 : serviceKey = key
# 응급의료기관 지정 : HPID = hpid
# 응급의료기관 하나씩 조회해야 함

# 응급의료기관에 대한 hpid값을 list로 추출
hpidList = list(solution_df['hpid']) 

# 각 응급의료기관의 응급실 병상수(hperyn), 수술실 수(hpopyn) 를 저장하기 위한 빈 리스트 생성
hperynList = [] 
hpopynList = [] 

# 대구광역시의 응급의료기관을 hpid 기준으로 하나씩 조회하여, 응급실 병상수/수술실 수를 확인 
# for문을 이용하여 hpid 하나씩 조회하고, hperyn, hpopyn 정보를 각 hperynList, hpopynList 리스트에 저장 
for hpid in hpidList: # 
    url_basic = f'https://apis.data.go.kr/B552657/ErmctInfoInqireService/getEgytBassInfoInqire?serviceKey={key}&HPID={hpid}' 
    result = urlopen(url_basic, context=context) 
    df_temp = pd.read_xml(result, xpath='.//item') 
    # print(df_temp[['hperyn', 'hpopyn']]) 
    
    if 'hperyn' in list(df_temp.columns) : 
        hperynList.append(df_temp['hperyn'][0]) 
        
    else: 
        hperynList.append(0) 
    
    if 'hpopyn' in list(df_temp.columns) : 
        hpopynList.append(df_temp['hpopyn'][0]) 
    else: 
        hpopynList.append(0) 
hperynList

# 기존 solution_df에 응급실 숫자, 수술실 숫자 정보 추가하기
solution_df['hperyn'] = hperynList
solution_df['hpopyn'] = hpopynList

 

 

hperyn, hpopyn 값이 0 이하인 병원 삭제

 

# hperyn, hpopyn 값이 0 이하인 병원을 삭제하자
solution_df = solution_df[solution_df['hpopyn']>0]
solution_df = solution_df[solution_df['hperyn']>0]
solution_df.to_csv(''daegu_hospital_list.csv', index = False) # 파일 저장
solution_df = pd.read_csv('./daegu_hospital_list.csv')
solution_df.head() # 정상적으로 저장되었는지 확인한다.

## solution_df 를 파일로 저장 
solution_df.to_csv('daegu_hospital_list.csv', index = False) ## 파일명 : daegu_hospital_list.csv, index = False

solution_df = pd.read_csv('./daegu_hospital_list.csv') ## 정상적으로 저장되었는지 확인 
solution_df.head()

 

url과 key를 통해 데이터 받아오기

 

url  = "https://apis.data.go.kr/B552657/ErmctInfoInqireService/getEgytListInfoInqire?serviceKey=" +key + "&Q0=" + city + "&numOfRows=100&pageNo=1"

print(f"url : \n{url}")
result = urlopen(url, context = context)
emrList = pd.read_xml(result, xpath = '//item') # 3줄의 역할은 아래와 같다.

 

ChatGPT 설명 참조

 

 

 

emrlist

 

emrList.shape # 응급의료기관 수 확인하기 : shape 

emrList.columns # 컬럼 정보 확인하기

## 응급의료기관 목록정보 중 기관코드(hpid), 응급기관명(dutyName), 주소(dutyAddr), 응급실연락처(dutyTel3), 위도(wgs84Lat), 경도(wgs84Lon) 정보만 추출한다.

## ermList 중 [hpid, dutyName, dutyAddr, dutyTel3, wgs84Lat, wgs84Lon] 컬럼 활용하자

solution_df = emrList[['hpid', 'dutyName', 'dutyAddr', 'dutyTel3', 'wgs84Lat', 'wgs84Lon']].copy()  ## solution_df 에 저장하기
solution_df.head()

 

사용할 공공 데이터

 

 

데이터 설명

 

 

오퍼레이션 목록

 

 

서비스 URL :  http://apis.data.go.kr/B552657/ErmctInfoInqireService


사용 목록은 아래와 같다.

 

"응급의료기관 목록정보 getEgytListInfoInqire"

"응급의료기관 기본정보 조회 getEgytBassInfoInqire"

"응급의료기관 실시간 가용병상정보 조회 getEmrrmRltmUsefulSckbdInfoInqire"
"중증질환자 수용가능정보 조회 getSrsillDissAceptncPosblInfoInqire"


서비스 URL과 조합하면 아래와 같다.

 

응급의료기관 목록정보 
https://apis.data.go.kr/B552657/ErmctInfoInqireService/getEgytListInfoInqire 

응급의료기관 기본정보 조회
https://apis.data.go.kr/B552657/ErmctInfoInqireService/getEgytBassInfoInqire

응급의료기관 실시간 가용병상정보 조회
https://apis.data.go.kr/B552657/ErmctInfoInqireService/getEmrrmRltmUsefulSckbdInfoInqire

중증질환자 수용가능정보 조회
https://apis.data.go.kr/B552657/ErmctInfoInqireService/getSrsillDissAceptncPosblInfoInqire

기본 세팅은 아래와 같이 한다.

 

 

인증키 코드

 

key = "일반 인증키(Encoding)"

# [국립중앙의료원 - 전국응급의료기관 조회 서비스] 활용을 위한 개인 일반 인증키(Encoding) 값 저장

city = quote("대구광역시") # city = 대구광역시, 인코딩 필요 

# 인증키 지정 : serviceKey = key
# 출력 행수 : numOfRows = 100 
# page 번호 : pageNo = 1

# 도시 지정 : Q0 = city
import json
import urllib
from urllib.request import Request, urlopen

 

# 주소에 geocoding 적용하는 함수를 작성.
def get_location(loc) :
    client_id = 'id'
    client_secret = 'secret'
    url = f"https://naveropenapi.apigw.ntruss.com/map-geocode/v2/geocode?query=" + urllib.parse.quote(loc)
    # 주소 변환
    request = urllib.request.Request(url)
    request.add_header('X-NCP-APIGW-API-KEY-ID', client_id)
    request.add_header('X-NCP-APIGW-API-KEY', client_secret)

    response = urlopen(request)
    res = response.getcode()

    if (res == 200) : # 응답이 정상적으로 완료되면 200을 return한다
        response_body = response.read().decode('utf-8')
        response_body = json.loads(response_body)
        print(response_body)
        # 주소가 존재할 경우 total count == 1이 반환됨.
        if response_body['meta']['totalCount'] == 1 :
        	# 위도, 경도 좌표를 받아와서 return해 줌.
            lat = response_body['addresses'][0]['y']
            lon = response_body['addresses'][0]['x']
            return (lon, lat)
        else :
            print('location not exist')
    else :
        print('ERROR')

#  함수 적용
start = get_location(start)
goal = get_location(goal)

option = ''

단계

  • 라이브러리 불러오기
  • 함수 만들기
  • geocoding
  • preprocessing
  • predict_disease
  • find_hospital
  • 화면 구성하기
  •  

 

라이브러리 불러오기

import pandas as pd
import numpy as np
import datetime
import joblib
from keras.models import load_model
from haversine import haversine
from urlib.parse import quote
import streamlit as st
from streamlit_folium import st_folium
import folium
import branca
from geopy.geocoders import Nominatim
import ssl
from urllib.request import urlopen
from keras.models import load_model

 

함수 만들기

 

geocoding

def geocoding(address):

	geolocoder = Nominatim(user_agent = 'South Korea', timeout = None)

	geo = geolocoder.geocode(address)

	lati = geo.latitude

	long = geo.longitude

	return lati, long

 

 

preprocessing # 발열, 고혈압, 저혈압 조건에 따른 질병 전처리 함수

def preprocessing(desease):

	desease['발열'] = [ 1 if x>=37 else 0 for x in desease['체온']]

	desease['고혈압'] = [1 if x >= 140 else 0 for x in desease['수축기 혈압']]

	desease['저혈압'] = [1 if x <= 90 else 0 for x in desease['수축기 혈압']]

	# X : '중증질환' 및 데이터분석에서 Target에 영향을 주지 않는 컬럼 제외한 나머지

	X = desease[['체온', '수축기 혈압', '이완기 혈압', '호흡 곤란','간헐성 경련',
 	  	 '설사', '기침', '출혈', '통증', '만지면 아프다', '무감각', '마비', '현기증', '졸도', 
  		 '말이 어눌해졌다', '시력이 흐려짐', '발열', '고혈압', '저혈압']]

	return X

 

predict_disease # 사전 저장된 모델 파일 필요(119_model_XGC.pkl)

def predict_disease(patient_data):

	sym_list =  ['뇌경색', '뇌출혈', '복부손상', '심근경색']

	test_df = pd.DataFrame(patient_data)

	test_x = preprocessing(test_df)

	model_XGC = joblib('./119_model_XGC.pkl')

	pred_y_XGC = model_XGC.predict(test_x)

	return sym_list[pred_y_XGC[0]]

 

find_hospital # 실시간 병원 정보 API 데이터 가져오기

def find_hospital(special_m, lati, long):

	context = ssl.create_default_context()
	context.set_ciphers("DEFAULT")

	#  [국립중앙의료원 - 전국응급의료기관 조회 서비스] 활용을 위한 개인 일반 인증키(Encoding) 저장
	key = "개인 일반 인증키(Encoding)"
	city = quote('대구광역시')
	solution_df = pd.read_csv('./daegu_hospital_list_1.csv')

	# 응급실 실시간 가용병상 조회
	url_realtime = 'https://apis.data.go.kr/B552657/ErmctInfoInqireService/getEmrrmRltmUsefulSckbdInfoInqire' + '?serviceKey=' + key + '&STAGE1=' + city + '&pageNo=1&numOfRows=100'
	result = urlopen(url_realtime, context=context)
	emrRealtime = pd.read_xml(result, xpath='.//item')
	solution_df = pd.merge(solution_df, emrRealtime[['hpid', 'hvec', 'hvoc']])

	# 응급실 실시간 중증질환 수용 가능 여부
	url_acpt = 'https://apis.data.go.kr/B552657/ErmctInfoInqireService/getSrsillDissAceptncPosblInfoInqire' + '?serviceKey=' + key + '&STAGE1=' + city + '&pageNo=1&numOfRows=100'
	result = urlopen(url_acpt, context=context)
	emrAcpt = pd.read_xml(result, xpath='.//item')
	emrAcpt = emrAcpt.rename(columns={'dutyName':'hpid'}) # 다른 API와 다르게 기관코드 컬럼명이 다르다.
	solution_df = pd.merge(solution_df,
                           emrAcpt[['hpid', 'MKioskTy1', 'MKioskTy2', 'MKioskTy3', 'MKioskTy4', 'MKioskTy5', 'MKioskTy7',
                                'MKioskTy8', 'MKioskTy9', 'MKioskTy10', 'MKioskTy11']])

	# 컬럼명 변경
	column_change = {'hpid': '병원코드',
                     'dutyName': '병원명',
                     'dutyAddr': '주소',
                     'dutyTel3': '응급연락처',
                     'wgs84Lat': '위도',
                     'wgs84Lon': '경도',
                     'hperyn': '응급실수',
                     'hpopyn': '수술실수',
                     'hvec': '가용응급실수',
                     'hvoc': '가용수술실수',
                     'MKioskTy1': '뇌출혈',
                     'MKioskTy2': '뇌경색',
                     'MKioskTy3': '심근경색',
                     'MKioskTy4': '복부손상',
                     'MKioskTy5': '사지접합',
                     'MKioskTy7': '응급투석',
                     'MKioskTy8': '조산산모',
                     'MKioskTy10': '신생아',
                     'MKioskTy11': '중증화상'
                     }

	solution_df. = solution_df.rename(columns = column_change)
	solution_df = solution_df.replace({'정보미제공':'N'})

	# 응급실 가용율, 포화도 추가
	solution_df.loc[solution_df['가용수술실수']<0, '가용수술실수'] = 0
	solution_df['응급실가용율'] = round(solution_df['가용응급실수'] / solution_df['응급실수'],2)
	solution_df.loc[solution_df['응급실가용율'] >1, '응급실가용율'] = 1
	solution_df['응급실포화도'] = pd.cut(solution_df['응급실가용율'], bins = [ -1, 0.1, 0.2, 0.3, 0.6, 1], 

                                   labels = ['불가', '혼잡', '보통','원활'] )

	# 중증 질환 수용 가능한 병원 추출
	if special_m == '중증 아님':
		condition1 = (soliution_df['응급실포화도'] != '불가']
		distance = solution_df[condition1].copy)_

	else:
		condition1 = (solution_df[special_m] =='Y') & (solution_df['가용수술실수'] >= 1)
		condition2 = (solution_df['응급실포화도'] != '불가')
		distance_df = solution_df[ condition1 & condition2].copy()
        
        # 환자 위치로부터 거리 계산
        distance = []
        patient = (lati, long)

        for idx, fow in distance_df.iterrows():
			distance.append(round(haversine((row['위도'], row['경도']), patient, unit = 'km'), 2) )

        distance_df['거리'] =distance
        distance_df['거리구분'] = pd.cut(distance_df['거리'], bins = [-1, 2, 5, 10, 100],
                                                              labels = ['2km이내', '5km이내','10km이내','10km이상'] )

        return distance_df

 

화면 구성하기

# 레이아웃 구성하기

st.set_page_config(layout = 'wide')

 

# tabs 만들기

tab1, tab2 = st.tabs(['출동 일지', '대시보드'])

 

# tab1 내용 구성하기

with tab1

        # 이름 넣기

        st.markdown('## 119 응급 출동 일지')

         # 시간 정보 가져오기

         now_date = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(hours = 9)

 

         # 환자 정보 넣기

         st.markdown('### 환자 정보')

 

         ## -------------------- ▼ 1-1그룹 날짜/시간 입력 cols 구성(출동일/날짜정보(input_date)/출동시간/시간정보(input_time)) ▼ -------------------

         col110, col111, col112, col113 = st.columns( [0.1, 0.4, 0.1, 0.4])

          with col110:

                    st.info('출동일')

          with col111:

                    input_date = st.date_input('출동 일자', label_visibility = 'collapsed')

          with col112:

                     st.info('출동시간')

          with col113:

                      input_time = st.time_input('출동 시간', datetime.time('출동 시간', datetime.time(now_date.hour, now_date.minute),

                                label_visibility = 'collapsed')

 

 

 

           # 그룹 이름/성별 입력

 

           col120, col121, col122, col123, col124, col125 = st.columns([0.1, 0.5, 0.1, 0.1, 0.1, 0.1 ])

           with col120:
                       st.info('이름')

           with col121:

                       name = st.text_input('이름', label_visibility = 'collapsed')

           with col122:

                       st.info('나이')

           with col123:

                       age = st.number_input('나이', label_visibility = 'collapsed', min_value = 0, max_value = 120)

            with col124:

                        st.info('성별')

            with col125:

                        patient_s = st.radio('성별', ['남성','여성'], label_visibility = 'collapsed', horizontal = True)             

 

 

            # 그룹 체온/ 환자위치(주소) 입력

            col130, col131, col132, col133 = st.columns([0.1, 0.4, 0.1, 0.4])

            with col130:

                         st.info('체온')

            with col131:

                         fever = st.number_input('체온', min_value = 30.0, max_value = 50.0, label_visibility = 'collapsed',

             step = 0.1, value = 36.5)

 

             with col132:

                          st.info('환자 위치')

             with col133:

                          location = st.text_input('환자 위치', label_visibility = 'collapsed', value = '대구광역시 북구 연암로 40')

 

             # 혈압 입력

            col140, col141, col142, col143 = st.columns(['0.1 ,0.4, 0.1, 0.4])

             with col140:

                          st.info('수축기 혈압')

             with col141:

                          high_blood = st.slider('수축기 혈압, min_value, max_value = 200,  value = 120, step = 1,

                                       label_visibility = 'collapsed')

             with col142:

                          st.info('이완기 혈압')

                          low_blood = st.slider('이완기 혈압',  min_value = 10, max_value = 200, value = 80, step =1,             

                                       label_visibility = 'collapsed')     

 

 

              # 환자 증상체크 입력 cols 구성

               st.markdown('### 증상 체크하기')

               col150, col151, col152, col153, col154, col155, col156, col157 = st.columns(8)

 

               with col150:

                                st.error('증상 체크')                  

               with col151:

                               cough_check = st.checkbox('기침')

                               convolution_check = st.checkbox('간헐척 경련')

                with col152:
                                paralysis_check = st.checkbox("마비")
                                insensitive_check = st.checkbox("무감각")
                with col153:
                                pain_check = st.checkbox("통증")
                                touch_pain_check = st.checkbox("만지면 아픔")
                with col154:
                                inarticulate_check = st.checkbox("말이 어눌해짐")
                                swoon_check = st.checkbox("졸도")
                with col155:
                                diarrhea_check = st.checkbox("설사")
                                bleeding_check = st.checkbox("출혈")
                with col156:
                                blurred_check = st.checkbox("시력 저하")
                                breath_check = st.checkbox("호흡 곤란")
                 with col157:
                                dizziness_check = st.checkbox("현기증")

                

              # 중증 질환 여부

              col160, col161, col162 = st.columns([0.2, 0.2, 0.6])

               with col160:

                             st.error('중증 질환 여부')

               with col161:

                             special_yn = st.selectbox("판단", ("중증 질환 아님", "중증 질환 선택", "중증 질환 예측"), label_visibility="collapsed")

               with col162:

                               st.write("")

 

                  ## -------------------- ▼ 1-7그룹 중증 질환 선택 또는 예측 결과 표시 cols 구성 ▼ --------------------
                   col170, col171 = st.columns([0.02, 0.98])

                    with col170:
                                        st.write("")
                    with col171:
                                        if special_yn == "중증 질환 예측":

                                                            patient_data = {
                                                                                "체온": [fever],
                                                                                "수축기 혈압": [high_blood],
                                                                                "이완기 혈압": [low_blood],
                                                                                "호흡 곤란": [int(breath_check)],
                                                                                "간헐성 경련": [int(convulsion_check)],
                                                                                "설사": [int(diarrhea_check)],
                                                                                "기침": [int(cough_check)],
                                                                                "출혈": [int(bleeding_check)],
                                                                                "통증": [int(pain_check)],
                                                                                "만지면 아프다": [int(touch_pain_check)],
                                                                                "무감각": [int(insensitive_check)],
                                                                                "마비": [int(paralysis_check)],
                                                                                "현기증": [int(dizziness_check)],
                                                                                "졸도": [int(swoon_check)],
                                                                                "말이 어눌해졌다": [int(inarticulate_check)],
                                                                                "시력이 흐려짐": [int(blurred_check)],
                                                                                "중증질환": [""]
                                                                                }
            
                                                            # AI 모델 중증질환 예측 함수 호출
                                                            special_m = predict_disease(patient_data)
            
                                                            st.markdown(f"### 예측된 중증 질환은 {special_m}입니다")
                                                            st.write("중증 질환 예측은 뇌출혈, 뇌경색, 심근경색, 응급내시경 4가지만 분류됩니다.")
                                                            st.write("이외의 중증 질환으로 판단될 경우, 직접 선택하세요")

                                        elif special_yn == "중증 질환 선택":
                                                            special_m = st.radio("중증 질환 선택",
                                                            ['뇌출혈', '신생아', '중증화상', "뇌경색", "심근경색", "복부손상", "사지접합",  "응급투석", "조산산모"],
                                            horizontal=True)

                                        else:
                                                    special_m = "중증 아님"
                                                    st.write("")


    ## -------------------- ▼ 1-8그룹 가용병원 표시 폼 지정 ▼ --------------------
    
                     with st.form(key='tab1_first'):
        
                     ### 병원 조회 버튼 생성
                     if st.form_submit_button(label='병원조회'):

                     #### 거리주소 -> 위도/경도 변환 함수 호출
                     lati, long = geocoding(location)

                     #### 인근 병원 찾기 함수 호출
                     hospital_list = find_hospital(special_m, lati, long)
            
                     #### 필요 병원 정보 추출 
                     display_column = ['병원명', "주소", "응급연락처", "응급실수", "수술실수", "가용응급실수", "가용수술실수", '응급실포화도',

                                          '거리', '거리구분']
                     display_df = hospital_list[display_column].sort_values(['거리구분', '응급실포화도', '거리'], ascending=[True, False, True])
                     display_df.reset_index(drop=True, inplace=True)

                     #### 추출 병원 지도에 표시
                     with st.expander("인근 병원 리스트", expanded=True):
                                          st.dataframe(display_df)
                                          m = folium.Map(location=[lati,long], zoom_start=11)
                                          icon = folium.Icon(color="red")
                                          folium.Marker(location=[lati, long], popup="환자위치", tooltip="환자위치: "+location, icon=icon).add_to(m)

                
                                           ###### folium을 활용하여 지도 그리기 (3일차 교재 branca 참조)
                
                                           for idx, row in hospital_list.iterrows():
                    
                                                                  html = """<!DOCTYPE html>
                                                                           <html>

                                                                                  
                                                                                   <table style="height: 126px; width: 330px;">  <tbody> <tr>
                                                                                   <td style="background-color: #2A799C;"><div style="color: #ffffff;text-align:center;">병원명</div></td>
                                                                                   <td style="width: 200px;background-color: #C5DCE7;">{}</td>""".format(row['병원명']) + """ </tr> 
                                                                                   <tr><td style="background-color: #2A799C;"><div style="color: #ffffff;text-align:center;">가용응급실수</div></td>
                                                                                   <td style="width: 200px;background-color: #C5DCE7;">{}</td>""".format(row['가용응급실수']) + """</tr>
                                                                                   <tr><td style="background-color: #2A799C;"><div style="color: #ffffff;text-align:center;">거리(km)</div></td>
                                                                                   <td style="width: 200px;background-color: #C5DCE7;">{}</td>""".format(row['거리']) + """ </tr>
                                                                                   </tbody> </table> </html> """
                    
                                                              iframe = branca.element.IFrame(html=html, width=350, height=150)
                                                              popup_text = folium.Popup(iframe, parse_html=True)
                                                              icon = folium.Icon(color="blue")

                                                              folium.Marker(location=[row['위도'], row['경도']], popup=popup_text,
                                                              tooltip=row['병원명'], icon=icon).add_to(m)

                                            st_folium(m, width=1000)


    
    # -------------------- 완료시간 저장하기 START-------------------- 


    ##  -------------------- ▼ 1-9그룹 완료시간 저장 폼 지정 ▼  --------------------
    with st.form(key='tab1_second'):

        ## 완료시간 시간표시 cols 구성
        col191, col192 = st.columns(2)
        
        with col191:
            st.success("완료 시간")
        with col192:
            end_time = st.time_input('완료 시간', datetime.time(now_date.hour, now_date.minute), label_visibility="collapsed")

        ## 완료시간 저장 버튼
        if st.form_submit_button(label='저장하기'):
            dispatch_data = pd.read_csv('./119_emergency_dispatch.csv', encoding="cp949" )
            id_num = list(dispatch_data['ID'].str[1:].astype(int))
            max_num = np.max(id_num)
            max_id = 'P' + str(max_num)
            elapsed = (end_time.hour - input_time.hour)*60 + (end_time.minute - input_time.minute)

            check_condition1 = (dispatch_data.loc[dispatch_data['ID'] ==max_id, '출동일시'].values[0]  == str(input_date))
            check_condition2 = (dispatch_data.loc[dispatch_data['ID']==max_id, '이름'].values[0] == name)

            ## 마지막 저장 내용과 동일한 경우, 내용을 update 시킴
            
            if check_condition1 and check_condition2:
                dispatch_data.loc[dispatch_data['ID'] == max_id, '나이'] = age
                dispatch_data.loc[dispatch_data['ID'] == max_id, '성별'] = patient_s
                dispatch_data.loc[dispatch_data['ID'] == max_id, '체온'] = fever
                dispatch_data.loc[dispatch_data['ID'] == max_id, '수축기 혈압'] = high_blood
                dispatch_data.loc[dispatch_data['ID'] == max_id, '이완기 혈압'] = low_blood
                dispatch_data.loc[dispatch_data['ID'] == max_id, '호흡 곤란'] = int(breath_check)
                dispatch_data.loc[dispatch_data['ID'] == max_id, '간헐성 경련'] = int(convulsion_check)
                dispatch_data.loc[dispatch_data['ID'] == max_id, '설사'] = int(diarrhea_check)
                dispatch_data.loc[dispatch_data['ID'] == max_id, '기침'] = int(cough_check)
                dispatch_data.loc[dispatch_data['ID'] == max_id, '출혈'] = int(bleeding_check)
                dispatch_data.loc[dispatch_data['ID'] == max_id, '통증'] = int(pain_check)
                dispatch_data.loc[dispatch_data['ID'] == max_id, '만지면 아프다'] = int(touch_pain_check)
                dispatch_data.loc[dispatch_data['ID'] == max_id, '무감각'] = int(insensitive_check)
                dispatch_data.loc[dispatch_data['ID'] == max_id, '마비'] = int(paralysis_check)
                dispatch_data.loc[dispatch_data['ID'] == max_id, '현기증'] = int(dizziness_check)
                dispatch_data.loc[dispatch_data['ID'] == max_id, '졸도'] = int(swoon_check)
                dispatch_data.loc[dispatch_data['ID'] == max_id, '말이 어눌해졌다'] = int(inarticulate_check)
                dispatch_data.loc[dispatch_data['ID'] == max_id, '시력이 흐려짐'] = int(blurred_check)
                dispatch_data.loc[dispatch_data['ID'] == max_id, '중증질환'] = special_m
                dispatch_data.loc[dispatch_data['ID'] == max_id, '이송 시간'] = int(elapsed)

            else: # 새로운 출동 이력 추가하기
                new_id = 'P' + str(max_num+1)
                new_data = {
                    "ID" : [new_id],
                    "출동일시" : [str(input_date)],
                    "이름" : [name],
                    "성별" : [patient_s],
                    "나이" : [age],
                    "체온": [fever],
                    "수축기 혈압": [high_blood],
                    "이완기 혈압": [low_blood],
                    "호흡 곤란": [int(breath_check)],
                    "간헐성 경련": [int(convulsion_check)],
                    "설사": [int(diarrhea_check)],
                    "기침": [int(cough_check)],
                    "출혈": [int(bleeding_check)],
                    "통증": [int(pain_check)],
                    "만지면 아프다": [int(touch_pain_check)],
                    "무감각": [int(insensitive_check)],
                    "마비": [int(paralysis_check)],
                    "현기증": [int(dizziness_check)],
                    "졸도": [int(swoon_check)],
                    "말이 어눌해졌다": [int(inarticulate_check)],
                    "시력이 흐려짐": [int(blurred_check)],
                    "중증질환": [special_m],
                    "이송 시간" : [int(elapsed)]
                }

                new_df= pd.DataFrame(new_data)
                dispatch_data = pd.concat([dispatch_data, new_df], axis=0, ignore_index=True)

            dispatch_data.to_csv('./119_emergency_dispatch.csv', encoding="cp949", index=False)

    # -------------------- 완료시간 저장하기 END-------------------- 

# -------------------- Streamlit 웹 화면 구성 End --------------------

 

 

 

 

 

단계

  • 라이브러리 불러오기
  • 데이터 확인
  • 데이터 정보들을 파악한다.
  • 데이터 전처리를 진행하자
  • 연령정보를 확인하자
  • 출동월을 확인하자
  • 발열 정보를 확인하자
  • 고혈압 정보를 확인하자
  • 저혈압 정보를 확인하자
  • 단변량 분석을 진행하자 - 범주형
  • 중증질환
  • 연령구분
  • 단변량 분석을 진행하자 - 숫자형
  • 체온
  • 이변량 분석을 진행하자 - 범주형 vs 범주형
  • Feature : 성별 & Target : 중증 질환
  • Feature : 연령구분 & Target : 중증 질환
  • Feature : 출동월 & Target : 중증 질환
  • Feature : 증상리스트 & Target : 중증 질환
  • 이변량 분석을 진행하자 - 숫자형 vs 범주형
  • Feature : 나이 & Target :중증 질환
  • Feature : 체온 & Target : 중증 질환
  • Feature : 수축기 혈압 & Target : 중증 질환
  • Feature : 이완기 혈압 & Target : 중증 질환
  • 함수 만들기

 

라이브러리 불러오기

import pandas as pd

import numpy as np

import matplotlib.pyplot as plt

import seaborn

 

import scipy.stats as spst

plt.rcParams['font.family'] = 'Malgun Gothic'

 

데이터 확인

data = pd.read_csv('119_emergency_dispatch.csv', encoding = 'cp949')

data.columns

원본 데이터는 이러한 형태이다.

 

데이터의 컬럼들을 이런 것들이 있다.

데이터 정보들을 파악한다.

data.shape

data.info

data.head(3)

data.tail(3)

data.isna().sum()

 

데이터 전처리를 하자

연령 정보를 구분하자

data['연령구분'] = pd.cut(data['나이'], bins = bins=[-1, 1, 10, 20, 30, 40, 50, 60, 70, 80, 90, np.inf],

                                       labels=['신생아', '10대미만', '10대','20대','30대','40대','50대','60대','70대','80대','90이상'],                                                       right=False)
data.head()

 

data['연령구분'].value_couts()

출동월을 확인하자

data['출동월']  =  pd.to_datetime(data['출동일시']).dt.month

data.head()

발열정보를 확인하자

data['발열'] = [ 1 if x >=37 else 0 for x in data['체온']]
data.head()

고혈압 정보를 확인하자

data['고혈압'] = [1 if x>= 140 else 0 for x in data['수축기 혈압']]

data.head()

 

저혈압 정보를 확인하자

data['저혈압'] = [1 if x <=  90 else 0 for x in data['수축기 혈압']]

data.head()

 

단변량 분석을 진행하자 - 범주형

중증질환

# 중증 질환 통계

data['중증질환'].value_counts()

# seaborn 라이브러리의 countplot 함수를 사용하여 '중증질환' 컬럼의 범주별 개수를 시각화한다.

sns.countplot( data= data, x = '중증질환')

# 중증 질환 중 [심근경색, 복부손상, 뇌경색, 뇌출혈]에 대한 자료만 추출하여 분석

desease = data[ data['중증질환].isin(['심근경색', '복부손상', '뇌경색', '뇌출혈'])]

desease['중증질환'].value_counts()

sns.countplot(data = desease, x='중증질환')

plt.show()

연령구분

# '연령구분' 컬럼의 고유한 값(범주) 별 개수 카운트

desease['연령구분'].value_counts()

sns.countplot( data=desease, x='연령구분')

plt.show()

 

단변량 분석을 진행하자 - 숫자형

체온

desease['체온'].describe()

sns.boxplot(x = desease['체온'])

plt.show()

 

이변량 분석을 진행하자 - 범주형 vs 범주형

Feature : 성별 & Target : 중증 질환

cross_sex = pd.crosstab(desease['성별'], desease['중증질환'])

# 통계 검증 : 카이제곱 검증 (chi2_contingency)

 

chi2_sex = spst.chi2_contingency(cross_sex)

chi2_sex

 

Feature : 연령구분 & Target : 중증 질환

cross_age = pd.crosstab( desease['연령구분'], desease['중증질환'])

display(cross_age)chi2_age = spst.chi2_contingency(cross_age)chi2_age

Feature : 출동월 & Target : 중증 질환

cross_month = pd.crosstab( desease['출동월'], desease['중증질환'])

display(cross_month)

chi_month = spst.chi2_contingency(cross_month)

chi_month

Feature : 증상리스트 & Target : 중증 질환

symptom = ['호흡 곤란', '간헐성 경련', '설사', '기침', '출혈', '통증', '만지면 아프다', '무감각', '마비', '현기증', '졸도',
       '말이 어눌해졌다', '시력이 흐려짐', '발열', '고혈압', '저혈압']

 

for one_sym in symptom:

        cross_sym = pd.crosstab(data[one_sym], desease['중증질환'])

        chi2_sym = spst.chi2_contingency(cross_sym)

        ## P-value (chi2_sym[1]) 값이 0.05보다 크면 귀무가설 채택, 0.05보다 작으면 대립가설 채택

        if chi2_sym[1] >= 0.05 :
            print("귀무가설 채택", one_sym, "에 따라 중증 질환에 차이가 없다. P-value ", chi2_sym[1])
        else:
            print("대립가설 채택", one_sym, "에 따라 중증 질환에 차이가 있다. P-value ", chi2_sym[1])

이변량 분석을 진행하자 - 숫자형 vs 범주형

Feature : 나이 & Target :중증 질환

Feature : 체온 & Target : 중증 질환

Feature : 수축기 혈압 & Target : 중증 질환

Feature : 이완기 혈압 & Target : 중증 질환

 

# anova 분석 대상

anova_column = ['나이', '체온', '수축기 혈압', '이완기 혈압']

 

for one_column in anova_column:

        Class_1 = desease.loc[desease['중증질환'] == '뇌출혈', one_column]

        Class_2 = desease.loc[desease['중증질환'] == '뇌경색', one_column]

        Class_3 = desease.loc[desease['중증질환'] == '복부손상', one_column]

        Class_4 = desease.loc[desease['중증질환'] == '심근경색', one_column]

        # 통계검증 : anova 검증 ( f_oneway )

        result = spst.f_oneway(Class_1, Class_2, Class_3, Class_4)

        # p-value 확인

        print(result)

        print(result[1])

for one_column in anova_column:

        sns.barplot( data = desease, x='중증질환', y = one_column)

        plt.show()

 

 

함수 만들기

 

def preprocessing(desease):

            desease = desease.copy()

            #########################################
            # 2. 데이터 전처리 하기                  #
            #########################################
   

            # '발열' 컬럼 구하기 : 체온이 37도 이상이면 1, 아니면 0
            desease['발열'] = [1 if x>=37  else 0 for x in desease['체온']]

            # '고혈압' 칼럼 구하기 : 수축기 혈압이 140 이상이면 1, 아니면 0
            desease['고혈압'] = [ 1 if x>=140  else 0 for x in desease['수축기 혈압']]

            # '저혈압' 칼럼 구하기 : 수축기 혈압이 90 이하이면 1, 아니면 0
            desease['저혈압'] = [ 1 if x<= 90  else 0 for x in desease['수축기 혈압']]

 

            #########################################
            # 3. X에 선택된 Feature값 넣기           #
            #########################################
            # X : '중증질환' 및 데이터분석에서 Target에 영향을 주지 않는 칼럼 제외한 나머지
            # X = desease.drop(columns =['출동일시','중증질환', 'ID','이름', '성별', '나이','이송 시간'] )
    
            X = desease[['체온', '수축기 혈압', '이완기 혈압', '호흡 곤란','간헐성 경련', '설사', '기침', '출혈', '통증',

                              '만지면 아프다', 
                             '무감각', '마비', '현기증', '졸도', '말이 어눌해졌다', '시력이 흐려짐', '발열', '고혈압', '저혈압']]
    
            # 4. X 반환하기                       #
            #########################################

            return X

 

 

## 확인하기
data = pd.read_csv("./119_emergency_dispatch.csv", encoding="cp949" )
desease = data[data['중증질환'].isin(['심근경색', '복부손상', '뇌경색', '뇌출혈'])]

X = preprocessing(desease)
print(X.shape)
X.head()

 

단계

  • 라이브러리 불러오기
  • 데이터 불러오기, 데이터 랜덤으로 섞기
  • 학습용, 평가용 데이터 준비하기
  • 모델링 머신러닝  DecisionTreeClassifier, RandomForestClassifier, XGBClassifier
  • 모델링 딥러닝 DNN
  • 최적 모델 선정 및 저장
  • 새로운 출동 이력 데이터에 대한 중증 질환 예측하기
  • 새로운 환자(출동 이력)에 대한 중증질환 예측 함수 정의하기

 

* 라이브러리 불러오기

* 데이터 불러오기, 데이터 랜덤으로 섞기

# 라이브러리 불러오기
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = 'Malgun Gothic'

# 데이터 불러오기, 데이터 랜덤으로 섞기
data = pd.read_csv('119_emergency_dispatch.csv', encoding = 'cp949')
disease = data[data['중증질환'].isin(['심근경색','복부손상','뇌경색','뇌출혈'])]
disease = disease.sample(frac=1).reset_index(drop=True)

 

* 학습용, 평가용 데이터 준비하기

# 미션2에서 정의한 preprocessing 전처리 함수 정의 가져와서 실행하기

# 미션 2에서 정의한 preprocssing 전처리 함수
def preprocessing(disease):
	disease = disease.copy()
    # 발열 구하기 : 체온 37도 이상이면 1, 아니면 0
    disease['발열'] = [ 1 if x >=37 else 0 for x in disease['체온']]
    # 고혈압 컬럼 구하기 : 수축기 혈압 140 이상이면 1, 아니면 0
    disease['고혈압'] = [ 1 if x>=140 else 0 for x in disease['수축기 혈압']]
    # 저혈압 컬럼 구하기 : 수축기 혈압 90 dlgkdlaus 1, 아니면 0
    
    # X에 선택된 Feature 값 넣기 
    # X : '중증질환' 및 데이터분석에서 Target에 영향을 주지 않는 칼럼 제외한 나머지
    X = desease[['체온', '수축기 혈압', '이완기 혈압', '호흡 곤란','간헐성 경련', '설사', 
    		'기침', '출혈', '통증', '만지면 아프다', '무감각', '마비', '현기증', '졸도', 
                '말이 어눌해졌다', '시력이 흐려짐', '발열', '고혈압', '저혈압']]
 	
    return X

 

* 데이터 확인

Y = disease['중증질환']
X = preprocessing(disease)

 

* AI 모델링을 위한 학습/검증 데이터 나누기

from sklearn.model_selection import train_test_split
train_x, test_x, train_y, test_y = train_test_split(X, Y, test_size = 0.3, random_state=2023)

 

* 모델링 DecisionTreeClassifier, RandomForest, XGBoost

 

DecisionTreeClassifier

from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score

model_DTC = DecisionTreeClassifier()
model_DTC(train_x, train_y)
pre_DTC = model_DTC.predict(test_x)
print(accuracy_score(test_y, pred_DTC))

 

RandomForestClassifier

from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score

model_RFC = RandomForesClassifier()
model_RFC.fit(train_x, train_y)
pred_RFC = model_RFC.predict(test_x)
print(accuracy_score(test_y, pred_RFC)

 

XGBClassifier

from xgboost import XGBClassifier
from sklearn.metrcis import accuracy_score

model_XGC = XGBClassifier()

# target 값 라벨링
train_y_1 = train_y.map({ '뇌경색':0, '뇌출혈':1, '복부손상':2, '심근경색':3})
test_y_1 = test_y.map({'뇌경색':0, '뇌출혈' : 1, '복부손상':2, '심근경색':3})
model_XGC.fit(train_x, train_y_1)
pred_XGC = model_XGC.predict(test_x)
print(accuracy_score(test_y_1, pred_XGC))

 

 

*모델링 딥러닝 DNN

from keras.models import Sequential
from keras.layers import Dense, Dropout
from keras.backend import clear_session
from sklearn.metrics import accuracy_score

# 메모리 정리
clear_session()

# 2) 선언하기
model_DNN = Sequential()
model_DNN.add(Dense(32, input_shape = (train_shape[1], ) )
model_DNN.add(Dropout(0.3) )
model_DNN.add(Dense(16, activation = 'relu') )
model_DNN.add(Dense(4, activation = 'softmax') )

# target 값 라벨링하기 {'뇌경색':0 ,'뇌출혈':1, '복부손상':2,'심근경색':3}
train_y_1 = train_y.map({'뇌경색':0 ,'뇌출혈':1, '복부손상':2,'심근경색':3})
test_y_1 = test_y.map({'뇌경색':0 ,'뇌출혈':1, '복부손상':2,'심근경색':3})

# 학습하기
model_DNN.compile(optimizer='adam', loss = 'sparse_categorical_crossentropy',
				metrics = ['accuracy'])
history = model_DNN.fit(train_x, train_y_1, epochs = 30)

# 예측하기
pred_DNN = model_DNN.predict(test_x)

# 평가하기
print(accuracy_score( test_y_1, np.argmax(print(pred_DNN, axis = 1)) )

 

* 최적 모델 선정 및 저장

# 최적 모델 선정 및 저장
# 머신러닝 모델인 경우
import joblib
joblib.dump(model_XGC, '119_model_XGC.pkl')

# 딥러닝 모델인 경우
model_DNN.save('119_model_DNN.keras')

 

* 새로운 출동 이력 데이터에 대한 중증 질환 예측하기

# 새로운 출동 이력 데이터 : 딕셔너리 형태

# new_dispatch 딕셔너리를 데이터 프레임으로 변환
# 변수명 : new_data

# new_data를 preprocessing 함수를 이용하여 데이터 전처리하기
# 변수명 : new_x

# 새로운 출동 이력 데이터 : 딕셔너리 형태
new_dispatch = {
    "ID" : [500001],
    "출동일시" :['2023-04-18'],
    "이름" : ['최**'],
    "성별" : ["여성"],
    "나이" : [80],
    "체온" : [37],
    "수축기 혈압" : [145],
    "이완기 혈압" : [100],
    "호흡 곤란":[0],
    "간헐성 경련":[1],
    "설사":[0],
    "기침":[0],
    "출혈":[0],
    "통증":[1],
    "만지면 아프다":[0],
    "무감각":[0],
    "마비":[1],
    "현기증":[0],
    "졸도":[1],
    "말이 어눌해졌다":[1],
    "시력이 흐려짐":[1],
}
# new_dispatch 딕셔너리를 데이터 프레임으로 변환
# 변수명 : new_data
new_data = pd.DataFrame(new_dispatch)

# new_data를 preprocessing 함수를 이용하여 데이터 전처리하기
# 변수명 : new_x
new_x = preprocessing(new_data)

 

* 모델 불러오기

# 모델 불러오기
# 머신러닝 모델인 경우
import joblib
model_m = joblib.load('119_model_XGC.pkl')

# 딥러닝 모델인 경우
from keras.models import load_model
model_d = load_model('./119_model_DNN.keras')

 

* 중증 질환 예측하기

# 머신러닝 모델인 경우
print_new_d = model_m.predict(new_x)
print("예측값: ", pred_new_m)

# 딥러닝 모델인 경우
pred_new_d = np.argmax(model_d.predict(new_x) )
print("예측값 :", pred_new_d)

# 중증질환 명칭으로 표시하기
sym_list = ['뇌경색', '뇌출혈', '복부손상', '심근경색' ]

# 머신러닝 모델인 경우
print("예측 중증질환명: ", sym_list[pred_new_m[0] )

# 딥러닝 모델인 경우
print("예측 중증질환명: ", sym_list[pred_new_d] )

 

* 새로운 환자(출동 이력)에 대한 중증질환 예측 함수 정의하기

# 중증질환 예측 함수 정의하기
# 함수명 : predict_disease
# 매개변수 : new_dispatch (출동 이력 데이터, 딕셔너리 형태)
# output : 중증 질환 명칭

#########################################
# 1. 함수 선언하기                       #
#########################################

def predict_disease(new_dispatch):
    
	#########################################
    # 2. 데이터 준비하기                     #
    #########################################
    # 중증 질환 명칭 및 라벨링 {'뇌경색':0, '뇌출혈':1, '복부손상':2, '심근경색':3}
    # 중증 질환 리스트 정의 : 라벨링 순서대로
    sym_list = ['뇌경색', '뇌출혈', '복부손상', '심근경색']
    
    # 딕셔너리 형태의 출동 이력 데이터를 데이터 프레임으로 변환
    # 변수명 : new_data

    new_data = pd.DataFrame(new_dispatch)
    # new_data를 preprocessing 함수를 이용하여 데이터 전처리된 new_x 받아오기
    # preporcessing 함수 정의 부분이 먼저 실행되어 있어야 함
    
    new_x = preprocessing(new_data) 

    #########################################
    # 3. 중증 질환 예측하기                  #
    #########################################
      
    # 저장된 AI모델 불러오기 
    # 모델 변수명 : model_m
    model_m = joblib.load('119_model_XGC.pkl')


    # new_x를 기반으로 중증질환 예측하기
    pred_new_m = model_m.predict(new_x)

    #########################################
    # 4. 중증 질환명으로 반환하기             #
    #########################################

    # 예측된 결과를 중증질환 명칭으로 반환하기
    return sym_list[pred_new_m[0]]

 

* 확인하기
# predict_disease 함수를 이용하여, 출동 이력 데이터로 중증질환 예측하기

new_dispatch = {
    "ID" : [500001],
    "출동일시" :['2023-04-18'],
    "이름" : ['최**'],
    "성별" : ["여성"],
    "나이" : [80],
    "체온" : [37],
    "수축기 혈압" : [145],
    "이완기 혈압" : [100],
    "호흡 곤란":[0],
    "간헐성 경련":[1],
    "설사":[0],
    "기침":[0],
    "출혈":[0],
    "통증":[1],
    "만지면 아프다":[0],
    "무감각":[0],
    "마비":[1],
    "현기증":[0],
    "졸도":[1],
    "말이 어눌해졌다":[1],
    "시력이 흐려짐":[1],
}

 

* 예측하기

predict_disease(new_dispatch)

API를 이용한 공공 데이터 가져오기

 

ㆍ 라이브러리 불러오기

  공공데이터 기본값 설정

  대구광역시 응급의료기관 목록정보 조회 - 필요한 컬럼만 추출

  응급의료기관별 기본정보 조회 - 응급실 숫자, 수술실 숫자 조회

  응급의료기관별 실시간 가용 병상 조회

  응급의료기관별 중증질환자 수용 가능 여부 조회

  자료 정비하기 - 컬럼명을 한글로 rename, "N"으로 replace, 0 이하 수치는 0으로, 가용율 round, 포화도 cut

  상황에 따른 병원 데이터 출력하기

  병원 거리 구하기 haversine

  중증 질환자를 수용 가능한 응급 의료기관들을 거리가 가까운 순으로 출력

  특정 중증질환 및 환자 위치에 따른 병원 실시간 정보 및 거리를 구하는 함수 작성하기

 

1. 라이브러리 불러오기

 

import  pandas as pd

import  numpy as np

form urllib.parse import quote



import ssl

from urllib.request import urlopen

context = ssl.create_default_context()

context.set_ciphers("DEFAULT")

 

2. 공공데이터 기본값 설정

 

국립중앙의료원 데이터를 사용했다.

 

 

오퍼레이션 목록

 

참고문서를 확인하면 이러한 정보들을 얻을 수 있다.

 

서비스 URL :  http://apis.data.go.kr/B552657/ErmctInfoInqireService

 

사용 목록은 아래와 같다.

 

"응급의료기관 목록정보 getEgytListInfoInqire"

"응급의료기관 기본정보 조회 getEgytBassInfoInqire"

"응급의료기관 실시간 가용병상정보 조회 getEmrrmRltmUsefulSckbdInfoInqire"

"중증질환자 수용가능정보 조회 getSrsillDissAceptncPosblInfoInqire"

 

서비스 URL과 조합하면 아래와 같다.

 

응급의료기관 목록정보 

https://apis.data.go.kr/B552657/ErmctInfoInqireService/getEgytListInfoInqire 

 

응급의료기관 기본정보 조회

https://apis.data.go.kr/B552657/ErmctInfoInqireService/ getEgytBassInfoInqire

 

응급의료기관 실시간 가용병상정보 조회

https://apis.data.go.kr/B552657/ErmctInfoInqireService/getEmrrmRltmUsefulSckbdInfoInqire

 

중증질환자 수용가능정보 조회

https://apis.data.go.kr/B552657/ErmctInfoInqireService/ getSrsillDissAceptncPosblInfoInqire"

 

기본 세팅은 아래와 같이 한다.

 

 

key = "일반 인증키(Encoding)"

# [국립중앙의료원 - 전국응급의료기관 조회 서비스] 활용을 위한 개인 일반 인증키(Encoding) 값 저장

city = quote("대구광역시") # city = 대구광역시, 인코딩 필요 

# 인증키 지정 : serviceKey = key
# 출력 행수 : numOfRows = 100 
# page 번호 : pageNo = 1

# 도시 지정 : Q0 = city

 

3.대구광역시 응급의료기관 목록정보 조회하기

 

url  = "https://apis.data.go.kr/B552657/ErmctInfoInqireService/getEgytListInfoInqire?serviceKey=" +key + "&Q0=" + city + "&numOfRows=100&pageNo=1"

print(f"url : \n{url}")
result = urlopen(url, context = context)
emrList = pd.read_xml(result, xpath = '//item') # 3줄의 역할은 아래와 같다.

 

 

emrList

 

emrList를 출력하면 이와 같다.

 

emrList.shape # 응급의료기관 수 확인하기 : shape 

emrList.columns # 컬럼 정보 확인하기



## 응급의료기관 목록정보 중 기관코드(hpid), 응급기관명(dutyName), 주소(dutyAddr), 응급실연락처(dutyTel3), 위도(wgs84Lat), 경도(wgs84Lon) 정보만 추출한다.



## ermList 중 [hpid, dutyName, dutyAddr, dutyTel3, wgs84Lat, wgs84Lon] 컬럼 활용하자



solution_df = emrList[['hpid', 'dutyName', 'dutyAddr', 'dutyTel3', 'wgs84Lat', 'wgs84Lon']].copy()  ## solution_df 에 저장하기



solution_df.head()

 

solution_df의 상태는 이와 같다.

 

4.응급의료기관별 기본정보 조회 - 응급실 숫자, 수술실 숫자 조회

 

hpidList = list(solution_df['hpid']) # 응급의료기관에 대한 hpid 값을 list로 추출한다.



hperynList = [] # 각 응급의료기관의 응급실 병상 숫자(hperyn)를 저장하기 위한 빈 리스트

hpopynList = [] # 각 응급의료기관의 응급실 수술실 숫자(hperyn)를 저장하기 위한 빈 리스트



for hpid in hpidList: # for문을 이용하여 hpid 하나씩 조회하고, hperyn, hpopyn 정보를 각 hperynList, hpopynList 리스트에 저장 

       url_basic = f'https://apis.data.go.kr/B552657/ErmctInfoInqireService/getEgytBassInfoInqire?serviceKey={key}&HPID={hpid}'

       result = urlopen(url_basic, context = context)

       df_temp = pd.read_xml(result, xpath = './/item



        if 'hperyn' in list(df_temp, columns):

                hperynList.append(df_temp['hperyn'][0])

        else:

                hperynList.append(0)



        if 'hpopyn' in list(df_temp.columns):

                hpopynList.append(df_temp['hpopyn'][0])

        else:

                hpopynList.append(0)



# 기존 solution_df에 응급실 숫자, 수술실 숫자 정보 추가하기

solution_df['hperyn'] = hperynList

solution_df['hpopyn'] = hpopynList



# hperyn, hpopyn 값이 0 이하인 병원을 삭제하자

solution_df = solution_df[solution_df['hpopyn']>0]

solution_df = solution_df[solution_df['hperyn']>0]



solution_df.to_csv(''daegu_hospital_list.csv', index = False) # 파일 저장

solution_df = pd.read_csv('./daegu_hospital_list.csv')

solution_df.head() # 정상적으로 저장되었는지 확인한다.

 

 

5. 응급의료기관별 실시간 가용 병상 조회

 

# url : https://apis.data.go.kr/B552657/ErmctInfoInqireService/getEmrrmRltmUsefulSckbdInfoInqire
# 인증키 지정 : serviceKey = key
# 도시 지정 : STAGE1=city
# 출력 행수 : numOfRows=100 
# page 번호 : pageNo = 1
# emrRealtime_big 변수명으로 저장

url_realtime = "https://apis.data.go.kr/B552657/ErmctInfoInqireService/getEmrrmRltmUsefulSckbdInfoInqire?serviceKey=" + key + "&STAGE1=" + city + "&numOfRows=100&pageNo=1"

result = urlopen(url_realtiem, context = context)

emrRealtime_big = pd.read_csv(result, xpath = '//item')

# 응급실 실시간 가용병상 정보에서

기관코드(hpid), 응급실 병상수('hvec'), 수술실 수('hvoc') 정보만 추출하여 emRealtime_small 변수에 저장

emrRealtime_small = emrRealtime_big[['hpid', 'hvec', 'hvoc']] # emrRealtime_big 중 [hpid, hvec, hvoc] 컬럼 활용

solution_df = pd.merge(solution_df, emrRealtime_small, on = 'hpid', how='inner')

 

6.응급의료기관별 중증질환자 수용 가능 여부 조회

 

# 실시간 중증질환자 수용 가능 병원 조회
# url : https://apis.data.go.kr/B552657/ErmctInfoInqireService/getSrsillDissAceptncPosblInfoInqire
# 인증키 지정 : serviceKey = key
# 도시 지정 : STAGE1=city
# 출력 행수 : numOfRows=100 
# page 번호 : pageNo = 1

url_acpt = 'https://apis.data.go.kr/B552657/ErmctInfoInqireService/getSrsillDissAceptncPosblInfoInqire?serviceKey=' + key + "&STAGE1=" +city + "&numOfRows=100&pageNo=1"

result = urlopen(url_acpt, context = context)

emrAcpt_big = pd.read_xml(result, xpath = './/item')

# 다른 API 함수와 다르게 기관코드 컬럼명이 달라서 hpid로 변경한다.

emrAcpt_big.rename(columns = { 'dutyName' : 'hpid' }, inplace = True)

## 실시간 중증질환자 수용 가능 병원정보에서 필요한 정보만 추출하여 emrAcpt_small 변수에 저장
## emrAcpt 중 [hpid, MKioskTy1, MKioskTy2, MKioskTy3, MKioskTy4, MKioskTy5, MKioskTy7,MKioskTy8, MKioskTy10, MKioskTy11] 컬럼 확인
# 'MKioskTy1' : '뇌출혈수술', 
# 'MKioskTy2' : '뇌경색의재관류',
# 'MKioskTy3' : '심근경색의재관류',
# 'MKioskTy4' : '복부손상의수술',
# 'MKioskTy5' : '사지접합의수술',
# 'MKioskTy7' : '응급투석',
# 'MKioskTy8' : '조산산모',
# 'MKioskTy10' : '신생아',
# 'MKioskTy11' : '중증화상'

emrAcpt_small = emrAcpt_big[['hpid', 'MKioskTy1', 'MKioskTy2', 'MKioskTy3', 'MKioskTy4', 'MKioskTy5', 'MKioskTy7','MKioskTy8', 'MKioskTy10', 'MKioskTy11']]

# solution_df와 emrAcpt_small 데이터프레임을 결합하여 solution_df에 저장

solution_df = pd.merge(solution_df, emAcpt_small, on = 'hpid', how = 'inner')

 

7. 자료 정비하기 - 컬럼명을 한글로 rename, "N"으로 replace, 0 이하 수치는 0으로, 가용율 round, 포화도 cut

 

# column명을 한글로 교체하기 위해 딕셔너리로 정의
column_change = {  'hpid' : '병원코드',
                   'dutyName' : '병원명',
                   'dutyAddr' : '주소',
                   'dutyTel3' : '응급연락처',
                   'wgs84Lat' : '위도', 
                   'wgs84Lon' : '경도',
                   'hperyn' : '응급실수',
                   'hpopyn' : '수술실수',
                   'hvec' : '가용응급실수',
                   'hvoc' : '가용수술실수',
                   'MKioskTy1' : '뇌출혈', 
                   'MKioskTy2' : '뇌경색',
                   'MKioskTy3' : '심근경색',
                   'MKioskTy4' : '복부손상',
                   'MKioskTy5' : '사지접합',
                   'MKioskTy7' : '응급투석',
                   'MKioskTy8' : '조산산모',
                   'MKioskTy10' : '신생아',
                   'MKioskTy11' : '중증화상',
                   
                   }


# solution_df의 column 명 변경하기 : rename

solution_df = solution_df.rename(columns = column_change)

solution_df.head()

# 중증질환 수용 가능 여부 데이터 중 정보 미제공, 불가능은 N로 변경 : replace

solution_df = solution_df.replace("정보미제공", "N")

solution_df = solution_df.replace("불가능", "N")

# 응급실가용율을 구하여 새로운 컬럼으로 추가하기

solution_df['응급실가용율'] = round(solution_df['가용응급실수'] / solution_df['응급실수'],2)

# 응급실가용율 값이 1보다 큰 경우, 1로 설정

solution_df.loc[solution_df['응급실가용율']>1, '응급실가용율'] = 1



# 응급실 가용율에 따라 포화도 분류
# 응급실 가용율 구분 단계 : ~0.1, 0.1 ~ 0.3, 0.3 ~ 0.6, 0.6 ~ 
# 포화도 명칭 : ['불가', '혼잡', '보통', '원활']
# pd.cut() 활용

bins = [ -np.inf, 0.1, 0.3, 0.6, np.inf ]

label = ['불가', '혼잡', '보통', '원활']

solution_df['응급실포화도'] = pd.cut(solution_df['응급실가용율'], bins = bins, labels = labels)

 

구간 나누기

 

8.상황에 따른 병원 데이터 출력하기

 

# 상황1) 응급실 수용이 가능한 병원
# 조건1 : 응급실 포화도가 불가가 아닌 병원을 출력
condition1 = ( solution_df['응급실포화도'] != '불가')
solution_df[condition1].head()



# 상황2) 중증질환자 수용이 가능하고, 응급실 수용이 가능한 병원

# 조건1 : '뇌출혈' 중증질환자 수용 및 수술실 수용 가능한(가용 수술실수가 1이상) 응급의료기관 조회
# 조건2 : 응급실 포화도가 불가가 아닌 병원을 출력

special_m = '뇌출혈'

condition1 = ( solution_df[special_m] =='Y' ) & ( solution_df['가용수술실수'] >= 1 )

condition2 = (solution_df['응급실포화도'] != '불가')

solution_df[condition1 & condition2]

 

9.병원 거리 구하기 haversine

 

from haversine import haversine # 라이브러리 import

patient= (35.8690760, 128.5811616)  #Latitude, Longitude # 환자 위치 : 서문시장 내

distance = [] # 환자와 병원 간 거리를 저장할 빈 리스트

distance_df = solution_df.copy() # solution_df의 복사본을 만들어 distance_df에 저장

for idx, row in distance_df.iterrows():

        # haversine 함수를 이용하여 환자와 병원간 거리 구하기 
        # distance 변수에 하나씩 채우기
        # print(row['위도'], row['경도'] )

        distance.append( round( haversine( patient,  ( row['위도'],  row['경도'] ), unit = 'km'), 2)

distance_df['거리'] = distance # distance_df의 '거리' 컬럼에 distance 값 저장하자

distance_df # 확인하기

# distance_df의 '거리'에 따라 거리구분 분류 : cut()

# '거리구분' 명칭 : ['2km이내', '5km이내', '10km이내', '10km이상']

bins = [-1, 2, 5, 10, np.inf ] 
labels = ['2km이내', '5km이내', '10km이내', '10km이상']

distance_df['거리구분'] = pd.cut(distance_df['거리'], bins = bins, labels = labels )
distance_df.head()

# 가까운 거리순으로 : '거리구분'

# 응급실이 원활한 순서로 : '응급실포화도'

# 정렬하기 : sort_values

distance_df.sort_values( ['거리구분', '응급실포화도'], ascending = [True, False])

 

정렬 기준에 따라 출력하면 위와 같다.

 

10.중증 질환자를 수용 가능한 응급의료기관을 가까운 거리순으로 출력

 

# 중증질환자 수용이 가능하고, 응급실 수용이 가능한 병원 중 환자 위치에서 가까운 병원
# 조건1 : '중증화상' 중증질환자 수용 및 수술실 수용 가능한 응급으료기관 조회
# 조건2 :응급실 포화도가 불가가 아닌 병원
# 정렬 : 환자 위치를 중심으로 가장 가까운 병원부터 출력

patient= (35.8690760, 128.5811616)  # 환자 위치 : 서문시장 내 #Latitude, Longitude

# 조건1 : '중증화상' 중증질환자 수용 및 수술실 수용 가능한 응급으료기관 조회

special_m = '중증화상'

condition1 = (solution_df[special_m] =='Y') & (solution_df['가용수술실수] >=1)

# 조건2 : '응급실 포화도가 불가가 아닌 병원

condition2 = (solution_df['응급실포화도'] != '불가')

# 조건1, 2에 해당되는 응급의료기관 정보를 distance_df에 저장하기

distance_df = solution_df[ condition1 & condition2].copy()

# 환자와 병원 간 거리를 저장할 빈 리스트 만들기

# 변수명 : distance

distance = []

for idx, row in distance_df.iterrows():

        # haversine 함수를 이용하여 환자와 병원간 거리 구하기 
        # 단위 : km
        # 소수 둘째자리까지 구하기
        distance.append( round( haversine( patient, ( row['위도'], row['경도']) , unit = 'km'), 2)  ) # distance 변수에 하나씩 채우기 

# distance_df의 '거리' 컬럼에 distance 값 저장하기

distance_df['거리'] = distance # distance_df의 '거리' 컬럼에 distance 값 저장

# distance_df의 '거리'에 따라 거리구분 분류 : cut()

# 거리구분 명칭 : ['2km 이내', '5kim 이내', '10km 이내', '10km 이상']

bins = [-1, 2, 5, 10, np.inf]

labels = ['2km이내', '5km이내', '10km이내', '10km이상']

distance_df['거리구분'] = pd.cut(distance_df['거리'], bins = bins, labels = labels)

# 환자 위치에서 가까운 거리순으로, 응급실 포화도가 원활한 순서로 정렬

distance_df.sort_values( ['거리구분','응급실포화도'] , ascending = [True, False])

 

11.특정 중증질환 및 환자 위치에 따른 병원 실시간 정보 및 거리를 구하는 함수 작성하기

 

## tabulate library가 없는 경우 설치 필요
## pandas.to_markdown() 시 tabulate library가 필요함
!pip install tabulate 



import pandas as pd
from urllib.parse import quote
from haversine import haversine
import ssl
from urllib.request import urlopen
import numpy as np



context=ssl.create_default_context()
context.set_ciphers("DEFAULT")



def find_hospital(special_m, lati, long):

    key = "개인키" 

    # city = 대구광역시, 인코딩 필요
    city = quote("대구광역시")

    ############################################################
    # 2) 병원 리스트 csv 파일 불러오기 (daegu_hospital_list.csv) #
    ############################################################
    solution_df = pd.read_csv('./daegu_hospital_list.csv')

    ############################################################
    # 3) 병원 실시간 정보 가져오기                               #
    ############################################################

    # 응급실 실시간 가용병상 

    url_realtime = "https://apis.data.go.kr/B552657/ErmctInfoInqireService/getEmrrmRltmUsefulSckbdInfoInqire?serviceKey=" + key + "&STAGE1=" + city + "&numOfRows=100&pageNo=1"

    result = urlopen(url_realtime, context = context)

    emrRealtime_big = pd.read_xml(result, xpath = '//item')



    ## 응급실 실시간 가용병상 정보에서 기관코드(hpid), 응급실 병상수('hvec'), 수술실 수('hvoc') 정보만 추출하여 emRealtime_small 변수에 저장
    ## emrRealtime_big 중 [hpid, hvec, hvoc] 컬럼 활용

    

    emrRealtime_small = emrRealtime_big[ ['hpid', 'hvec', 'hvoc' ] ] 



    # solution_df와 emrRealtime_small 데이터프레임을 결합하여 solution_df에 저장하자

    solution_df = pd.merge(solution_df, emrRealtime_small )



    # 실시간 중증질환자 수용 가능 병원 조회
    url_acpt = 'https://apis.data.go.kr/B552657/ErmctInfoInqireService/getSrsillDissAceptncPosblInfoInqire?serviceKey=' + key + "&STAGE1=" +city + "&numOfRows=100&pageNo=1"



    # 다른 API 함수와 다르게 기관코드 컬럼명이 다름(hpid --> dutyName)

    # 기관코드 컬럼명을 'hpid'로 일치화시키기 위해 컬럼명을 변경하자

    emrAcpt_big = emrAcpt_big.rename(columns = {'dutyname' : 'hpid'})



    ## 실시간 중증질환자 수용 가능 병원정보에서 필요한 정보만 추출하여 emrAcpt_small 변수에 저장
    ## emrAcpt 중 [hpid, MKioskTy1, MKioskTy2, MKioskTy3, MKioskTy4, MKioskTy5, MKioskTy7,MKioskTy8, MKioskTy10, MKioskTy11] 컬럼 확인

    emrAcpt_small = emrAcpt_big[['hpid', 'MKioskTy1', 'MKioskTy2', 'MKioskTy3', 'MKioskTy4', 'MKioskTy5', 'MKioskTy7','MKioskTy8', 'MKioskTy10', 'MKioskTy11']]



     # solution_df와 emrRealtime_small 데이터프레임을 결합하여 solution_df에 저장하자

    solution_df = pd.merge(solution_df, emrAcpt_small)



    ############################################################
    # 4) 자료 정비하기                                          #
    ############################################################

    # solution_df의 컬럼명 변경하기

    column_change = {  'hpid' : '병원코드',
                    'dutyName' : '병원명',
                    'dutyAddr' : '주소',
                    'dutyTel3' : '응급연락처',
                    'wgs84Lat' : '위도', 
                    'wgs84Lon' : '경도',
                    'hperyn' : '응급실수',
                    'hpopyn' : '수술실수',
                    'hvec' : '가용응급실수',
                    'hvoc' : '가용수술실수',
                    'MKioskTy1' : '뇌출혈', 
                    'MKioskTy2' : '뇌경색',
                    'MKioskTy3' : '심근경색',
                    'MKioskTy4' : '복부손상',
                    'MKioskTy5' : '사지접합',
                    'MKioskTy7' : '응급투석',
                    'MKioskTy8' : '조산산모',
                    'MKioskTy10' : '신생아',
                    'MKioskTy11' : '중증화상',                   
                    }

    solution_df = solution_df.rename(columns=column_change)

    # 중증질환 수용 가능 여부 데이터 중 정보 미제공, 불가능은 N로 변경 : replace



    solution_df = solution_df.replace( { "정보미제공": "N"} }

    solution_df = solution_df.replace( { "불가": "N"} }



    ## 응급실수/가용응급실수, 수술실수/가용수술실 수가 0보다 작은 경우는 비정상 데이터로 추정
    ## 0보다 작은 수는 0으로 변경

    solution_df.loc[solution_df['응급실수']<0, '응급실수'] = 0

    solution_df.loc[solution_df['수술실수']<0, '수술실수'] = 0

    solution_df.loc[solution_df['가용응급실수']<0, '가용응급실수']  = 0

    solution_df.loc[solution_df['가용응급실수']<0, '가용수술실수'] = 0



    # 응급실 가용율이 1이 넘는 경우는 1로 대체

    solution_df.loc[ solution['응급실가용율'] >1, '응급실가용율'] =1



    # 응급실 가용율에 따라 포화도 분류
    # 응급실 가용율 구분 단계 : ~0.1, 0.1 ~ 0.3, 0.3 ~ 0.6, 0.6 ~ 
    # 포화도 명칭 : ['불가', '혼잡', '보통', '원활']
    # pd.cut() 활용

    bins = [  -np.inf, 0.1, 0.3, 0.6, np.inf ]

    labels = ['불가', '혼잡' ,'보통', '원활' ]

    solution_df['응급실포화도'] = pd.cut(solution_df['응급실가용율'], bins = bins, labels = labels)



    ############################################################
    # 5) 환자 수용 가능한 병원 구하기                            
    ############################################################
    
    # 매개변수 special_m로 받은 중증질환이 중증질환 리스트에 포함될 경우
    # 중증질환 리스트 :  ['뇌출혈', '뇌경색', '심근경색', '복부손상', '사지접합', '응급투석', '조산산모', '신생아','중증화상' ]
    severe_diseases = ['뇌출혈', '뇌경색', '심근경색', '복부손상', '사지접합', '응급투석', '조산산모', '신생아','중증화상']

    

    if special_m in severe_diseases:

        # 조건1 : special_m 중증질환자 수용이 가능하고

        # 조건2 : 응급실 포화도가 불가가 아닌 병원

        condition1 = (solution_df[special_m] =='Y') & (solution_df['가용수술실수'] >= 1)

        condition2 =(solution_df['응급실포화도'] != '불가')

        # 조건1, 2에 해당되는 응급의료기관 정보를 distance_df에 저장하기

        distance_df = solution[condition1 & condition2].copy()



    # 매개변수 special_m 값이 중증질환 리스트에 포함이 안되는 경우

    else:

        # 조건 1: 응급실 포화도가 불가가 아닌 병원

        condition1 = solution_df['응급실포화도'] != '불가'

       

        # 조건 1에 해당하는 응급의료기관 정보를 distance_df에 저장하기

        distance_df = solution_df[condition1].copy()



    ############################################################
    # 6) 환자와 병원간 거리 구하기                               
    ############################################################

    distance = []

    patient = (lati, long)

    

    for idx, row in distance_df.iterrows():

        distance.append(round( haversine( ( row['위도'], row['경도']), patient, unit = 'km'), 2 ))

    distance['거리'] = distance

    

    ############################################################
    # 7) 거리 구간 구하기                                       #
    ############################################################

    bins = [-np.inf, 2, 5, 10, np.inf]

    labels = ['2km이내', '5km이내', '10km이내', '10km이상']

    

    distance_df.loc[:, 거리구분] = pd.cut( distance['거리'], bins = bins, labels = labels)



    ############################################################
    # 8) 결과값 반환하기                                        #
    ############################################################

    return distance_df

+ Recent posts