Lesson 04: 파이썬의 자료형 III (dict, set)

Author

최규빈

Published

June 24, 2023

딕셔너리 기본내용

intro: str, list, tuple 정리

- str, list, tuple은 모두 시퀀스형이라는 공통점이 있다. \(\to\) 원소의 위치번호로 인덱싱이 가능

lst = [1,2,3,4]
lst[0] # 위치번호=0
1
lst[-1] # 위치번호=-1
4

- str, list, tuple은 차이점도 존재함. 잠깐 정리해보자.

시퀀스형의 카테고리

  • 컨테니어형: list, tuple
  • 균일형: str
  • 가변형: list
  • 불변형: tuple, str

표로 정리하면

컨테니어형 균일형
가변형 list .
불변형 tuple str

- 시퀀스형이 아닌 1차원 자료형도 있을까? 원소의 위치번호로 인덱싱이 불가능한 자료형

- 왜 이런게 필요할까?

  • 벡터에서 원소를 뽑는것은 정보의 모임에서 정보를 검색하는 것과 같다.
  • 정보를 순서대로 나열한뒤에 그 순서를 이용하여 검색하는 방법은 유용하다.
  • 하지만 경우에 따라서는 키워드를 기억해서 그 키워드를 바탕으로 정보에 접근하는 방법이 유용할 수 있다.

카카오톡 대화내용검색

(상황1) 오늘아침에 와이프가 뭔가를 카톡으로 부탁했었음. 그런데 그 뭔가가 기억안남.

(상황2) 개강전에 동료교수와 함께 저녁약속을 카톡으로 잡았었음. 그런데 그게 언제인지 기억안남.

(상황3) 오늘아침 동료교수와 함께 점심약속을 카톡으로 잡았었음. 그런데 그 장소가 기억나지 않음.

- 순서대로 정리된 자료를 검색할때는 시퀀스형이 유리하다. 그런데 키워드로 검색하고 싶을 경우는 딕셔너리 타입이 유리하다.

선언

dct = {'guebin': 49, 'hanni': 80}
dct 
{'guebin': 49, 'hanni': 80}

원소추출

- 원소의 위치로 추출할 수 없고, key로 추출해야 한다.

dct = {'guebin':49, 'hanni':80}
dct
{'guebin': 49, 'hanni': 80}

guebin의 점수를 추출하고 싶다면?

dct['guebin']
49

- 만약에 dict가 아니라 list로 정보를 저장했다면?

(예제) 아래와 같은 리스트에서 guebin의 점수를 추출하고 싶다면?

lst=[['guebin',49],['hanni',80]]
lst
[['guebin', 49], ['hanni', 80]]

(풀이1)

lst[0][1] # guebin의 점수를 출력하란 의미
49

(풀이2) – 진짜 최악

[lst[i][1] for i in range(len(lst)) if lst[i][0] == 'guebin']
[49]

(풀이3) – 덜 최악

[score for name,score in lst if name == 'guebin']
[49]

- ’guebin’의 점수를 추출하는 제일 쉬운 코드

dict(lst)['guebin']
49

원소추가, 변경

dct={'guebin':49, 'hanni':80}
dct
{'guebin': 49, 'hanni': 80}

- 원소에 접근: guebin의 점수 출력

dct['guebin']
49

- 추가: hynn학생의 점수를 추가

dct['hynn'] = 99
dct
{'guebin': 49, 'hanni': 80, 'hynn': 99}

- 변경: hanni의 점수를 변경

dct['hanni'] = 100 
dct
{'guebin': 49, 'hanni': 100, 'hynn': 99}

연산

- 하나있어요..

dct = {'guebin':49, 'hanni':80} 
dct
{'guebin': 49, 'hanni': 80}
'guebin' in dct
True
'hanni' in dct
True
'hynn' in dct
False

딕셔너리 특수기능

(pop)

dct = {'guebin':49, 'hanni':80} 
dct.pop('hanni')
dct
{'guebin': 49}

(get)

dct = {'guebin':49, 'hanni':80} 
dct
{'guebin': 49, 'hanni': 80}
dct.get('guebin') 
49

아래와 같은 기능

dct['guebin']
49

미묘한 차이점이 존재함

