쿼카러버의 기술 블로그

[Python Advanced] Collections 라이브러리 1탄 : Counter (Pythonic하게 Count하는 법) 본문

[Python]

[Python Advanced] Collections 라이브러리 1탄 : Counter (Pythonic하게 Count하는 법)

quokkalover 2022. 1. 20. 00:15

Python의 가장 큰 장점은 다양한 라이브러리를 사용할 수 있다는 점이다. 이들을 좀 활용해보고 정리하는 차원에서 앞으로 Python Advanced시리즈를 써보려고 한다.

우선 Collections, Itertools, Lambda 등 여러가지 라이브러리들에 대해 집중 탐구해보려고 한다.

일단 첫 시작은 Collection 모듈이다.

Collections모듈은 dict, list, set, tuple과 같은 일반적으로 사용되는 built-in container를 약간 변형해서 대체해주는 특별한 container들을 제공한다.

예:

1) Counter
2) NamedTuple,
3) orderedDict,
4) Default Dict

본 글에서는 Counter에 집중한다.

Counter

Counter는 key로 string, list와 같은 built-in container내의 아이템들을 key로, 아이템의 갯수를 value로 바꾸어 dictionary자료형에 담아주는 container다. 이렇게 말로쓰면 어려운데(나는 어려움. 독자는 아닐수도) 아래 예시를 보면 아마 5초만에 이해할 수 있을 것이다. 참고 읽어보기를 바란다.

기능

1) string, list같은 container내의 각 unique한 element들의 갯수를 파악할 수 있다.
2) Counter는 Counter끼리의 addition, subtraction, intersection, union같은 Arithmetic operation을 매우 쉽게 할 수 있도록 해준다.

용도

1) Text File 내의 단어 갯수 찾기
2) Matplotlib을 활용해서 Categorical Data 차트 그리기
3) Sample의 mode(=통계에서의 mode the most frequent value)찾기

예시

글로 설명하면 복잡하고 예시로 설명하겠다.

from collections import Counter
a = "bbbcccdfffrrr"
counter_example = Counter(a)
print(counter_example)

>>> Counter({'b': 3, 'c': 3, 'f': 3, 'r': 3, 'd': 1})

위 예시에서 보이듯이, string내 각 문자들(b, c, d, f, r)의 갯수를 새서, 각 문자를 key로 그리고, string내 각 문자들의 갯수를 value로 담고 있다.

이제 이 Counter내의 값들을 dict로 변형하면 (물론 counter자료형의 내장 함수들도 있는데, 편의상 dict로 변형해서 사용한다.)

dict_counter = dict(my_counter)
print(dict_counter)

이제 dict와 똑같이 아래 연산들을 실행해볼 수 있다.

print(dict_counter)
print(dict_counter.keys())
print(dict_counter.values())

#>>> {'b': 3, 'c': 3, 'd': 1, 'f': 3, 'r': 3}
#>>> dict_keys(['b', 'c', 'd', 'f', 'r'])
#>>> dict_values([3, 3, 1, 3, 3])

string뿐만 아니라 list도 가능하다.

my_list = ["asd", "as", "as", "cds", "cds", "bbc", "bbc", "bbc"]
counter_example = Counter(my_list)
print(counter_example)
#>>> Counter({'bbc': 3, 'as': 2, 'cds': 2, 'asd': 1})
dict_counter = dict(counter_example)

print(dict_counter)
print(dict_counter.keys())
print(dict_counter.values())
#>>> {'asd': 1, 'as': 2, 'cds': 2, 'bbc': 3}
#>>> dict_keys(['asd', 'as', 'cds', 'bbc'])
#>>> dict_values([1, 2, 2, 3])

기본적인 Counter 사용법은 위와 같고 이제 아까 말했던 Arithmetic Operation에서의 활용사례를 한번 살펴보자.

Addtion of two Counters
각 Coutner내의 key들의 갯수끼리의 덧셈을 구해줌. 하나의 Counter에만 있어도, 더해줌.

from collections import Counter
c1 = Counter('aabvbdfmfmfdds')
c2 = Counter('serosdkfgdfglsl')

print("c1:", c1)
print("c2:", c2)
print("c1 + c2 :", c1 + c2)

#>>> c1: Counter({'d': 3, 'f': 3, 'a': 2, 'b': 2, 'm': 2, 'v': 1, 's': 1})
#>>> c2: Counter({'s': 3, 'd': 2, 'f': 2, 'g': 2, 'l': 2, 'e': 1, 'r': 1, 'o': 1, 'k': 1})
#>>> c1 + c2 : Counter({'d': 5, 'f': 5, 's': 4, 'a': 2, 'b': 2, 'm': 2, 'g': 2, 'l': 2, 'v': 1, 'e': 1, 'r': 1, 'o': 1, 'k': 1})

