이 글은 EBS 수학과 함께하는 AI 기초를 바탕으로 작성되었습니다.
글 발췌나 라이선스에 관련한 안내를 찾아볼 수 없었지만 아무리 무료로 배포된다하더라도 엄연한 출판물이므로 교재 내용을 막 퍼오기는 어렵다고 판단하였다.
사실 교재 내용도 꽤 친절하여 내가 뭐 기록을 남길만한 부분도 많지 않다. 다만 알게된 내용 몇가지를 정리하거나, 제공된 소스를 일부 바꾸어 보는 것도 괜찮을 듯하여 글로 남기고자 한다.
1. 은재 삼촌의 디저트 카페
어쨌건간, 이번 단원에서 은재가 은재 삼촌의 디저트 카페 창업을 돕기 위해 유동인구 조사를 한단다. 삼촌을 돕는 보기드문 착한 친구네
2-1 단원 초반에서는 수학의 ‘명제’와 접목시켜,
- 일평균 유동인구 200명 이상
- 반경 300미터 이내 전통차 전문점 2개 이하
- 1층 임대로 월 100만원 이하
- 인테리어 변경 가능
와 같은 명제들을 논리곱으로 연결하여 참일때만 카페를 오픈한다고 설명한다. 뭐… 명제 중요하지.
2. 생활데이터 다루기
2-1. matplotlib 사용하기
우선 matplotlib
의 그래프에서 한글을 사용하고자 하면 아래와 같이 한글폰트를 font_manager
를 통해 불러와 matplotlib.rc
에 등록해야겠다. (이 과정을 거치지 않으면 한글이 정상적으로 표시 되지 않는다.) (fname
으로 한글폰트파일의 경로를 넘겨줘야한다.)
from matplotlib import font_manager, rc
font_name = font_manager.FontProperties(fname='korean-support-fontfile.ttf').get_name()
rc('font'.family=font_name)
바로 다음과 같은 작업때문에 한글이 필요하겠다. 물론 영어로 쓰면 필요 없겠다. 도표의 이름과 x축, y축 이름을 설정해주자. 우선 matplotlib.pyplot
을 불러와야 할텐데 이름이 너무 길어지므로 plot
으로 불러왔다.
import matplotlib.pyplot as plot
plot.title('시간별 유동인구 (평균)', fontsize=16)
plot.xlabel('시간 (n시 ~ n+1시)', fontsize=10)
plot.ylabel('유동 인구 수 (명)', fontsize=12)
좋다. 이제 점을 찍어줄 차례이다.
hour_title = [ \
'00-01', '01-02', '02-03', '03-04', '04-05', \
'05-06', '06-07', '07-08', '08-09', '09-10', \
'10-11', '11-12', '12-13', '13-14', '14-15', \
'15-16', '16-17', '17-18', '18-19', '19-20', \
'20-21', '21-22', '22-23', '23-24',] # 시간 별 x축 이름
# ... 생략
plot.scatter(hour_title, avg)
plot.plot(hour_title, avg, label='총 유동인구')
pyplot.scatter
는 해당하는 점만 찍어준다.
pyplot.plot
는 그 해당하는 점을 잇는 꺾은선 그래프만 그려준다.
예제에서 나온 hour_title
은 각 시간대에 대한 이름이다. 0번째 원소는 0시부터 1시까지이므로 ‘00-01’로 그냥 설정했다.
현재 avg
가 있는 자리에는 x축 순서대로 정렬된 x값에 따른 y값이 담긴 리스트를 넘겨줘야하는데, avg에는 각 시간대별로 평균 유동인구를 담았다.
pyplot.plot
의 label
속성은 후술할pyplot.legend
때문이다.
plot.legend()
plot.show()
표현할 그래프가 하나라면 헷갈릴 염려가 없다만, 여러 그래프를 출력한다면 비교하기야 좋겠다만 뭐가뭔지 헷갈릴 만하다. 따라서 이런 그래프에는 legend, 범례를 표시하는게 맞다. plot
이든 scatter
든 label
로 연결해 놓고 legend
를 실행하면 legend가 잘 출력된다. 교재에는 matplotlib.pyplot.legend
에 대해 다루지 않았기에 kongdols-room님의 글을 참고하였다.
이후 plot.show
를 실행하여 matplot
라이브러리에 내장된 그래프 출력 프로그램을 통해 출력된 그래프를 볼 수 있다.
(당연히 여기서 GUI가 필요한 거다.)
2-2. 데이터 가져오기
2-2-1. Python 자료 유형
파이썬에서 여려 값을 묶어 저장하는 방법에는 List, Tuple, Dictionary가 있는데,
배열과 가장 비슷한 친구는 List겠다. 다만 이름에서도 알다시피 정적으로 공간이 할당되지 않고, 동적이다.
- ```append``` 로 리스트 뒤에 새로 원소를 추가하거나
- ```pop```으로 뒤 원소를 삭제하거나
- ```insert``` 로 특정 지점에 원소 추가,
- ```remove``` 로 개체를 찾아 삭제
등이 가능하고 배열처럼 []
연산자로 접근 가능하다.
Tuple은 순서쌍이라 보는 게 맞겠다. 필요에 따라 여러 값을 순서에 따라 묶어야 할때 쓸데없이 클래스를 만들거나 class pair
같은 걸 사용하지 않아도 되겠다. 뭐 tuple이랑 똑같은 거긴 하겠다. (first_value, second_value,...)
와 같이 사용하고, 원소 접근시 마찬가지로 []
연산자를 사용할 수 있다.
Dictionary는 map, record과 같은 친구인데 아래와 같이 사용한다.
person = {'name':'gildong','age':21,'marriage':False}
print(person['age'])
원소 접근시 마찬가지로 []
연산자를 사용하나, 숫자로 된 index가 아닌 ‘Key’로 접근함에 주목해야한다.
2-2-2. csv파일로부터 읽어오기
앞으로 생활데이터를 처리하면 그 양이 어마어마 할텐데 복사 붙여넣기나 일일이 코드에 삽입해줄수는 없는 노릇이다. json에서 불러오든 sql에서 검색을 하든 웹에서 스크래핑을 해오든 간에 외부에서 가져와야하는 건 분명하다.
교재에서도 그랬고, 역시 만만한게 csv인듯하다.
CSV는 Comma-Separated Values의 준말로 해당 파일형식의 확장자이기도 하다. 이름에서도 유추할 수 있듯, 값들이 그냥 comma(,)로 구분되어있을 뿐이다. (행은 개행문자로 구분되어 있겠지만)
그렇게 그냥 텍스트파일 형태로 저장되어 있다. MS Office Excel이나 다른 SpreadSheet 편집 소프트웨어가 설치되어 있다면 설치과정에서 csv 확장자가 연결되었어서 스프레드시트 문서 편집기로 열리겠다만, 그냥 메모장으로도 열린다. 열어보면 진짜 comma와 value들만 있을 뿐인 그렇게 간단한 파일 형식을 가진다.
패키지 csv는 외부패키지가 아닌 python설치시 자동으로 설치됐을 내장패키지이다. 역시 문서화가 잘 되어있다.
import csv
fcsv = open("file1.csv")
reader = csv.reader(fcsv)
for row in reader:
print(row)
#출력:
#1행1열, 1행2열, 1행3열, 1행4열
#2행1열, 2행2열, 2행3열
#3행1열, 3행2열, 3행3열, 3행4열
csv.reader
는 csv의 각 행을 iterate하는 reader객체를 내놓고, for문에서 그대로 순회하면 각 행이 comma로 구분된 값들로 출력이 된다.
한편 아래와 같이 csv.DictReader
로 dict로 읽을 수 있다. (파일 읽기도 저렇게 읽어주는게 좋겠다.)
import csv
with open("file2.csv") as fcsv:
reader = csv.DictReader(fcsv)
for row in reader:
print(row)
#출력:
#{'1행1열': '2행1열', '1행2열': '2행2열', '1행3열': '2행3열'}
#{'1행1열': '3행1열', '1행2열': '3행2열', '1행3열': '3행3열'}
#{'1행1열': '4행1열', '1행2열': '4행2열', '1행3열': '4행3열'}
csv.DictReader
의 fieldname
속성을 생략하면 csv파일의 첫 행의 각 값이 key로 사용하여 나머지 행을 dict로 mapping하게 된다. 자세한 속성은 앞서도 언급했지만 csv패키지 문서를 참조하면 좋겠다.
2-3. 데이터 처리하기
교재에서는 은재가 시간대별로 총 유동인구, 여성유동인구, 30대 이하 유동인구를 각각 조사했단다.
책이나 소스코드를 모두 긁어올수 없기에 대충 작성하였다. 책을 참고하여라.
avg = []
for hr in range(24):
num = 0
for day in range(7):
num += int(rows[day][hr]['num'])
avg.append(num/7)
csv파일로부터 데이터를 잘 읽어 요일,시간정보를 통해 접근할수 있도록 리스트에 dict행을 그대로 집어넣었었기 때문에 ‘num’이란 key로 다시 접근하고 있다.
rows로부터 정보를 읽어올때 마지막에 int
로 형변환하고 있음에 주목하여라. csv로 불러온 값은 무조건 문자열이다. 정수형 데이터로 다루고자 하면 형변환이 필요하다.
3. 예제에서 끝내지 말고…
근데 교재를 보다보면 34페이지에 우리한테는 안 알려주고 혼자 막 이것저것 출력해놨다. 게다가 페이지 하단에는 프로그램을 확장해보라고 과제까지 줬다.
3-1. 여성유동인구, 청년유동인구까지 표시하기 (2-1-09.py)
기존에 2-1-08.py에서 총 유동인구의 평균을 구하는 과정을 응용하여, 여성과 청년인구도 함께 계산하도록 하였고,
pyplot.plot
과 pyplot.scatter
의 c
속성에 색상을 주어 구분 가능하도록 하여 표시하였다.
또 앞서도 이야기 했지만 그래프가 3개가 되면서 교재에 없던 legend 를 추가하였다.
# (1~15행까지는 p.33 2-1-08.py와 동일하므로 생략)
avg = [] # 시간별 평균 유동인구
wavg = [] # 시간별 평균 여성유동인구
yavg = [] # 시간별 평균 30대이하 유동인구
for hr in range(24): # 각 시간별로 순회하여 구하기
num = wnum = ynum = 0 # 요일별 합 구할 임시변수
for day in range(7): # 요일별 순회
num += int(a[day][hr]['num']) # 해당 시간대 요일별 합 구하기
wnum += int(a[day][hr]['wnum'])
ynum += int(a[day][hr]['ynum'])
avg.append(num/7) # 요일 수로 나누어 평균을 구해 추가
wavg.append(wnum/7)
yavg.append(ynum/7)
import matplotlib.pyplot as plot # pyplot 라이브러리
from matplotlib import font_manager, rc # font manager 라이브러리
font_name = font_manager.FontProperties(fname="malgun.ttf").get_name()
rc('font', family=font_name) #한글을 출력하기 위한 폰트 로딩
plot.title('시간별 유동인구 (평균)', fontsize=16)
plot.xlabel('시간 (n시 ~ n+1시)', fontsize=10)
plot.ylabel('유동 인구 수 (명)', fontsize=12)
plot.plot(hour_title, avg, label='총 유동인구')
plot.scatter(hour_title, avg)
plot.plot(hour_title, wavg, c='green', label='여성인구') # label로 legend와 연결
plot.scatter(hour_title, wavg, c='green') # c로 색상 설정
plot.plot(hour_title, yavg, c='red', label='30대 이하 인구')
plot.scatter(hour_title, yavg, c='red')
plot.legend() # legend 표시 (label로 연결 이후)
plot.show()
3-2. 09시~21시 40세 이상과 40세 미만 주중 유동인구 (2-1-10.py)
앞서 주중 유동 인구수와 주말 유동 인구수를 비교했을 때 차이가 있었다. 시간대별 평균 유동 인구수를 주중 데이터만 사용해서 산출해 보자. (p.32)
은재가 수집한 데이터에서 오전 9시부터 오후 9시까지의 유동 인구 중 40세 이상 행인의 수와 40세 미만 행인의 수를 한꺼번에 그래프로 출력하는 프로그램으로 확장해 보자 (p.34)
이번엔 위 두가지 ‘생각해보기’에 주목하였다.
우선 각 요일별로 확인하기 위해
for day in range(7)
과 같이 7개의 모든 날을 순회하였으므로, 월화수목금, 0,1,2,3,4 그러니까 [0:5] 범위를 순회하도록 하면 되겠다.
또한 40세 미만 유동인구는 ‘ynum’ key로 접근 가능하다만, 40세 이상에 대한 정보는 없다. 대신 총 유동인구에서 40세 미만을 빼주면 되겠다.
# (1~15행까지는 p.33 2-1-08.py와 동일하므로 생략)
yavg = [] # 시간별 평균 40세 미만 유동인구
oavg = [] # 시간별 40세 이상 평균 유동인구
for hr in range(24): # 각 시간별로 순회하여 구하기
num = ynum = 0 # 요일별 합 구할 임시변수
for day in range(5): # 주중 요일별 순회 [0:5] = [0,1,2,3,4]
num += int(rows[day][hr]['num']) # 해당 시간대 요일별 합 구하기
ynum += int(rows[day][hr]['ynum'])
yavg.append(ynum/7) # 요일 수로 나누어 평균을 구해 추가
oavg.append((num-ynum)/7)
import matplotlib.pyplot as plot # pyplot 라이브러리
from matplotlib import font_manager, rc # font manager 라이브러리
font_name = font_manager.FontProperties(fname="malgun.ttf").get_name()
rc('font', family=font_name) #한글을 출력하기 위한 폰트 로딩
plot.title('시간별 유동인구 (평균)', fontsize=16)
plot.xlabel('시간 (n시 ~ n+1시)', fontsize=10)
plot.ylabel('유동 인구 수 (명)', fontsize=12)
plot.plot(hour_title[9:21], yavg[9:21], label='40세 이하')
plot.scatter(hour_title[9:21], yavg[9:21])
plot.plot(hour_title[9:21], oavg[9:21], c='green', label='40세 이상')
plot.scatter(hour_title[9:21], oavg[9:21], c='green')
plot.legend()
plot.show()
4. 더 해볼 것.
역시 또 도장이다.
파이썬 코딩도장: 웹페이지의 HTML을 가져와서 파일로 저장하기
이걸 해보면 어떨까.