dct['hynn'] # hynn이 없어서 키에러 출력, 그런 key는 없다.. 
KeyError: 'hynn'
dct.get('hynn') # hynn이 없으면 아무것도 출력안함 

(keys,values,items)

- .keys()는 딕셔너리의 키를 리턴한다.

dct = {'guebin':49, 'hanni':80} 
dct
{'guebin': 49, 'hanni': 80}
_keys=dct.keys()
_keys
dict_keys(['guebin', 'hanni'])
type(_keys) # 리턴된 자료형은 이상한것임
dict_keys
list(_keys) # 아무튼 그 이상한 자료형도 리스트화 가능 
['guebin', 'hanni']

- .values()는 딕셔너리의 값들을 리턴한다.

_values = dct.values()
_values 
dict_values([49, 80])
type(_values)
dict_values
list(_values)
[49, 80]

- .items()는 딕셔너리의 (키,값)을 리턴한다.

_items = dct.items()
_items 
dict_items([('guebin', 49), ('hanni', 80)])
type(_items)
dict_items
list(_items)
[('guebin', 49), ('hanni', 80)]

for문과 dict (\(\star\))

dct = {'guebin': 49, 'hanni': 80}
dct
{'guebin': 49, 'hanni': 80}

(예시1)

for k in dct.keys():
    print(k)
guebin
hanni
for k in dct:
    print(k)
guebin
hanni
  • 딕셔너리 그자체도 for문에 넣을 수 있다.
  • k에는 value가 삭제되어 들어간다. (즉 key만)
  • 결과를 보면 dct 대신에 dct.keys()와 list(dct)를 넣었을때와 결과가 같다.

Note: list(dct) 하면 key만 리턴된다.

(예시2)

for v in dct.values():
    print(v)
49
80

(예시3)

for i in dct.items():
    print(i)
('guebin', 49)
('hanni', 80)

(예시4)

for k,v in dct.items():
    print(k,v)
guebin 49
hanni 80

(예시5) – {}의 중간고사 점수는 {}점 입니다.

for k,v in dct.items():
    print('{}의 중간고사 점수는 {}점입니다.'.format(k,v))
guebin의 중간고사 점수는 49점입니다.
hanni의 중간고사 점수는 80점입니다.

딕셔너리 고급내용 (\(\star\))

dict에서 key혹은 value만 뽑아내기

- 예제: 아래의 dict에서 key만 뽑아내고 싶다.

dct = {'guebin':49, 'hanni':80} 

(풀이1)

list(dct.keys())
['guebin', 'hanni']

(풀이2)

[k for k,v in dct.items()]
['guebin', 'hanni']

- 예제: 아래의 dict에서 value만 뽑아내고 싶다.

dct = {'guebin':49, 'hanni':80} 

(풀이)

[v for k,v in dct.items()]
[49, 80]

바꿔치기 (1)

- 예제1: 아래와 같은 리스트가 있다고 하자.

lst = list('abcd'*2)
lst
['a', 'b', 'c', 'd', 'a', 'b', 'c', 'd']

아래의 규칙에 의하여 lst의 각 원소의 값을 바꾸고 싶다고 하자. 이를 구현하는 코드를 작성하라.

변환전 변환후
‘a’ [1,0,0,0]
‘b’ [0,1,0,0]
‘c’ [0,0,1,0]
‘d’ [0,0,0,1]

hint: 아래의 dct를 이용할 것

lst = list('abcd'*2)
lst
['a', 'b', 'c', 'd', 'a', 'b', 'c', 'd']
dct = {'a':[1,0,0,0], 'b':[0,1,0,0], 'c':[0,0,1,0], 'd':[0,0,0,1]}
dct
{'a': [1, 0, 0, 0], 'b': [0, 1, 0, 0], 'c': [0, 0, 1, 0], 'd': [0, 0, 0, 1]}

(풀이)

[dct[x] for x in lst]
[[1, 0, 0, 0],
 [0, 1, 0, 0],
 [0, 0, 1, 0],
 [0, 0, 0, 1],
 [1, 0, 0, 0],
 [0, 1, 0, 0],
 [0, 0, 1, 0],
 [0, 0, 0, 1]]

- 예제2: 예제1을 역변환하라.

