본문 바로가기

Python/Let's Get IT 파이썬 프로그래밍

[Let's Get IT 파이썬 프로그래밍] 10. 사용자 정의 함수

728x90
반응형

  오늘 다루는 콘텐츠는 536~592 페이지의 내용이다. 교재의 구분법에 따르면, 파트3에 접어드는 셈인데 프로그래밍한 코드를 보다 간편하게 만드는 작업들을 이번 챕터부터 소개한다고 한다. 학교를 다니며 글을 길게 쓰는 것이 어려운가, 짧게 간추리는 것이 어려운가 생각해본 적이 있을 것이다. 여러 의견이 있겠지만 개인적으로는 짧게 쓰는 것이 난이도가 높다고 본다. 마구잡이로 덧붙여 길이를 늘릴 수 있지만, 빼는 건 특정 요소를 없애버릴 경우, 글이 이어지지 않을 수 있기 때문이다. 문맥을 유지하며 요약하는 것은 쉽지 않은 스킬이다. 각설하고, 본론으로 들어가보자!


1. 키워드

이번 단원에서는 몇몇 새로운 개념이 등장한다. 그중에서도 매개변수, 인자, 지역변수, 전역변수 등은 챙겨가도록 하자.

 

2. 프로젝트 함수화

기존의 포스트에서 정리한 코드를 거듭 사용하므로 굳이 새로 입력할 것은 많지 않다. 단, 여러 코드를 만들어 긴 코드를 줄여가기에 흐름을 잘 쫓아와야 한다. 

 

2-1 파일 불러오기 함수

# load library
import csv
from openpyxl import load_workbook as lw

def load_file():
    # 생활인구 데이터
    f = open("./data/LOCAL_PEOPLE_DONG_202206.csv", 
            encoding = 'utf8')
    data = csv.reader(f)
    next(data) # 컬럼명 제거
    data = list(data)

    # 행정동 코드 데이터
    code = lw("./data/행정동코드_매핑정보_20200325.xlsx", 
                            data_only = True) # 수식 제외 셀값만 가져오기

    # 행정동코드 시트의 데이터 가져오기
    code = code['행정동코드']

    # 행과 열의 데이터 출력 
    all_cell = []

    # code 데이터를 행 단위로 가져온다.
    for r in code.rows:
        r_value = [] # 빈 셀을 만든다

        # 행 단위로 가져온 데이터에서 셀값을 추출해 리스트에 담는다.
        for cell in r:
            r_value.append(cell.value)

        # 위의 과정을 반복하며 모든 데이터를 가져온다.
        all_cell.append(r_value)

    # 컬럼을 제외한다.
    code = all_cell[2:]
    
    # 위경도 데이터
    f2 = open("./data/서울시 행정동별 전력 사용량 2008년 위치정보 (좌표계_ WGS1984).csv", 
            encoding = 'cp949')
    where = csv.reader(f2)
    next(where) # 컬럼명 제거
    where = list(where)
    
    # 행정동 데이터 자료형 변환
    for row in data:
        for i in range(32):
            if i == 0 :
                row[i] = str(row[i])
            elif i <= 2 :
                row[i] = int(row[i])
            else :
                row[i] = float(row[i])

    # 코드 데이터 자료형 변환
    for row in code:
        row[1] = int(row[1])
        
    # 위경도 데이터 자료형 변환
    for row in where:
        row[2], row[-2], row[-1] = int(row[2]), float(row[-2]), float(row[-1])  
        
    return data, code, where

 

2-2 동이름과 코드 링크 함수

def link_dong(dong):
    
    for row in code:
        if row[-1] == dong:
            dong_code = row[1]

    print(dong, "(" , dong_code , ")" + "을 살펴봅니다!")
    return dong_code

 

2-3 꺾은선 그래프 작성 함수

import matplotlib.pyplot as plt

def linegraph(pop_list, label_list, title):
    plt.rc('font', family = "Malgun Gothic")
    plt.title(title, pad = 15)

    for i in range(len(pop_list)):
        plt.plot(range(24), pop_list[i], label = label_list[i])
        
    plt.xticks(range(24), range(24))
    plt.legend(loc = "best")
    plt.show()

여러 그래프를 그리는 경우를 고려하여 반복문으로 대응할 수 있도록 이차원 리스트의 형태로 만들어준다. 그냥 대괄호가 2개로 늘어난다고 보면 된다.  

 

2-4 시간대별 인구분석

def time_pop(dong, dong_code, make_line = 'y'):
    
    pop = [0 for i in range(24)]
    
    for row in data :
        if row[2] == dong_code:
            pop[row[1]] += row[3]

    pop = [i/31 for i in pop]
    
    if make_line == 'n':
        return pop
    
    pop = [pop]
    label = ['평균 생활인구']
    title = dong + " 시간대별 평균 생활인구"
    linegraph(pop, label, title)

동이름, 동 코드, 그래프 작성 여부를 인자로 받는 사용자 정의 함수를 만들었다. 마지막 줄에 위에서 작성한 그래프 작성 함수가 들어간 것을 통해 함수 안에 함수를 사용해도 무방하다는 것을 알 수 있다.

 

2-5 주중/말 생활인구 분석

import datetime as dt

def week_pop(dong, dong_code):
    
    weekday = [0 for i in range(24)]
    weekend = [0 for i in range(24)]

    for row in data:
        if row[2] == dong_code:
            if dt.date(int(row[0][:4]), int(row[0][4:6]), int(row[0][6:])).weekday() < 5:
                weekday[row[1]] += row[3]
            else :
                weekend[row[1]] += row[3]

    # 2021년 1월의 주중/주말 일수 구하기
    day_num, end_num = 0,0

    for i in range(1,32):
        if dt.date(2021,1,i).weekday() < 5:
            day_num +=1
        else:
            end_num +=1

    weekday = [w/day_num for w in weekday]
    weekend = [w/end_num for w in weekend]
    
    tar_data = [weekday, weekend]
    label = ['주중', '주말']
    title = dong + " 주중/말 평균 생활인구"
    linegraph(tar_data, label, title)

비슷한 패턴으로 성별 인구분석도 그려보자.

 

2-6 타 행정동과의 비교

def diff_pop(dong, dong_code):
    
    # 비교할 행정동 코드 찾기
    dong2 = input("비교할 행정동은? ==> ")
    dong_code2 = link_dong(dong2)
    
    # 두 지역의 평균 생활인구 구하기
    pop_a = time_pop(dong, dong_code, 'n')
    pop_b = time_pop(dong2, dong_code2, 'n')
    
    # 꺾은선 그래프 그리기
    tar_data = [pop_a, pop_b]
    label = [dong, dong2]
    title = dong + " & " + dong2 + " 시간대별 평균 생활인구"
    linegraph(tar_data, label, title)

함수 안에 비교할 동에 관한 코드가 추가되었다.

 

2-7 지도 그리기 함수

import folium as fol
import operator as op

def topk_map(k):
      
    pop = {}

    for r in data:
        dong_code, p = r[2], r[3]

        if dong_code not in pop.keys():
            pop[dong_code] = p
        else :
            pop[dong_code] += p

    top = sorted(pop.items(), key = op.itemgetter(1), reverse = True)[:k]

    # 행정동 코드 저장
    top_code = [c[0] for c in top]
    
    top_name = [0 for i in range(k)]
    top_lat = [0 for i in range(k)]
    top_long = [0 for i in range(k)]

    for i in range(len(top)):
        
        # 행정동 이름
        for r in code:
            if r[1] == top_code[i]:
                top_name[i] = r[-1]   

        # 행정동 위경도
        for r in where:
            if r[3] == top_name[i]:
                top_lat[i], top_long[i] = r[-1], r[-2]

    for i in range(len(top_name)):
    
        # 위경도에 0인 데이터가 있으면
        if top_lat[i] == 0 or top_long[i] == 0 :

            # 7자리 행정동 코드 저장 
            for r in code:
                if top_code[i] == r[1]:
                    code7 = r[0]

            # 7자리 코드 위경도 구하기
            for r in where:
                if code7 == r[2]:
                    top_lat[i], top_long[i] = r[-1], r[-2]
        
    # 지도 그리기
    for i in range(len(top_code)):
        top_name[i] = str(i + 1) + '-' + top_name[i]
    
    topk_map = fol.Map([top_lat[0], top_long[0]], zoom_start = 10)

    for i in range(k):
        fol.Marker([top_lat[i], top_long[i]], tooltip = top_name[i],
              icon = fol.Icon(color = "purple", icon_color = "yellow", icon = "star",
                             prefix = 'fa')).add_to(topk_map)

    return topk_map

이전 포스트에서 Top 19인가를 뽑아서 그렸는데 이번에는 함수로 만들어 생활인구수 상위 K개를 그릴 수 있게 해봤다.

 

3. 함수 호출

data, code, where = load_file()

tar_dong = input("찾으려는 행정동을 입력하세요! => ")
tar_code = link_dong(tar_dong)

# 하위분석1
time_pop(tar_dong, tar_code)

# 하위분석2
week_pop(tar_dong, tar_code)

# 하위분석3
sex_pop(tar_dong, tar_code)

# 하위분석4
diff_pop(tar_dong, tar_code)

# 하위분석5
topk_map(5)

 

 

한 번에 그래프와 지도 5개가 그려질 것이다. 그중에서 몇 개 뽑아 얘기하자면, 생활인구수 1위 역삼1동은 6~10시에 걸쳐 인구수가 급상승하지만, 여의동은 5시부터 시작되어 한 시간 정도 빠른 패턴을 보인다. 이는 주식 시장의 개장같은 시간에 민감한 금융업이 밀집된 여의도의 특성이 반영된 결과로 생각된다. 


Self Check 3문제도 다 풀어놨으니 혹시 궁금한 독자는 언제나 그랬듯 내 Github에서 확인하길 바란다. 이건 서비스~!

def dan19(num):
    for i in range(num, 20):
        print(num, "x" , i , "=" , num*i)

728x90
반응형