collections 모듈은 파이썬의 자료형(list, tuple, dict)들에게 확장된 기능을 주기 위해 제작된 파이썬의 내장 모듈이다! 자주 쓰는 클래스는 3가지가 있고 알아두면 좋을만한 것 3가지도 있다.
위 세가지는 굉장히 유용하고 자주 쓰는 클래스들이다!
그리고 좀더 섬세한 사용을 위해 알아두면 좋은 클래스들이 있는데
이 페이지에서는 따로 정리하지 않았다. 선택과 집중!
먼저, 다음과 같은 코드로 임포트할 수 있다. 대문자 소문자 주의!
from collections import deque
from collections import Counter
from collections import OrderedDict
from collections import defaultdict
from collections import namedtuple
count = Counter(iterable)
deque_list = deque(deque_list)
근데 이렇게 쓰면 코드가 너무 길어져서 귀찮으므로 이런 방법도 쓸 수 있다.
import collections
count = collections.Counter([1,2,3])
deque_list = collections.deque(deque_list)
근데 이러면 뭐할때마다 collections를 앞에 붙여줘야 된다...
import 코드 복붙도 괜찮고 자주 쓰는 걸 함수화 하는 것도 좋다!
import collections
def cnts(num_list):
return collections.Counter(num_list)
a = [1,2,3]
print(cnts(a))
>>> Counter({1: 1, 2: 1, 3: 1})
각각 클래스에는 딸린 빌트인함수들이 존재하는데 이걸 사용하기 위해서는 반드시 iterable객체들을 각 클래스의 형태로 초기화시켜줄 필요가 있다. 예를 들면, 위의 코드처럼 a라는 리스트를 Counter 타입으로 초기화시켜주면 순서가 부여된 Counter 객체가 된다.
예시 : 아까 함수화한 걸로 a를 초기화해주면 Counter의 내장함수를 사용할 수 있다!
a = cnts(a)
a.update({'a' : 3, 'e' : 2})
print(a)
>>> Counter({'a': 3, 'e': 2, 1: 1, 2: 1, 3: 1})
초기화는 프로그래밍 언어에서 중요한 개념이다!
init이라고 줄여서 쓰기도 하며 자주 쓰이니 기억해두자!
오늘의 참고
https://learntutorials.net/ko/python/topic/498/%EC%BB%AC%EB%A0%89%EC%85%98-%EB%AA%A8%EB%93%88
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=ouo7581&logNo=221543909505
https://www.daleseo.com/python-collections-counter/
https://ichi.pro/ko/python-keollegsyeon-modyul-250859860348947
https://statinknu.tistory.com/12
https://ybdata-sci.tistory.com/16
collections 모듈 내에서도 딕셔너리에 특화된 클래스다.
리스트나 문자열 등 이터러블한 객체나 이터러블 객체의 집합을 받아서 값이 같은 것끼리 묶고, 그 갯수가 몇개인지를 키로 받아서 딕셔너리 형태로 리턴하는 계산기 클래스다!
# 예제 1 기본형
import collections
lst = ['aa', 'cc', 'dd', 'aa', 'bb', 'ee']
print(collections.Counter(lst))
>>> Counter({'aa': 2, 'cc': 1, 'dd': 1, 'bb': 1, 'ee': 1})
# 예제 2 class collections.Counter([iterable-or-mapping])
# 정규표현식과 연계하여 책 안에서 가장 많이 나온 단어 10개 찾기!
>>> import re
>>> words = re.findall(r'\w+', open('hamlet.txt').read().lower())
>>> Counter(words).most_common(10)
[('the', 1143), ('and', 966), ('to', 762), ('of', 669), ('i', 631),
('you', 554), ('a', 546), ('my', 514), ('hamlet', 471), ('in', 451)]
Counter 하면 요소는 딕셔너리의 키로 저장되고 갯수는 딕셔너리 값으로 반환한다!
개수는 임의의 정수값이며 0이나 음수를 포함할 수 있다!
>>>
c = Counter() # a new, empty counter
c = Counter('gallahad') # a new counter from an iterable
c = Counter({'red': 4, 'blue': 2}) # a new counter from a mapping
c = Counter(cats=4, dogs=8) # a new counter from keyword args
아래 코드처럼 딕셔너리와 달리 누락된 항목이 있어도 KeyError가 아닌 0을 반환해준다!
>>>
c = Counter(['eggs', 'ham'])
c['bacon'] # count of a missing element is zero
0
개수를 0으로 설정해도 계수기에서 요소가 제거되지 않습니다. 완전히 제거하려면 del을 사용하십시오:
>>>
c['sausage'] = 0 # counter entry with a zero count
del c['sausage'] # del actually removes the entry
버전 3.1에 추가.
버전 3.7에서 변경: As a dict subclass, Counter inherited the capability to remember insertion order. Math operations on Counter objects also preserve order. Results are ordered according to when an element is first encountered in the left operand and then by the order encountered in the right operand.
계수기 객체는 모든 딕셔너리에서 사용할 수 있는 메서드 이외의 세 가지 메서드를 지원합니다:
elements()
개수만큼 반복되는 요소에 대한 이터레이터를 반환합니다. 요소는 처음 발견되는 순서대로 반환됩니다. 요소의 개수가 1보다 작으면 elements()는 이를 무시합니다.
>>>
c = Counter(a=4, b=2, c=0, d=-2)
sorted(c.elements())
['a', 'a', 'a', 'a', 'b', 'b']
most_common([n])
n 개의 가장 흔한 요소와 그 개수를 가장 흔한 것부터 가장 적은 것 순으로 나열한 리스트를 반환합니다. n이 생략되거나 None이면, most_common()은 계수기의 모든 요소를 반환합니다. 개수가 같은 요소는 처음 발견된 순서를 유지합니다:
>>>
Counter('abracadabra').most_common(3)
[('a', 5), ('b', 2), ('r', 2)]
subtract([iterable-or-mapping])
이터러블이나 다른 매핑 (또는 계수기)으로부터 온 요소들을 뺍니다. dict.update()와 비슷하지만 교체하는 대신 개수를 뺍니다. 입력과 출력 모두 0이나 음수일 수 있습니다.
>>>
c = Counter(a=4, b=2, c=0, d=-2)
d = Counter(a=1, b=2, c=3, d=4)
c.subtract(d)
c
Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})
버전 3.2에 추가.
total()
Compute the sum of the counts.
>>>
c = Counter(a=10, b=5, c=0)
c.total()
15
버전 3.10에 추가.
일반적인 딕셔너리 메서드를 Counter 객체에 사용할 수 있습니다만, 두 메서드는 계수기에서 다르게 동작합니다.
fromkeys(iterable)
이 클래스 메서드는 Counter 객체에 구현되지 않았습니다.
update([iterable-or-mapping])
요소는 이터러블에서 세거나 다른 매핑(또는 계수기)에서 더해집니다. dict.update()와 비슷하지만, 교체하는 대신 더합니다. 또한, 이터러블은 (key, value) 쌍의 시퀀스가 아닌, 요소의 시퀀스일 것으로 기대합니다.
Counters support rich comparison operators for equality, subset, and superset relationships: ==, !=, <, <=, >, >=. All of those tests treat missing elements as having zero counts so that Counter(a=1) == Counter(a=1, b=0) returns true.
버전 3.10에 추가: Rich comparison operations were added.
버전 3.10에서 변경: In equality tests, missing elements are treated as having zero counts. Formerly, Counter(a=3) and Counter(a=3, b=0) were considered distinct.
Counter 객체로 작업하는 일반적인 패턴:
c.total() # total of all counts
c.clear() # reset all counts
list(c) # list unique elements
set(c) # convert to a set
dict(c) # convert to a regular dictionary
c.items() # convert to a list of (elem, cnt) pairs
Counter(dict(list_of_pairs)) # convert from a list of (elem, cnt) pairs
c.most_common()[:-n-1:-1] # n least common elements
+c # remove zero and negative counts
Several mathematical operations are provided for combining Counter objects to produce multisets (counters that have counts greater than zero). Addition and subtraction combine counters by adding or subtracting the counts of corresponding elements. Intersection and union return the minimum and maximum of corresponding counts. Equality and inclusion compare corresponding counts. Each operation can accept inputs with signed counts, but the output will exclude results with counts of zero or less.
>>>
>>> c = Counter(a=3, b=1)
>>> d = Counter(a=1, b=2)
>>> c + d # add two counters together: c[x] + d[x]
Counter({'a': 4, 'b': 3})
>>> c - d # subtract (keeping only positive counts)
Counter({'a': 2})
>>> c & d # intersection: min(c[x], d[x])
Counter({'a': 1, 'b': 1})
>>> c | d # union: max(c[x], d[x])
Counter({'a': 3, 'b': 2})
>>> c == d # equality: c[x] == d[x]
False
>>> c <= d # inclusion: c[x] <= d[x]
False
단항 덧셈과 뺄셈은 빈 계수기를 더하거나 빈 계수기를 빼는 것의 줄임 표현입니다.
>>>
c = Counter(a=2, b=-4)
+c
Counter({'a': 2})
-c
Counter({'b': 4})
버전 3.3에 추가: 단항 플러스, 단항 마이너스 및 제자리 멀티 셋 연산에 대한 지원이 추가되었습니다.
참고 계수기는 주로 양의 정수로 작동하여 횟수를 나타내도록 설계되었습니다; 그러나, 다른 형이나 음수 값이 필요한 사용 사례를 불필요하게 배제하지 않도록 주의를 기울였습니다. 이러한 사용 사례에 도움이 되도록, 이 절은 최소 범위와 형 제약 사항을 설명합니다.
Counter 클래스 자체는 키와 값에 제한이 없는 딕셔너리 서브 클래스입니다. 값은 개수를 나타내는 숫자로 의도되었지만, 값 필드에 어떤 것이든 저장할 수 있습니다.
most_common() 메서드는 값에 대해 순서만을 요구합니다.
c[key] += 1과 같은 제자리 연산의 경우, 값 형은 덧셈과 뺄셈만 지원하면 됩니다. 따라서 분수(fractions), 부동 소수점(floats) 및 십진수(decimals)가 작동하고 음수 값이 지원됩니다. update()와 subtract()에 대해서도 마찬가지인데, 입력과 출력 모두 음수와 0을 허용합니다.
멀티 셋(multiset) 메서드는 양의 값에 대한 사용 사례를 위해서만 설계되었습니다. 입력은 음수이거나 0일 수 있지만, 양수 값을 갖는 출력만 만들어집니다. 형 제한은 없지만, 값 형은 더하기, 빼기 및 비교를 지원해야 합니다.
elements() 메서드는 정수 개수를 요구합니다. 0과 음수 개수는 무시합니다.
더 보기
스몰토크(Smalltalk)의 Bag 클래스.
Multisets에 대한 위키피디아 항목.
예제가 포함된 C++ multisets 자습서.
멀티 셋에 대한 수학 연산과 그 사용 사례에 대해서는, Knuth, Donald. The Art of Computer Programming Volume II, Section 4.6.3, Exercise 19를 참조하십시오.
주어진 요소 집합에 대해 주어진 크기의 모든 서로 다른 멀티 셋을 열거하려면, itertools.combinations_with_replacement()를 참조하십시오:
map(Counter, combinations_with_replacement('ABC', 2)) # --> AA AB AC BB BC CC
deque 객체
class collections.deque([iterable[, maxlen]])
iterable의 데이터로 왼쪽에서 오른쪽으로 (append()를 사용해서) 초기화된 새 데크(deque) 객체를 반환합니다. iterable을 지정하지 않으면, 새 데크는 비어 있습니다.
데크는 스택과 큐를 일반화 한 것입니다 (이름은 《deck》이라고 발음하며 《double-ended queue》의 약자입니다). 데크는 스레드 안전하고 메모리 효율적인 데크의 양쪽 끝에서의 추가(append)와 팝(pop)을 양쪽에서 거의 같은 O(1) 성능으로 지원합니다.
list 객체는 유사한 연산을 지원하지만, 빠른 고정 길이 연산에 최적화되어 있으며, 하부 데이터 표현의 크기와 위치를 모두 변경하는 pop(0)과 insert(0, v) 연산에 대해 O(n) 메모리 이동 비용이 발생합니다.
maxlen이 지정되지 않거나 None이면, 데크는 임의의 길이로 커질 수 있습니다. 그렇지 않으면, 데크는 지정된 최대 길이로 제한됩니다. 일단 제한된 길이의 데크가 가득 차면, 새 항목이 추가될 때, 해당하는 수의 항목이 반대쪽 끝에서 삭제됩니다. 제한된 길이의 데크는 유닉스의 tail 필터와 유사한 기능을 제공합니다. 또한 가장 최근 활동만 관심이 있는 트랜잭션과 기타 데이터 풀을 추적하는 데 유용합니다.
deque 객체는 다음 메서드를 지원합니다:
append(x)
데크의 오른쪽에 x를 추가합니다.
appendleft(x)
데크의 왼쪽에 x를 추가합니다.
clear()
데크에서 모든 요소를 제거하고 길이가 0인 상태로 만듭니다.
copy()
데크의 얕은 복사본을 만듭니다.
버전 3.5에 추가.
count(x)
x 와 같은 데크 요소의 수를 셉니다.
버전 3.2에 추가.
extend(iterable)
iterable 인자에서 온 요소를 추가하여 데크의 오른쪽을 확장합니다.
extendleft(iterable)
iterable에서 온 요소를 추가하여 데크의 왼쪽을 확장합니다. 일련의 왼쪽 추가는 iterable 인자에 있는 요소의 순서를 뒤집는 결과를 줍니다.
index(x[, start[, stop]])
데크에 있는 x의 위치를 반환합니다 (인덱스 start 또는 그 이후, 그리고 인덱스 stop 이전). 첫 번째 일치를 반환하거나 찾을 수 없으면 ValueError를 발생시킵니다.
버전 3.5에 추가.
insert(i, x)
x를 데크의 i 위치에 삽입합니다.
삽입으로 인해 제한된 길이의 데크가 maxlen 이상으로 커지면, IndexError가 발생합니다.
버전 3.5에 추가.
pop()
데크의 오른쪽에서 요소를 제거하고 반환합니다. 요소가 없으면, IndexError를 발생시킵니다.
popleft()
데크의 왼쪽에서 요소를 제거하고 반환합니다. 요소가 없으면, IndexError를 발생시킵니다.
remove(value)
value의 첫 번째 항목을 제거합니다. 찾을 수 없으면, ValueError를 발생시킵니다.
reverse()
데크의 요소들을 제자리에서 순서를 뒤집고 None을 반환합니다.
버전 3.2에 추가.
rotate(n=1)
데크를 n 단계 오른쪽으로 회전합니다. n이 음수이면, 왼쪽으로 회전합니다.
데크가 비어 있지 않으면, 오른쪽으로 한 단계 회전하는 것은 d.appendleft(d.pop())과 동등하고, 왼쪽으로 한 단계 회전하는 것은 d.append(d.popleft())와 동등합니다.
데크 객체는 하나의 읽기 전용 어트리뷰트도 제공합니다:
maxlen
데크의 최대 크기 또는 제한이 없으면 None.
버전 3.1에 추가.
상기한 것들 외에도, 데크는 이터레이션, 피클링, len(d), reversed(d), copy.copy(d), copy.deepcopy(d), in 연산자를 사용한 멤버십 검사 및 첫 번째 요소를 액세스하는 d[0]과 같은 서브 스크립트 참조를 지원합니다. 인덱스를 사용하는 액세스는 양쪽 끝에서는 O(1) 이지만 중간에서는 O(n) 으로 느려집니다. 빠른 무작위 액세스를 위해서는 대신 리스트를 사용하십시오.
버전 3.5부터, 데크는 add(), mul() 및 imul()을 지원합니다.
예:
>>>
>>> from collections import deque
>>> d = deque('ghi') # make a new deque with three items
>>> for elem in d: # iterate over the deque's elements
... print(elem.upper())
G
H
I
>>> d.append('j') # add a new entry to the right side
>>> d.appendleft('f') # add a new entry to the left side
>>> d # show the representation of the deque
deque(['f', 'g', 'h', 'i', 'j'])
>>> d.pop() # return and remove the rightmost item
'j'
>>> d.popleft() # return and remove the leftmost item
'f'
>>> list(d) # list the contents of the deque
['g', 'h', 'i']
>>> d[0] # peek at leftmost item
'g'
>>> d[-1] # peek at rightmost item
'i'
>>> list(reversed(d)) # list the contents of a deque in reverse
['i', 'h', 'g']
>>> 'h' in d # search the deque
True
>>> d.extend('jkl') # add multiple elements at once
>>> d
deque(['g', 'h', 'i', 'j', 'k', 'l'])
>>> d.rotate(1) # right rotation
>>> d
deque(['l', 'g', 'h', 'i', 'j', 'k'])
>>> d.rotate(-1) # left rotation
>>> d
deque(['g', 'h', 'i', 'j', 'k', 'l'])
>>> deque(reversed(d)) # make a new deque in reverse order
deque(['l', 'k', 'j', 'i', 'h', 'g'])
>>> d.clear() # empty the deque
>>> d.pop() # cannot pop from an empty deque
Traceback (most recent call last):
File "<pyshell#6>", line 1, in -toplevel-
d.pop()
IndexError: pop from an empty deque
>>> d.extendleft('abc') # extendleft() reverses the input order
>>> d
deque(['c', 'b', 'a'])
deque 조리법
이 절은 데크로 작업하는 다양한 접근 방식을 보여줍니다.
제한된 길이의 데크는 유닉스의 tail 필터와 유사한 기능을 제공합니다:
def tail(filename, n=10):
'Return the last n lines of a file'
with open(filename) as f:
return deque(f, n)
데크를 사용하는 또 다른 접근법은 오른쪽에 추가하고 왼쪽에서 팝 하여 최근에 추가된 요소의 시퀀스를 유지하는 것입니다:
def moving_average(iterable, n=3):
# moving_average([40, 30, 50, 46, 39, 44]) --> 40.0 42.0 45.0 43.0
# http://en.wikipedia.org/wiki/Moving_average
it = iter(iterable)
d = deque(itertools.islice(it, n-1))
d.appendleft(0)
s = sum(d)
for elem in it:
s += elem - d.popleft()
d.append(elem)
yield s / n
라운드 로빈 스케줄러(round-robin scheduler)는 deque에 저장된 입력 이터레이터로 구현할 수 있습니다. 위치 0에 있는 활성 이터레이터에서 값이 산출됩니다. 그 이터레이터가 소진되면, popleft()로 제거할 수 있습니다; 그렇지 않으면, rotate() 메서드로 끝으로 보내 순환할 수 있습니다:
def roundrobin(*iterables):
"roundrobin('ABC', 'D', 'EF') --> A D E B F C"
iterators = deque(map(iter, iterables))
while iterators:
try:
while True:
yield next(iterators[0])
iterators.rotate(-1)
except StopIteration:
# Remove an exhausted iterator.
iterators.popleft()
rotate() 메서드는 deque 슬라이싱과 삭제를 구현하는 방법을 제공합니다. 예를 들어, del d[n]의 순수 파이썬 구현은 팝 될 요소의 위치를 잡기 위해 rotate() 메서드에 의존합니다:
def delete_nth(d, n):
d.rotate(-n)
d.popleft()
d.rotate(n)
deque 슬라이싱을 구현하려면, 대상 요소를 데크의 왼쪽으로 가져오기 위해 rotate()를 적용하는 유사한 접근법을 사용하십시오. popleft()로 이전 항목을 제거하고, extend()로 새 항목을 추가한 다음, 회전을 되돌립니다. 이 접근 방식에 약간의 변형을 가하면, dup, drop, swap, over, pick, rot 및 roll과 같은 Forth 스타일 스택 조작을 쉽게 구현할 수 있습니다.
defaultdict 객체
class collections.defaultdict(default_factory=None, /[, ...])
Return a new dictionary-like object. defaultdict is a subclass of the built-in dict class. It overrides one method and adds one writable instance variable. The remaining functionality is the same as for the dict class and is not documented here.
첫 번째 인자는 default_factory 어트리뷰트의 초깃값을 제공합니다; 기본값은 None입니다. 나머지 모든 인자는 키워드 인자를 포함하여 dict 생성자에 전달될 때와 마찬가지로 취급됩니다.
defaultdict 객체는 표준 dict 연산 외에도 다음 메서드를 지원합니다:
missing(key)
default_factory 어트리뷰트가 None이면, key를 인자로 사용하는 KeyError 예외가 발생합니다.
default_factory가 None이 아니면, 주어진 key에 대한 기본값을 제공하기 위해 인자 없이 호출되며, 반환 값은 key로 딕셔너리에 삽입되고 반환됩니다.
default_factory를 호출할 때 예외가 발생하면 이 예외는 변경되지 않고 전파됩니다.
이 메서드는 요청된 키를 찾을 수 없을 때 dict 클래스의 getitem() 메서드에 의해 호출됩니다; 이것이 반환하거나 발생시키는 모든 것은, getitem()이 반환하거나 발생시킵니다.
missing()은 getitem() 이외의 어떤 연산에서도 호출되지 않음에 유의하십시오. 이것은 get()이 일반 딕셔너리와 마찬가지로 default_factory를 사용하지 않고 None을 기본값으로 반환한다는 것을 의미합니다.
defaultdict 객체는 다음 인스턴스 변수를 지원합니다:
default_factory
이 어트리뷰트는 missing() 메서드에서 사용됩니다; 생성자의 첫 번째 인자가 있으면 그것으로, 없으면 None으로 초기화됩니다.
버전 3.9에서 변경: PEP 584에 지정된, 병합(|)과 업데이트(|=) 연산자가 추가되었습니다.
defaultdict 예
list를 default_factory로 사용하면, 키-값 쌍의 시퀀스를 리스트의 딕셔너리로 쉽게 그룹화 할 수 있습니다:
>>>
s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
d = defaultdict(list)
for k, v in s:
d[k].append(v)
sorted(d.items())
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]
각 키가 처음 발견될 때, 아직 매핑에 있지 않게 됩니다; 그래서 default_factory 함수를 사용하여 항목이 자동으로 만들어지는데, 빈 list를 반환합니다. 그런 다음 list.append() 연산이 값을 새 리스트에 추가합니다. 키를 다시 만나면, 조회가 정상적으로 진행되고 (해당 키의 리스트를 반환합니다), list.append() 연산은 다른 값을 리스트에 추가합니다. 이 기법은 dict.setdefault()를 사용하는 동등한 기법보다 간단하고 빠릅니다:
>>>
d = {}
for k, v in s:
d.setdefault(k, []).append(v)
sorted(d.items())
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]
default_factory를 int로 설정하면 defaultdict를 세는(counting) 데 유용하게 사용할 수 있습니다 (다른 언어의 백(bag)이나 멀티 셋(multiset)처럼):
>>>
s = 'mississippi'
d = defaultdict(int)
for k in s:
d[k] += 1
sorted(d.items())
[('i', 4), ('m', 1), ('p', 2), ('s', 4)]
글자가 처음 발견될 때, 매핑에서 누락되었으므로, default_factory 함수는 int()를 호출하여 기본 계수 0을 제공합니다. 증분 연산은 각 문자의 개수를 쌓아나갑니다.
항상 0을 반환하는 함수 int()는 상수 함수의 특별한 경우일 뿐입니다. 상수 함수를 만드는 더 빠르고 유연한 방법은 (단지 0이 아니라) 임의의 상숫값을 제공 할 수 있는 람다 함수를 사용하는 것입니다:
>>>
def constant_factory(value):
return lambda: value
d = defaultdict(constant_factory('<missing>'))
d.update(name='John', action='ran')
'%(name)s %(action)s to %(object)s' % d
'John ran to <missing>'
default_factory를 set으로 설정하면, defaultdict를 집합의 딕셔너리를 만드는 데 유용하게 만듭니다:
>>>
s = [('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('red', 1), ('blue', 4)]
d = defaultdict(set)
for k, v in s:
d[k].add(v)
sorted(d.items())
[('blue', {2, 4}), ('red', {1, 3})]