lst= [[1, 0, 0, 0], #a
      [0, 1, 0, 0], #b
      [0, 0, 1, 0], #c
      [0, 0, 0, 1], #d
      [1, 0, 0, 0],
      [0, 1, 0, 0],
      [0, 0, 1, 0],
      [0, 0, 0, 1]]
lst 
[[1, 0, 0, 0],
 [0, 1, 0, 0],
 [0, 0, 1, 0],
 [0, 0, 0, 1],
 [1, 0, 0, 0],
 [0, 1, 0, 0],
 [0, 0, 1, 0],
 [0, 0, 0, 1]]
dct = {'a':[1,0,0,0], 'b':[0,1,0,0], 'c':[0,0,1,0], 'd':[0,0,0,1]}

(풀이)

[k for l in lst for k,v in dct.items() if l == v]
['a', 'b', 'c', 'd', 'a', 'b', 'c', 'd']

바꿔치기 (2)

- 예제1: 아래와 같은 리스트를 고려하자.

lst = ['딸기','사과','바나나','딸기','사과','오토바이','자동차','버스','기차','오토바이','자동차']

다음의 맵핑규칙에 따라서 위의 리스트의 원소를 바꾸어라.

변환전 변환후
딸기 과일
사과 과일
바나나 과일
오토바이 탈것
자동차 탈것
버스 탈것
기차 탈것

(풀이)

dct = {'과일':['딸기','사과','바나나'], '탈것':['오토바이','자동차','버스','기차']}
dct
{'과일': ['딸기', '사과', '바나나'], '탈것': ['오토바이', '자동차', '버스', '기차']}
[x for l in lst for x,y in dct.items() if l in y]
['과일', '과일', '탈것']

집합 기본내용

선언

wishlist={'notebook','desktop'}
wishlist
{'desktop', 'notebook'}

원소추출

- 일단 인덱스로는 못합니다.

wishlist={'notebook','desktop'}
wishlist[0]
TypeError: 'set' object is not subscriptable

- 딱히 하는 방법이 없어요.. 그리고 이걸 하는 의미가 없어요.. (원소에 접근해서 뭐하려고??)

원소추가

- 이건 의미가 있음

wishlist={'notebook','desktop'} 
wishlist
{'desktop', 'notebook'}
wishlist.add('ipad')
wishlist
{'desktop', 'ipad', 'notebook'}
wishlist.add('notebook') # 이미 원소로 있는건 추가되지 않음. 
wishlist
{'desktop', 'ipad', 'notebook'}

원소삭제

wishlist={'desktop', 'ipad', 'notebook'}
wishlist
{'desktop', 'ipad', 'notebook'}
wishlist.remove('notebook')
wishlist
{'desktop', 'ipad'}

연산

- in 연산자

wishlist={'desktop', 'ipad', 'notebook'}
wishlist
{'desktop', 'ipad', 'notebook'}
'notebook' in wishlist
True
  • 참고로 in연산자는 집합에서만 쓰는것은 아님

- 합집합, 교집합, 차집합

day1 = {'notebook','desktop'}
day2 = {'notebook','ipad'}
day1 | day2 # 합집합
{'desktop', 'ipad', 'notebook'}
day1 & day2 # 교집합
{'notebook'}
day1 - day2 # 차집합 
{'desktop'}
day2 - day1 # 차집합
{'ipad'}

- 부분집합

day1 = {'notebook', 'desktop'}
day2 = day1 | {'ipad'} 
day1 < day2  # day1는 day2의 부분집합인가? 
True
day2 < day1
False

집합 특수기능

- 합집합

day1 = {'notebook', 'desktop'}
day2 = {'notebook','ipad'}
day1.union(day2)
{'desktop', 'ipad', 'notebook'}

- 나머지 메소드는 스스로 찾아보세요

for문과 set

day1 = {'notebook', 'desktop'}
day2 = {'notebook', 'ipad'}
for i in day1|day2: 
    print(i)
notebook
ipad
desktop

집합 고급내용

set 컴프리헨션

- 예시1

lst = [1,2,1,1,3,4,5]
{l for l in lst}
{1, 2, 3, 4, 5}

유니크한 원소

- 예제1: 아래의 list는 모두 몇 종류의 문자로 이루어져 있는가?

