Python 라이브러리 공부 기록 03. 데이터형
카테고리: Python
💡
파이썬에서 자주 쓰이는 라이브러리, 통계, 데이터 분석에 유용한 라이브러리들을 학습하고 정리한 페이지입니다.
관심분야인 야구에 관한 응용문제를 직접 만들어 풀어보며 학습했습니다.
03. 데이터형
03-05. collections.Counter - 동일한 요소의 개수
collections.Counter
클래스는 리스트나 문자열에서 동일한 값의 요소가 몇개 있는지 세주는 기능을 한다.
아래 고려대 사회학과 학과장 인사말에서 빈도수가 가장 높은 단어를 출력해보자.
data = """
반갑습니다. 저희 고려대 사회학과는 국내 최대 규모의 학생 수를 자랑하며, 다양한 세부
분야의 뛰어난 교수진을 보유하고 있습니다. 1963년 창설된 이래 저희 사회학과는 5천여
명에 달하는 졸업생을 배출하였으며, 이들은 정부와 기업을 비롯하여 정치, 언론, 방송,
문화, 학술, 예술 등의 분야에서 빼어난 능력을 발휘하고 있습니다. 세계화와 정보화의
급변하는 세계를 이해하고 대처하는 데 있어 사회학은 중요한 역할을 할 수 있습니다. 또한
한국사회는 압축적 고도성장의 시대가 저물어가면서 그동안 누적되어온 부작용과 새로운
사회문제의 등장으로 복합적 위기국면에 들어서고 있습니다. 저출산-고령화, 고용불안정에 따른
사회적 불안의 심화, 삶의 질과 환경에 대한 관심의 증대와 같은 전환기의 당면과제들은 다른
어떤 학문분야보다 사회학의 적극적 역할을 필요로 하고 있습니다. 사회학은 개인의 문제를
공적 이슈로 전환시키는 비판적 관점과 상상력을 제공하고, 다원적 민주사회에 필요한 성숙한
시민의식을 배양시키며, 다양한 자료를 과학적으로 분석할 수 있는 방법론을 습득케 하며,
단편적 현실을 체계적이고 깊이 있게 이해할 수 있도록 하는 다양한 이론을 제공합니다.
2014년 저희 사회학과는 학부제에서 학과제로 전환하여 학부생들의 결속력을 높이고, 학부
커리큘럼 개편을 통해 정치, 경제, 사회, 문화의 4개 영역과 관련된 다양한 수업을
제공함으로써 체계적이고 포괄적인 교육과정을 수립하였습니다. 또한 거의 모든 대학원생들이
장학금을 받고 있을 정도로, 최고의 교육환경을 제공하고자 노력하고 있습니다. 고려대
사회학과에 대한 계속적인 성원과 관심 부탁드립니다. 감사합니다.
"""
우선 문장을 단어별로 나누기 위해 re
의 findall
함수를 사용했다.
정규식에서 \w+
은 단어를 의미하므로 refindall
에 의해 data에서 모든 단어가 words
로 반환된다.
그리고 words
를 활용하여 Counter
객체를 만들고, most_common
함수를 이용해 매개변수를 1로 지정하여 가장 빈도수가 높은 한 단어를 출력한다.
from collections import Counter
import re
words = re.findall(r'\w+', data)
Counter = Counter(words)
print(Counter.most_common(1))
#결과
[('있습니다', 6)]
빈도수가 가장 높은 5개를 출력하려면 아래와 같이 한다.
print(Counter.most_common(5))
#결과
[('있습니다', 6), ('다양한', 4), ('저희', 3), ('사회학과는', 3), ('수', 3)]
03-06. collections.defaultdict
다음과 같은 문자열이 있을 때 문자열의 각 단어의 개수를 value로 갖는 딕셔너리를 만들려고 한다.
Life is too short, You need python.
{'L': 1, 'i': 2, 'f': 1, 'e': 3, ' ': 6, 's': 2, 't': 3, 'o': 5, 'h': 2, 'r': 1, ',': 1, 'Y': 1, 'u': 1, 'n': 2, 'd': 1, 'p': 1, 'y': 1, '.': 1}
클래식한 방법은 아래와 같은 반복문이다.
d = dict()
for c in text:
if c not in d:
d[c] = 0
d[c] += 1
여기서 d[c] = 0
부분은 초깃값을 설정해줌으로써 반복문이 오류가 없도록 한다.
하지만 이렇게 초깃값을 설정하지 않더라도 딕셔너리를 defaultdict()
로 생성하면 자동으로 이 과정을 생략하고 오류도 발생하지 않는다.
from collections import defaultdict
d = defaultdict(int) #초깃값을 정수로 설정해주기 위해 int 입력
for c in text:
d[c] +=1
print(d)
03-07. pprint - 예쁘게 출력하기
아래와 같은 딕셔너리는 길이가 너무 길어 가독성이 떨어진다.
result = {'userId': 1, 'id': 1, 'title': 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit', 'body': 'quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto'}
pprint
를 사용하면 이를 정돈되게 출력할 수 있다.
import pprint
pprint.pprint(result)
#결과
{'body': 'quia et suscipit\n'
'suscipit recusandae consequuntur expedita et cum\n'
'reprehenderit molestiae ut ut quas totam\n'
'nostrum rerum est autem sunt rem eveniet architecto',
'id': 1,
'title': 'sunt aut facere repellat provident occaecati excepturi optio '
'reprehenderit',
'userId': 1}
03-08. bisect - 이진 탐색
한 학급 학생들의 점수가 [33, 99, 77, 70, 89, 90, 100]라고 할 때, 등급의 기준이 아래와 같다고 하자.
- 90점 이상 : A
- 80점 이상 : B
- 70점 이상 : C
- 60점 이상 : D
- 0~59점 : F
이 문제를 해결하기 위해선 if
를 사용한 분기문을 작성하는 방법도 있지만, bisect
를 사용하면 훨씬 간편하고 우아하게 코드를 짤 수 있다.
bisect
함수의 첫번째 매개변수에는 등급의 기준을 리스트로 입력하고, 두번째 매개변수엔 점수 데이터를 입력한다.
그리고 등급명을 grade
객체에 담아주기 위해 아래처럼 코드를 작성한다.
import bisect
result = []
for score in [33, 99, 77, 70, 89, 90, 100]:
pos = bisect.bisect([60,70,80,90], score)
grade = 'FDCBA'[pos]
result.append(grade)
print(result)
만약 ‘이상’이 아니라 ‘초과’라면 bisect
함수가 아니라 bisect_left
함수를 사용하면 된다.
❓ 문제.
MLB API를 기반으로 보스턴 레드삭스 현역 투수들의 통산 볼넷삼진비율을 A~F등급으로 나눠보자.
(4.0 이상 S / 3.5 이상 A / 2.5 이상 B / 2.0 이상 C / 1.5 이상 D / 1.5 미만 F) 최종결과값은 선수이름을 key로 하고 등급을 value로 하는 딕셔너리로 만들어라.
💡 정답.
d=dict()
#2021 정규시즌 출전 선수중에
for n in statsapi.get('sports_players',{'season':2021, 'gameType':'R'})['people']:
#보스턴 선수이고 투수인 선수를 뽑아서
if n['currentTeam']['id'] == 111 and n['primaryPosition']['code'] =='1':
#이 선수의 fullname을 key로 하고 strikeoutwalkratio를 value로 하는 딕셔너리를 만든다
d[n['fullName']] = float(statsapi.player_stat_data(n['id'],'pitching','career')['stats'][0]['stats']['strikeoutWalkRatio'])
import bisect
swr = d.values() #value(볼넷삼진비율)만 리스트로 만든다
result = []
for score in swr:
pos = bisect.bisect([1.5, 2.0, 2.5, 3.5, 4.0], score)
grade = 'FDCBAS'[pos]
result.append(grade)
print(result)
i=0
result_ = dict() #최종결과를 출력할 딕셔너리를 만든다
for n in d.keys():
result_[n] = result[i] #앞서 만든 등급 리스트의 인덱스 순대로 선수 이름을 매칭시킨다
i+=1
import pprint
pprint.pprint(result_) #예쁘게 출력한다
#결과
{'Adam Ottavino': 'B',
'Austin Brice': 'C',
'Austin Davis': 'C',
'Brad Peacock': 'C',
'Brandon Brennan': 'D',
'Brandon Workman': 'C',
'Chris Sale': 'S',
'Colten Brewer': 'D',
'Connor Seabold': 'F',
'Darwinzon Hernandez': 'D',
'Eduard Bazardo': 'D',
'Eduardo Rodriguez': 'B',
'Garrett Richards': 'C',
'Garrett Whitlock': 'S',
...중략...
'Yacksel Rios': 'D'}
03-09. enum - 상수 집합
enum
은 서로 관련이 있는 여러 개의 상수 집합을 정의할 때 사용하는 모듈이다.
날짜를 입력하면 그 날짜의 요일에 해당되는 메뉴를 리턴하는 함수를 아래와 같이 정의해보자.
from datetime import date
def get_menu(input_date):
weekday = input_date.isoweekday() # 1:월요일, 2:화요일, ... , 7: 일요일
if weekday == 1:
menu = "김치찌개"
elif weekday == 2:
menu = "비빔밥"
elif weekday == 3:
menu = "된장찌개"
elif weekday == 4:
menu = "불고기"
elif weekday == 5:
menu = "갈비탕"
elif weekday == 6:
menu = "라면"
elif weekday == 7:
menu = "건빵"
return menu
print(get_menu(date(2020, 12, 6)))
print(get_menu(date(2020, 12, 18)))
#결과
건빵
갈비탕
위 코드는 요일을 나타내는 숫자 1~7이라는 매직넘버(상수로 선언하지 않은 숫자)를 사용하고 있다. 그러나 이러한 매직넘버를 코드의 가독성을 떨어뜨리기에 지양해야 한다.
from enum import IntEnum
class week(IntEnum):
MONDAY=1
TUESDAY=2
WEDNESDAY=3
THURSDAY=4
FRIDAY=5
SATURDAY=6
SUNDAY=7
def get_menu(input_date):
menu = {
week.MONDAY: "김치찌개",
week.TUESDAY: "비빔밥",
week.WEDNESDAY: "된장찌개",
week.THURSDAY: "불고기",
week.FRIDAY: "갈비탕",
week.SATURDAY: "라면",
week.SUNDAY: "건빵"
}
return menu[input_date.isoweekday()]
print(get_menu(date(2021,3,15)))
print(get_menu(date(2022,3,15)))
#결과
김치찌개
비빔밥
위와 같이 enum.IntEnum
을 상속받아 week
클래스를 만들면 유지보수에 유리하며 가독성도 좋아진다.
03-10. graphlib.TopologicalSorter - 위상정렬
- 규칙1: 주황색 화살표
- 규칙2: 초록색 화살표
- 규칙3: 파란색 화살표
영어 수업의 선수강 규칙이 위와 같다고 할 때, 5개의 수업을 어떤 순서로 수강해야 하는지를 알려고 한다.
이는 graphlib
의 TopologicalSorter
를 이용하여 위상정렬을 쓰면 쉽게 해결할 수 있다.
add(노드, 선행노드)
함수는 어떤 노드의 선행노드를 추가할 때 사용하는 함수이다. 선행노드는 여러개를 입력할 수도 있다.
from graphlib import TopologicalSorter
ts = TopologicalSorter()
#규칙1
ts.add('영어중급', '영어초급') #영어중급의 선수과목은 영어초급
ts.add('영어고급', '영어중급')
#규칙2
ts.add('영어문법', '영어중급')
ts.add('영어회화', '영어문법')
#규칙3
ts.add('영어회화', '영어문법')
print(list(ts.static_order()))
#결과
['영어초급', '영어중급', '영어문법', '영어고급', '영어회화']