Subtraction of two Counters
각 Counter내의 key들의 갯수의 차를 구해줌 (positive count만 남음) 장바구니로 만들고, 한번에 장바구니의 차를 뺄 수도 있음. 

from collections import Counter
t1 = Counter('aabbddffggjik')
t2 = Counter('aaabbbssshhhggdkkll')

print("t1:", t1)
print("t2:", t2)
print("t1-t2 :", t1-t2)
print("t2-t1 :", t2-t1)
>>> t1: Counter({'a': 2, 'b': 2, 'd': 2, 'f': 2, 'g': 2, 'j': 1, 'i': 1, 'k': 1})
>>> t2: Counter({'a': 3, 'b': 3, 's': 3, 'h': 3, 'g': 2, 'k': 2, 'l': 2, 'd': 1})
>>> t1-t2 : Counter({'f': 2, 'd': 1, 'j': 1, 'i': 1})
>>> t2-t1 : Counter({'s': 3, 'h': 3, 'l': 2, 'a': 1, 'b': 1, 'k': 1})

Intersections(&) of two Counters

각 counter에서 겹치는 element 갯수의 최소값을 리턴한다 min(t1[x], t2[x]):

from collections import Counter
t1 = Counter('aaabbbbccdeeee')
t2 = Counter('aabbccccdddee')

print("t1 :", t1)
print("t2 :", t2)
print("t1&t2 :", t1&t2)

#>>> t1 : Counter({'e': 4, 'b': 4, 'a': 3, 'c': 2, 'd': 1})
#>>> t2 : Counter({'c': 4, 'd': 3, 'a': 2, 'e': 2, 'b': 2})
#>>> t1&t2 : Counter({'c': 2, 'a': 2, 'e': 2, 'b': 2, 'd': 1})

Union(|) of two Counters
각 counter내의 key들의 갯수의 최대값을 리턴
한 Counter에만 있는 key는 같이 포함됨

from collections import Counter
t1 = Counter('aaabbbbccdeeeef')
t2 = Counter('aabbccccdddee')

print("t1 :", t1)
print("t2 :", t2)
print("t1|t2 :", t1|t2)
>>> t1 : Counter({'b': 4, 'e': 4, 'a': 3, 'c': 2, 'd': 1, 'f': 1})
>>> t2 : Counter({'c': 4, 'd': 3, 'a': 2, 'b': 2, 'e': 2})
>>> t1|t2 : Counter({'b': 4, 'c': 4, 'e': 4, 'a': 3, 'd': 3, 'f': 1})

Counter를 활용하여 Multiset과 set으로의 기능 동시에 활용
Python의 set은 중복된값을 허용하지 않는다. 하지만 아래처럼 Counter를 활용하면 여러개의 값을 허용하는 Multiset과 set을 동시에 만들 수 있다.

from collections import Counter

# A Python multiset
multiset = Counter([1, 1, 2, 3, 3, 3, 4, 4])
multiset
#>>>Counter({3: 3, 1: 2, 4: 2, 2: 1})

# The keys are equivalent to a set
multiset.keys() == {1, 2, 3, 4}
#>>>True

위 기능은 쇼핑카트 같은데 사용할 수 있는데, 하나의 상품을 여러개 담도록 하고 각 가격을에 대해 가격을 적용하고 계산하고싶을 때 아래 예시처럼 활용해볼 수 있겠다.

from collections import Counter

prices = {"course": 97.99, "book": 54.99, "wallpaper": 4.99}
cart = Counter(course=1, book=3, wallpaper=2)

for product, units in cart.items():
    subtotal = units * prices[product]
    price = prices[product]
    print(f"{product:9}: ${price:7.2f} × {units} = ${subtotal:7.2f}")
#>>> course   : $  97.99 × 1 = $  97.99
#>>> book     : $  54.99 × 3 = $ 164.97
#>>> wallpaper: $   4.99 × 2 = $   9.98

 

다양한 활용 기타 예시 

import math
from collections import Counter

prime_factors = Counter({2: 2, 3: 3, 17: 1})
math.prod(prime_factors.elements())
#>>> 1836

from collections import Counter

"".join(Counter("mississippi").elements())
#>>> 'miiiisssspp'

 

unary operation

from collections import Counter

counter = Counter(a=2, b=-4, c=0)

+counter
#> Counter({'a': 2})

-counter
#> Counter({'b': 4})

 

참고자료 :
https://www.guru99.com/python-counter-collections-example.html
https://www.python-engineer.com/courses/advancedpython/06-collections/
https://www.geeksforgeeks.org/operations-on-python-counter/

https://realpython.com/python-counter/

Comments