lst=list('asdfasssdfdsasdfasdfasdfasdf')

(풀이)

set(lst)
{'a', 'd', 'f', 's'}
len(set(lst))
4

- 예제2: 아래의 txt에서 어떠한 종류의 문자가 각각 몇번씩 사용되었는지 빈도를 구하는 코드를 작성하라.

txt = 'asdkflkjahsdlkjfhlaksglkjdhflkgjhlskdfjhglkajhsdlkfjhalsdkf'
txt
'asdkflkjahsdlkjfhlaksglkjdhflkgjhlskdfjhglkajhsdlkfjhalsdkf'

(풀이)

{k:list(txt).count(k) for k in set(txt)}
{'s': 6, 'a': 5, 'g': 3, 'k': 10, 'j': 7, 'h': 7, 'd': 6, 'l': 9, 'f': 6}

Quiz

1. 아래와 같은 맵핑을 고려하자.

문자 숫자
a 1
b 0

이를 딕셔너리로 표현하면 아래와 같다.

dct = {'a':0, 'b':1} 

위 규칙에 따라서 아래의 리스트의 원소를 문자로 각각 변환하라.

lst = [1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1]
# 출력은 아래와 같아야 한다. 
['b', 'a', 'b', 'a', 'b', 'a', 'b', 'b', 'b', 'b', 'a', 'a', 'b', 'a', 'b']

(풀이)

[(k,v) for k,v in dct.items()]
[('a', 0), ('b', 1)]
[k for l in lst for k,v in dct.items() if l == v] 
['b', 'a', 'b', 'a', 'b', 'a', 'b', 'b', 'b', 'b', 'a', 'a', 'b', 'a', 'b']

2. 아래와 같은 맵핑을 고려하자.

의미
1,2 겨울방학
3,4,5,6 1학기
7,8 여름방학
9,10,11,12 2학기

이러한 규칙에 맞게 아래의 리스트를 적절한 문자열로 변환하라.

month = [1,2,2,3,4,5,6,7,8,9,9,10,11,12] 
## 출력은 아래와 같아야 한다. 
['겨울방학', '겨울방학', '겨울방학', '1학기', '1학기', '1학기', '1학기', '여름방학', '여름방학', '2학기', '2학기', '2학기', '2학기', '2학기']

(풀이)

dct = {'겨울방학':list(range(1,3)), 
       '1학기':list(range(3,7)),
       '여름방학':list(range(7,9)),
       '2학기':list(range(9,13))}
dct
{'겨울방학': [1, 2], '1학기': [3, 4, 5, 6], '여름방학': [7, 8], '2학기': [9, 10, 11, 12]}
[k for m in month for k,v in dct.items() if m in v]
['겨울방학',
 '겨울방학',
 '겨울방학',
 '1학기',
 '1학기',
 '1학기',
 '1학기',
 '여름방학',
 '여름방학',
 '2학기',
 '2학기',
 '2학기',
 '2학기',
 '2학기']

합성변환 3-5.

아래와 같은 맵핑을 고려하자.

(규칙1)

문자 숫자
바나나 0
사과 1
오토바이 2
자동차 3
자전거 4

(규칙2)

아이템 카테고리
바나나 과일
사과 과일
오토바이 탈것
자동차 탈것
자전거 탈것

각각의 규칙을 나타내는 딕셔너리는 아래와 같이 선언되어있다고 하자.

dct1 = {'바나나':0, '사과':1, '오토바이':2, '자동차':3, '자전거':4} 
dct2 = {'과일':['바나나','사과'], '탈것':['오토바이','자동차','자전거']} 

3. 규칙1를 이용하여 아래와 같은 리스트를 변환하는 함수를 구현하고 그 함수를 f라 선언하라.

# 입력 
[0,1,0,1,4]

# 출력 
['바나나', '사과', '바나나', '사과', '자전거']

(사용예시)

f([0,1,0,1,4])
['바나나', '사과', '바나나', '사과', '자전거']

(풀이)

lst = [0,1,0,1,4]
def f(lst):
    return [k for l in lst for k,v in dct1.items() if l ==v]
f([0,1,0,1,4])
['바나나', '사과', '바나나', '사과', '자전거']

