Python 강좌 / collections·itertools로 집계와 반복 처리하기

collections는 집계용 컨테이너를, itertools는 반복 흐름을 조합하는 도구를 제공합니다. 짧은 코드로 바꿀 수 있다는 이유보다 데이터의 순서·소비 방식·기본값이 목적에 맞는지 확인하고 선택합니다.

Counter와 defaultdict의 역할

Counter는 항목별 등장 횟수를 세는 딕셔너리 계열 클래스입니다. defaultdict는 없는 키를 대괄호로 조회할 때 기본 팩터리로 값을 만들므로 분류별 누적에 편리합니다. 단순 get 호출과 키 생성 동작은 다를 수 있습니다.

관련 동작과 지원 조건은 collections·itertools로 집계와 반복 처리하기 관련 공식 문서에서 확인할 수 있습니다.

없는 키를 읽는 것만으로 항목이 생기는 동작이 의도에 맞지 않으면 일반 딕셔너리와 get을 사용하는 편이 명확합니다. 라이브러리가 반복문을 없애는 것이 아니라 반복의 의도를 표현하도록 도와준다고 이해합니다.

전체 예제와 실행 결과

별도의 실습 폴더에서 아래 파일을 UTF-8로 저장합니다. 가상환경을 선택한 터미널에서 python collections_demo.py 명령으로 실행합니다. macOS·Ubuntu에서 가상환경을 사용하지 않는 경우 명령 이름을 python3으로 바꿉니다.

collections_demo.py 파일의 전체 내용입니다.

from collections import Counter, defaultdict
from itertools import chain, groupby

rows = [("food", 3500), ("bus", 2500), ("food", 8000)]
counts = Counter(category for category, _ in rows)
totals = defaultdict(int)
for category, amount in rows:
    totals[category] += amount
print(sorted(counts.items()))
print(sorted(totals.items()))
print(list(chain([1, 2], [3])))
for category, group in groupby(sorted(rows), key=lambda row: row[0]):
    print(category, sum(amount for _, amount in group))

예상 결과는 다음과 같습니다.

[('bus', 1), ('food', 2)]
[('bus', 2500), ('food', 11500)]
[1, 2, 3]
bus 2500
food 11500

groupby는 인접한 묶음을 만듭니다

itertools.groupby는 같은 키가 붙어 있는 동안만 하나의 그룹으로 만듭니다. SQL의 GROUP BY처럼 흩어진 모든 같은 값을 자동으로 모으는 동작이 아닙니다. 예제는 분류가 모이도록 정렬한 뒤 그룹을 만듭니다.

그룹 이터레이터는 원본 반복 흐름을 공유하므로 다음 그룹으로 진행한 뒤 이전 그룹을 다시 읽을 수 있다고 가정하지 않습니다. 필요하면 해당 그룹을 사용하는 시점에 리스트나 집계 결과로 저장합니다.

메모리와 읽기 쉬운 선택

chain은 모음들을 순서대로 연결해 읽고 islice는 반복 결과 일부를 선택하는 데 사용할 수 있습니다. 최종적으로 list에 모두 넣으면 전체 결과 보관 메모리가 생깁니다. 무한 반복자를 사용할 때는 islice나 명시적인 종료 조건이 필요합니다.

예제에서 sorted를 제거해 food가 두 그룹으로 나뉘는지 비교한 뒤 복구합니다. 횟수를 세려면 Counter, 금액 합계는 defaultdict 또는 명시적인 딕셔너리 집계처럼 결과의 의미에 맞게 선택하세요.