마케팅/데이터분석(파이썬)

(파이썬/python) 모두를 위한 프로그래밍 : 파이썬 챕터 9 / 딕셔너리

컬렉션

리스트나 딕셔너리 같은 변수를 가지는 상황이며 하나의 정보보다는 여러 개의 정보를 저장할때 사용

 

리스트

순서대로 정리된 컬렉션. 데이터를 추가하면 항상 리스트의 끝에 추가되고 0부터 n-1번 위치까지 순서대로 n개의 원소가 저장됨

 

딕셔너리

순서대신 키(key)가 존재함. 물건에 포스트잇으로 라벨을 붙이는 것과 같음.

딕셔너리는 다음과 같이 dict()라는 생성자를 통해 생성할 수 있음.

purse = dict() #또는 purse = {}
purse['money'] = 12 #'money'라는 키에 12라는 값 연결
purse['candy'] = 3 #'candy'라는 키에 3이라는 값 연결
purse['tissues'] = 75 #'tissues'라는 키에 75라는 값 연결

print(purse)
#{'money': 12, 'candy': 3, 'tissues': 75}

print(purse['candy'])
#3

purse['candy'] = purse['candy'] + 2
print(purse)
#{'money': 12, 'candy': 5, 'tissues': 75}

 

연관 배열

이렇게 키와 값이 연결되는 개념을 보통 연관 배열이라고 합니다. 접근하는 방식은 리스트와 비슷하지만 키를 갖고 접근한다는 차이점이 있습니다.

연관이 의미하는 것은 키와 값 사이의 연결 관계입니다.

리스트에는 위치와 값 사이에 연결 관계가 있었습니다. 그러나 위치와의 연결 관계는 비교적 덜 강력하고 덜 유연합니다.

그래서 대부분의 현대 프로그래밍 언어에는 연관 배열이라는 개념이 있습니다.

 

사람의 방식으로 이름 빈도수 세기

사람이 여러 개의 이름을 보고 그 빈도수를 세는 방식은 보통 다음과 같습니다.

 

1) 새로운 이름을 보면 목록에 추가한다.

2) 추가된 이름이 1번 나왔다는 표시를 한다.

3) 목록에 있는 이름이면 기존의 숫자에 1을 더해준다.

4) 모든 이름을 살펴본 후 표시의 갯수를 세어 가장 높은 것을 찾는다.

 

이 과정을 파이썬 코드로 다음과 같이 표현할 수 있습니다.

ccc = dict()
ccc['csev'] = 1
ccc['cwen'] = 1
print(ccc)
#{'csev':1, 'cwen':1}
ccc['cwen'] = ccc['cwen'] + 1
print(ccc)
#{'csev':1, 'cwen':2}

 

딕셔너리를 이용해 이름 빈도수 세기

그런데 만약 수백만 개의 이름이 있다면 이런 방식으로 세는 것이 매우 어려워집니다.

이때 딕셔너리를 사용하면 쉽게 해결할 수 있는데, 기본 아이디어는 다음과 같습니다.

 

1) 이미 저장되어 있는 이름인지 확인한다.

2-1) 만약 이미 저장되어 있는 이름일 경우 : 1을 더한다.

2-2) 만약 저장되지 않은 이름일 경우 : 이름을 저장하고 1을 배정한다.

3) 최종결과 중 가장 빈도가 많은 이름을 찾는다.

 

여기서 핵심은 이미 저장되어 있는 이름인지 확인하는 부분입니다.

다음과 같이 딕셔너리에 없는 키를 찾으려고 하면 오류가 발생하게 됩니다.

 

ccc = dict()
print(ccc['csev'])

#Traceback (most recent call last):
# File"<stdin>", line 1, in <module>
#KeyError: 'csev'

 

in 연산자

이런 문제를 for, list, 문자열에서 사용했던 in 연산자를 사용해 해결할 수 있습니다.