4. 규칙2를 이용하여 아래와 같이 리스트를 변환하는 함수를 구현하고 그 함수를 g라고 선언하라.

# 입력 
['바나나','바나나','바나나','자동차']

# 출력 
['과일','과일','과일','탈것']
def g(lst):
    return [k for l in lst for k,v in dct2.items() if l in v]

(사용예시)

g(['바나나','바나나','바나나','자동차'])
['과일', '과일', '과일', '탈것']

(풀이)

def g(lst):
    return [k for l in lst for k,v in dct2.items() if l in v]
g(['바나나','바나나','바나나','자동차'])
['과일', '과일', '과일', '탈것']

5. 규칙1-2를 이용하여 아래와 같은 숫자로 이루어진 입력을 ‘과일’, ‘탈것’ 중 하나로 바꾸는 코드를 구현하라.

# 입력 
[0,1,0,1,3,4,2,2,3,4,1,0]

# 출력 
['과일', '과일', '과일', '과일', '탈것', '탈것', '탈것', '탈것', '탈것', '탈것', '과일', '과일']

hint \(g(f(x))\) 를 이용하라.

(풀이)

g(f([0,1,0,1,3,4,2,2,3,4,1,0]))
['과일', '과일', '과일', '과일', '탈것', '탈것', '탈것', '탈것', '탈것', '탈것', '과일', '과일']

Oxford-III: 6–10 // reference

아래는 이미지 파일명들이 저장된 string을 불러오는 코드이다.

import requests
url = 'https://raw.githubusercontent.com/guebin/PP2023/main/posts/01_PythonBasic/Oxford-IIIT.txt'
txt = requests.get(url).content.decode()

이미지파일이 저장된 형식은 아래와 같다.

Abyssinian_1.jpg
British_Shorthair_129.jpg
{1,2,3}
{'a', 'b', 'd'}

note: British_Shorthair와 같이 종 이름 사이에 _가 들어있는 경우도 있음.

6. txt를 적당히 변환하여 아래와 같은 list를 만들어라.

lst[:10],lst[810:820]
(['Abyssinian',
  'Abyssinian',
  'Abyssinian',
  'Abyssinian',
  'Abyssinian',
  'Abyssinian',
  'Abyssinian',
  'Abyssinian',
  'Abyssinian',
  'Abyssinian'],
 ['BritishShorthair',
  'BritishShorthair',
  'BritishShorthair',
  'BritishShorthair',
  'BritishShorthair',
  'BritishShorthair',
  'BritishShorthair',
  'BritishShorthair',
  'BritishShorthair',
  'BritishShorthair'])

hint1

'Abyssinian/nAbyssinian'.split('/n')
['Abyssinian', 'Abyssinian']

hint2

''.join(['British', 'Shorthair'])
'BritishShorthair'
''.join(['Abyssinian'])
'Abyssinian'

(풀이)

lst = [''.join(l.split('_')[:-1]) for l in txt.split('\n')]
lst[:10],lst[810:820]
(['Abyssinian',
  'Abyssinian',
  'Abyssinian',
  'Abyssinian',
  'Abyssinian',
  'Abyssinian',
  'Abyssinian',
  'Abyssinian',
  'Abyssinian',
  'Abyssinian'],
 ['BritishShorthair',
  'BritishShorthair',
  'BritishShorthair',
  'BritishShorthair',
  'BritishShorthair',
  'BritishShorthair',
  'BritishShorthair',
  'BritishShorthair',
  'BritishShorthair',
  'BritishShorthair'])

7. 그림파일에는 총 몇가지 종류의 고양이와, 몇가지 종류의 강아지가 있는가?

note: 고양이사진은 대문자로 시작하고, 강아지 사진은 소문자로 시작한다.

note: 12종의 고양이, 25종의 강아지가 있음

(풀이)

sum([l[0].isupper() for l in set(lst)]) # 고양이 12
12
len(set(lst)) - sum([l[0].isupper() for l in set(lst)]) # 강아지 25
25

(풀이2)

[s[0].isupper() for s in set(lst)].count(True) # 고양이 12
12
[s[0].isupper() for s in set(lst)].count(False) # 강아지 25 
25

8. 아래는 1번의 결과로 얻어진 lst의 첫 10개의 원소와 마지막 10개의 원소이다.

lst[:10], lst[-10:]
(['Abyssinian',
  'Abyssinian',
  'Abyssinian',
  'Abyssinian',
  'Abyssinian',
  'Abyssinian',
  'Abyssinian',
  'Abyssinian',
  'Abyssinian',
  'Abyssinian'],
 ['yorkshireterrier',
  'yorkshireterrier',
  'yorkshireterrier',
  'yorkshireterrier',
  'yorkshireterrier',
  'yorkshireterrier',
  'yorkshireterrier',
  'yorkshireterrier',
  'yorkshireterrier',
  'yorkshireterrier'])

적당한 변환을 정의하여 lst를 아래와 같이 바꾸어라.

lst2[:10], lst2[-10:] # 바뀐 lst
(['cat', 'cat', 'cat', 'cat', 'cat', 'cat', 'cat', 'cat', 'cat', 'cat'],
 ['dog', 'dog', 'dog', 'dog', 'dog', 'dog', 'dog', 'dog', 'dog', 'dog'])

(풀이)

lst2= [{True:'cat', False:'dog'}[l[0].isupper()] for l in lst]
lst2[:10],lst2[-10:]
(['cat', 'cat', 'cat', 'cat', 'cat', 'cat', 'cat', 'cat', 'cat', 'cat'],
 ['dog', 'dog', 'dog', 'dog', 'dog', 'dog', 'dog', 'dog', 'dog', 'dog'])

9. txt에는 강아지사진과 고양이사진이 모두 몇장씩 들어있는가?

(풀이)

sum([l[0].isupper() for l in lst])
2403
len(lst) - sum([l[0].isupper() for l in lst])
4990

10. txt에 각 종별로 몇장의 사진이 있는지 조사하라.

## 출력예시
{'beagle': 200,
 'scottishterrier': 199,
 'newfoundland': 200,
 'Birman': 200,
 'Bombay': 200,
 'pug': 200,
 'germanshorthaired': 200,
 'samoyed': 200,
 'Sphynx': 200,
 'englishsetter': 200,
 'Bengal': 200,
 'MaineCoon': 200,
 'Persian': 200,
 'boxer': 200,
 'staffordshirebullterrier': 191,
 'Siamese': 200,
 'bassethound': 200,
 'wheatenterrier': 200,
 'englishcockerspaniel': 200,
 'Ragdoll': 200,
 'yorkshireterrier': 200,
 'EgyptianMau': 200,
 'BritishShorthair': 200,
 'keeshond': 200,
 'RussianBlue': 200,
 'saintbernard': 200,
 'americanbulldog': 200,
 'Abyssinian': 203,
 'leonberger': 200,
 'greatpyrenees': 200,
 'japanesechin': 200,
 'pomeranian': 200,
 'chihuahua': 200,
 'shibainu': 200,
 'americanpitbullterrier': 200,
 'miniaturepinscher': 200,
 'havanese': 200}

(풀이)

{l:lst.count(l) for l in set(lst)}
{'Bombay': 200,
 'scottishterrier': 199,
 'boxer': 200,
 'EgyptianMau': 200,
 'pug': 200,
 'germanshorthaired': 200,
 'newfoundland': 200,
 'saintbernard': 200,
 'Abyssinian': 203,
 'pomeranian': 200,
 'Birman': 200,
 'bassethound': 200,
 'samoyed': 200,
 'staffordshirebullterrier': 191,
 'RussianBlue': 200,
 'Bengal': 200,
 'americanpitbullterrier': 200,
 'Sphynx': 200,
 'chihuahua': 200,
 'beagle': 200,
 'greatpyrenees': 200,
 'havanese': 200,
 'shibainu': 200,
 'Ragdoll': 200,
 'keeshond': 200,
 'miniaturepinscher': 200,
 'americanbulldog': 200,
 'BritishShorthair': 200,
 'englishsetter': 200,
 'wheatenterrier': 200,
 'MaineCoon': 200,
 'yorkshireterrier': 200,
 'Siamese': 200,
 'leonberger': 200,
 'Persian': 200,
 'japanesechin': 200,
 'englishcockerspaniel': 200}