ccc라는 딕셔너리 안에 'csev'라는 값이 있는지 확인하기 위해 in 연산자를 사용하면 참 또는 거짓으로 알려줍니다.

'csev' in ccc
#False

 

리스트, in, not을 활용한 빈도 수 세기

counts = dict()
names = ['csev', 'cwen', 'csev', 'zqian', 'cwen']
for name in names :
    if name in counts :
        counts[name] = counts[name] + 1
    else :
        counts[name] = 1
print(counts)

#{'csev': 2, 'zqian': 1, 'cwen': 2}

counts = dict()
names = ['csev', 'cwen', 'csev', 'zqian', 'cwen']
for name in names :
    if name not in counts :
        counts[name] = 1
    else :
        counts[name] = counts[name] + 1
print(counts)

#{'csev': 2, 'zqian': 1, 'cwen': 2}

 

get 메소드

이와 같이 딕셔너리에 존재하는 키인지 아닌지 여부에 따라 처리하는 패턴은 get이라는 메소드를 사용하여 간결하게 해결할 수 있습니다.

 

여기에서 counts.get(name, 0)의 의미는 counts 딕셔너리에 name이라는 키가 존재할 경우 name이라는 값을 불러오고, 그렇지 않을 경우에는 counts 딕셔너리의 name이라는 키에 0이라는 값을 갖는 데이터를 추가하라는 의미입니다.

counts = dict()
names = ['csev', 'cwen', 'csev', 'zqian', 'cwen']
for name in names :
    counts[name] = counts.get(name, 0)+1
print(counts)

#{'csev': 2, 'zqian': 1, 'cwen': 2}

 

split 메소드

문자열에 split 메소드를 실행시키면 다음과 같이 띄어쓰기를 기준으로 문장을 분할해 단어들의 리스트로 만들어줍니다.

counts = dict()

line = 'The general pattern to count the words'
words = line.split()
print('Words:', words)

for word in words:
    counts[word] = counts.get(word,0) + 1
print('Counts', counts)

 

딕셔너리에 루프를 적용하는 방법 & keys, values, item 메소드 활용법

counts = {'chuck':1, 'fred':42, 'jan':100}
for key in counts:
    print(key, counts[key])

#chuck 1
#fred 42
#jan 100

jjj = {'chuck':1, 'fred':42, 'jan':100}
print(list(jjj))
#['jan', 'chuck', 'fred']

jjj = {'chuck':1, 'fred':42, 'jan':100}
print(jjj.keys())
#['jan', 'chuck', 'fred']

jjj = {'chuck':1, 'fred':42, 'jan':100}
print(jjj.values())
#[100, 1, 42]

jjj = {'chuck':1, 'fred':42, 'jan':100}
print(jjj.items())
#[('jan', 100), ('chuck', 1), ('fred', 42)]

jjj = {'chuck':1, 'fred':42, 'jan':100}
for aaa,bbb in jjj.items() :
    print(aaa, bbb)
    
#chuck 1
#fred 42
#jan 100

 

파일에 저장된 데이터 읽어와서 빈도 분석하기

name = input('Enter file:')
handle = open(name)

counts = dict()
for line in handle:
    words = line.split()
    for word in words:
        counts[word] = counts.get(word,0)+1

print(counts)

bigcount = None
bigword = None
for word,count in counts.items():
    if bigcount is None or count > bigcount:
        bigword = word
        bigcount = count

print(bigword, bigcount)

 

연습문제

fname = input('Enter File: ')
if len(fname) < 1 : fname = 'clown.txt'
hand = open(fname)

di = dict()
for lin in hand:
    lin = lin.rstrip()

    wds = lin.split()

    for w in wds:
        di[w] = di.get(w,0) + 1
#print(di)
largest = -1
theword = None
for k,v in di.items() :
    #print(k,v)
    if v > largest :
        largest = v
        theword = k

print(theword, largest)