파이썬 리스트를 특정 함수 기반으로 효율적으로 그룹화하는 방법

PythonBeginner
지금 연습하기

소개

데이터 컬렉션을 구성하고 조작하는 것은 Python 프로그래밍의 기본적인 작업입니다. 흔히 사용되는 작업 중 하나는 특정 기준에 따라 리스트 요소를 그룹화하는 것입니다. 이 프로세스는 데이터를 체계적인 범주로 변환하여 분석하고 작업하기 쉽게 만듭니다.

이 튜토리얼에서는 다양한 기술을 사용하여 Python 리스트의 요소를 효율적으로 그룹화하는 방법을 배웁니다. 기본적인 접근 방식으로 시작하여 이 목적을 위해 더 강력한 내장 함수를 점차적으로 소개할 것입니다. 이 Lab 을 마치면 Python 에서 리스트 데이터를 그룹화하는 다양한 방법에 대한 실질적인 이해를 갖게 될 것입니다.

딕셔너리를 사용한 기본 리스트 그룹화

리스트 그룹화가 무엇을 의미하는지, 그리고 Python 딕셔너리를 사용하여 기본적인 그룹화 기술을 구현하는 방법을 이해하는 것부터 시작해 보겠습니다.

리스트 그룹화란 무엇인가요?

리스트 그룹화는 특정 특성 또는 함수를 기반으로 리스트 요소를 범주로 구성하는 프로세스입니다. 예를 들어, 숫자의 리스트를 짝수인지 홀수인지에 따라 그룹화하거나, 단어의 리스트를 첫 글자에 따라 그룹화할 수 있습니다.

기본 그룹화를 위한 딕셔너리 사용

Python 에서 리스트 요소를 그룹화하는 가장 간단한 방법은 딕셔너리를 사용하는 것입니다.

  • 키는 그룹을 나타냅니다.
  • 값은 각 그룹에 속하는 요소를 포함하는 리스트입니다.

숫자가 짝수인지 홀수인지에 따라 숫자를 그룹화하는 간단한 예제를 만들어 보겠습니다.

단계 1: Python 파일 생성

먼저 코드를 작성할 새 Python 파일을 만들어 보겠습니다.

  1. WebIDE 를 열고 /home/labex/project 디렉토리에 group_numbers.py라는 새 파일을 만듭니다.

  2. 파일에 다음 코드를 추가합니다.

## Basic list grouping using dictionaries
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

## Initialize empty dictionary to store our groups
even_odd_groups = {"even": [], "odd": []}

## Group numbers based on whether they are even or odd
for num in numbers:
    if num % 2 == 0:
        even_odd_groups["even"].append(num)
    else:
        even_odd_groups["odd"].append(num)

## Print the resulting groups
print("Grouping numbers by even/odd:")
print(f"Even numbers: {even_odd_groups['even']}")
print(f"Odd numbers: {even_odd_groups['odd']}")
  1. 파일을 저장합니다.

단계 2: Python 스크립트 실행

결과를 보려면 스크립트를 실행합니다.

  1. WebIDE 에서 터미널을 엽니다.

  2. 스크립트를 실행합니다.

python3 /home/labex/project/group_numbers.py

다음과 유사한 출력을 볼 수 있습니다.

Grouping numbers by even/odd:
Even numbers: [2, 4, 6, 8, 10]
Odd numbers: [1, 3, 5, 7, 9]

단계 3: 더 복잡한 기준에 따라 그룹화

이제 스크립트를 수정하여 3 으로 나눈 나머지를 기준으로 숫자를 그룹화해 보겠습니다.

  1. group_numbers.py 파일에 다음 코드를 추가합니다.
## Group numbers by remainder when divided by 3
remainder_groups = {0: [], 1: [], 2: []}

for num in numbers:
    remainder = num % 3
    remainder_groups[remainder].append(num)

print("\nGrouping numbers by remainder when divided by 3:")
for remainder, nums in remainder_groups.items():
    print(f"Numbers with remainder {remainder}: {nums}")
  1. 파일을 저장합니다.

  2. 스크립트를 다시 실행합니다.

python3 /home/labex/project/group_numbers.py

이제 추가 출력을 볼 수 있습니다.

Grouping numbers by remainder when divided by 3:
Numbers with remainder 0: [3, 6, 9]
Numbers with remainder 1: [1, 4, 7, 10]
Numbers with remainder 2: [2, 5, 8]

딕셔너리를 사용하는 이 기본 기술은 리스트 요소를 그룹화하는 간단한 방법을 제공합니다. 그러나 그룹화 요구 사항이 더 복잡해짐에 따라 Python 은 더 강력하고 효율적인 방법을 제공하며, 다음 단계에서 이를 살펴보겠습니다.

효율적인 그룹화를 위한 itertools.groupby() 사용

이제 그룹화의 기본 개념을 이해했으므로 내장 함수 itertools.groupby()를 사용하여 더 강력한 접근 방식을 살펴보겠습니다. 이 함수는 정렬된 데이터를 사용할 때 특히 유용합니다.

itertools.groupby() 이해

itertools 모듈의 groupby() 함수는 키 함수를 기반으로 반복 가능한 객체의 연속된 요소를 그룹화합니다. 이 함수는 다음 쌍을 생성하는 이터레이터를 반환합니다.

  • 키 함수에서 반환된 값
  • 그룹의 항목을 생성하는 이터레이터

중요 참고 사항: groupby()는 연속된 항목만 그룹화하므로 입력 데이터를 먼저 정렬해야 하는 경우가 많습니다.

실제로 어떻게 작동하는지 보기 위해 예제를 구현해 보겠습니다.

단계 1: 새 Python 파일 생성

  1. /home/labex/project 디렉토리에 groupby_example.py라는 새 파일을 만듭니다.

  2. 필요한 모듈을 가져오기 위해 다음 코드를 추가합니다.

import itertools

## Sample data
words = ["apple", "banana", "avocado", "blueberry", "apricot", "blackberry"]

단계 2: 첫 글자를 기준으로 단어 그룹화

이제 itertools.groupby()를 사용하여 단어를 첫 글자를 기준으로 그룹화해 보겠습니다.

  1. groupby_example.py 파일에 다음 코드를 추가합니다.
## First, we need to sort the list by the key we'll use for grouping
## In this case, the first letter of each word
words.sort(key=lambda x: x[0])
print("Sorted words:", words)

## Now group by first letter
grouped_words = {}
for first_letter, group in itertools.groupby(words, key=lambda x: x[0]):
    grouped_words[first_letter] = list(group)

## Print the resulting groups
print("\nGrouping words by first letter:")
for letter, words_group in grouped_words.items():
    print(f"Words starting with '{letter}': {words_group}")
  1. 파일을 저장합니다.

  2. 스크립트를 실행합니다.

python3 /home/labex/project/groupby_example.py

다음과 유사한 출력을 볼 수 있습니다.

Sorted words: ['apple', 'apricot', 'avocado', 'banana', 'blackberry', 'blueberry']

Grouping words by first letter:
Words starting with 'a': ['apple', 'apricot', 'avocado']
Words starting with 'b': ['banana', 'blackberry', 'blueberry']

단계 3: 정렬의 중요성 이해

groupby()를 사용할 때 정렬이 중요한 이유를 설명하기 위해 정렬하지 않고 다른 예제를 추가해 보겠습니다.

  1. groupby_example.py 파일에 다음 코드를 추가합니다.
## Sample data (unsorted)
unsorted_words = ["apple", "banana", "avocado", "blueberry", "apricot", "blackberry"]

print("\n--- Without sorting first ---")
print("Original words:", unsorted_words)

## Try to group without sorting
unsorted_grouped = {}
for first_letter, group in itertools.groupby(unsorted_words, key=lambda x: x[0]):
    unsorted_grouped[first_letter] = list(group)

print("\nGrouping without sorting:")
for letter, words_group in unsorted_grouped.items():
    print(f"Words starting with '{letter}': {words_group}")
  1. 파일을 저장합니다.

  2. 스크립트를 다시 실행합니다.

python3 /home/labex/project/groupby_example.py

출력에서 정렬하지 않고 그룹화하면 다른 결과가 생성되는 것을 알 수 있습니다.

--- Without sorting first ---
Original words: ['apple', 'banana', 'avocado', 'blueberry', 'apricot', 'blackberry']

Grouping without sorting:
Words starting with 'a': ['apple']
Words starting with 'b': ['banana']
Words starting with 'a': ['avocado']
Words starting with 'b': ['blueberry']
Words starting with 'a': ['apricot']
Words starting with 'b': ['blackberry']

동일한 키를 가진 여러 그룹이 있는 것을 확인하세요. 이는 groupby()가 연속된 항목만 그룹화하기 때문입니다. 데이터가 정렬되지 않은 경우, 동일한 키를 갖지만 리스트의 다른 위치에 나타나는 항목은 별도의 그룹에 배치됩니다.

itertools.groupby() 함수는 매우 효율적이며 표준 라이브러리의 일부이므로 많은 그룹화 작업에 강력한 도구입니다. 그러나 정렬된 데이터에 가장 적합하다는 것을 기억하십시오.

collections.defaultdict 를 사용한 그룹화

Python 에서 그룹화를 위한 또 다른 강력한 도구는 collections 모듈의 defaultdict 클래스입니다. 이 접근 방식은 일반 딕셔너리를 사용하는 것보다 더 깔끔하고 효율적인 방식으로 데이터를 그룹화하는 방법을 제공합니다.

defaultdict 이해

defaultdict는 누락된 키에 대해 자동으로 첫 번째 값을 초기화하는 딕셔너리 서브클래스입니다. 이를 통해 딕셔너리에 항목을 추가하기 전에 키가 존재하는지 확인할 필요가 없습니다. 그룹화 목적으로는 새 그룹에 대한 빈 리스트를 초기화하기 위해 조건부 코드를 작성하는 것을 피할 수 있음을 의미합니다.

defaultdict가 그룹화 프로세스를 어떻게 단순화하는지 살펴보겠습니다.

단계 1: 새 Python 파일 생성

  1. /home/labex/project 디렉토리에 defaultdict_grouping.py라는 새 파일을 만듭니다.

  2. 필요한 모듈을 가져오고 샘플 데이터를 생성하기 위해 다음 코드를 추가합니다.

from collections import defaultdict

## Sample data - a list of people with their ages
people = [
    {"name": "Alice", "age": 25, "city": "New York"},
    {"name": "Bob", "age": 30, "city": "Boston"},
    {"name": "Charlie", "age": 35, "city": "Chicago"},
    {"name": "David", "age": 25, "city": "Denver"},
    {"name": "Eve", "age": 30, "city": "Boston"},
    {"name": "Frank", "age": 35, "city": "Chicago"},
    {"name": "Grace", "age": 25, "city": "New York"}
]

단계 2: 나이별로 사람 그룹화

이제 defaultdict를 사용하여 사람들을 나이별로 그룹화해 보겠습니다.

  1. defaultdict_grouping.py 파일에 다음 코드를 추가합니다.
## Group people by age using defaultdict
age_groups = defaultdict(list)

for person in people:
    age_groups[person["age"]].append(person["name"])

## Print the resulting groups
print("Grouping people by age:")
for age, names in age_groups.items():
    print(f"Age {age}: {names}")
  1. 파일을 저장합니다.

  2. 스크립트를 실행합니다.

python3 /home/labex/project/defaultdict_grouping.py

다음과 유사한 출력을 볼 수 있습니다.

Grouping people by age:
Age 25: ['Alice', 'David', 'Grace']
Age 30: ['Bob', 'Eve']
Age 35: ['Charlie', 'Frank']

단계 3: 일반 딕셔너리 접근 방식과 비교

defaultdict를 사용하는 이점을 이해하기 위해 일반 딕셔너리 접근 방식과 비교해 보겠습니다.

  1. defaultdict_grouping.py 파일에 다음 코드를 추가합니다.
print("\n--- Comparison with regular dictionary ---")

## Using a regular dictionary (the conventional way)
regular_dict_groups = {}

for person in people:
    age = person["age"]
    name = person["name"]

    ## Need to check if the key exists
    if age not in regular_dict_groups:
        regular_dict_groups[age] = []

    regular_dict_groups[age].append(name)

print("\nRegular dictionary approach:")
for age, names in regular_dict_groups.items():
    print(f"Age {age}: {names}")
  1. 파일을 저장합니다.

  2. 스크립트를 다시 실행합니다.

python3 /home/labex/project/defaultdict_grouping.py

두 접근 방식 모두 동일한 결과를 생성하지만 defaultdict 접근 방식이 더 깔끔하고 코드가 덜 필요하다는 것을 알 수 있습니다.

단계 4: 여러 기준별로 그룹화

이제 예제를 확장하여 도시와 나이별로 사람들을 그룹화해 보겠습니다.

  1. defaultdict_grouping.py 파일에 다음 코드를 추가합니다.
## Grouping by city and then by age
city_age_groups = defaultdict(lambda: defaultdict(list))

for person in people:
    city = person["city"]
    age = person["age"]
    name = person["name"]

    city_age_groups[city][age].append(name)

print("\nGrouping people by city and then by age:")
for city, age_groups in city_age_groups.items():
    print(f"\nCity: {city}")
    for age, names in age_groups.items():
        print(f"  Age {age}: {names}")
  1. 파일을 저장합니다.

  2. 스크립트를 다시 실행합니다.

python3 /home/labex/project/defaultdict_grouping.py

다음과 유사한 추가 출력을 볼 수 있습니다.

Grouping people by city and then by age:

City: New York
  Age 25: ['Alice', 'Grace']

City: Boston
  Age 30: ['Bob', 'Eve']

City: Chicago
  Age 35: ['Charlie', 'Frank']

City: Denver
  Age 25: ['David']

이 중첩된 defaultdict 접근 방식은 최소한의 코드로 더 복잡한 그룹화 계층 구조를 허용합니다. defaultdict는 모든 그룹 키를 미리 알 수 없는 경우 특히 유용하며, 필요할 때 자동으로 새 그룹을 생성합니다.

실용적인 적용: 그룹화 기술을 사용한 데이터 분석

이제 데이터를 그룹화하는 여러 가지 방법을 이해했으므로 이러한 기술을 실제 문제에 적용하여 학생 기록 데이터 세트를 분석해 보겠습니다. 다양한 그룹화 방법을 사용하여 데이터에서 유용한 정보를 추출합니다.

예제 데이터 세트 설정

먼저 학생 기록 데이터 세트를 만들어 보겠습니다.

  1. /home/labex/project 디렉토리에 student_analysis.py라는 새 파일을 만듭니다.

  2. 다음 코드를 추가하여 예제 데이터를 설정합니다.

import itertools
from collections import defaultdict

## Sample student data
students = [
    {"id": 1, "name": "Emma", "grade": "A", "subject": "Math", "score": 95},
    {"id": 2, "name": "Noah", "grade": "B", "subject": "Math", "score": 82},
    {"id": 3, "name": "Olivia", "grade": "A", "subject": "Science", "score": 90},
    {"id": 4, "name": "Liam", "grade": "C", "subject": "Math", "score": 75},
    {"id": 5, "name": "Ava", "grade": "B", "subject": "Science", "score": 88},
    {"id": 6, "name": "William", "grade": "A", "subject": "History", "score": 96},
    {"id": 7, "name": "Sophia", "grade": "B", "subject": "History", "score": 85},
    {"id": 8, "name": "James", "grade": "C", "subject": "Science", "score": 72},
    {"id": 9, "name": "Isabella", "grade": "A", "subject": "Math", "score": 91},
    {"id": 10, "name": "Benjamin", "grade": "B", "subject": "History", "score": 84}
]

print("Student Records:")
for student in students:
    print(f"ID: {student['id']}, Name: {student['name']}, Subject: {student['subject']}, Grade: {student['grade']}, Score: {student['score']}")
  1. 파일을 저장합니다.

defaultdict 를 사용하여 과목별로 학생 그룹화

각 과목을 수강하는 학생을 분석해 보겠습니다.

  1. student_analysis.py 파일에 다음 코드를 추가합니다.
print("\n--- Students Grouped by Subject ---")

## Group students by subject using defaultdict
subject_groups = defaultdict(list)

for student in students:
    subject_groups[student["subject"]].append(student["name"])

## Print students by subject
for subject, names in subject_groups.items():
    print(f"{subject}: {names}")
  1. 파일을 저장합니다.

과목별 평균 점수 계산

각 과목의 평균 점수를 계산해 보겠습니다.

  1. student_analysis.py 파일에 다음 코드를 추가합니다.
print("\n--- Average Scores by Subject ---")

## Calculate average scores for each subject
subject_scores = defaultdict(list)

for student in students:
    subject_scores[student["subject"]].append(student["score"])

## Calculate and print averages
for subject, scores in subject_scores.items():
    average = sum(scores) / len(scores)
    print(f"{subject} Average: {average:.2f}")
  1. 파일을 저장합니다.

itertools.groupby() 를 사용하여 성적 분석

이제 itertools.groupby()를 사용하여 성적 분포를 분석해 보겠습니다.

  1. student_analysis.py 파일에 다음 코드를 추가합니다.
print("\n--- Grade Distribution (using itertools.groupby) ---")

## Sort students by grade first
sorted_students = sorted(students, key=lambda x: x["grade"])

## Group and count students by grade
grade_counts = {}
for grade, group in itertools.groupby(sorted_students, key=lambda x: x["grade"]):
    grade_counts[grade] = len(list(group))

## Print grade distribution
for grade, count in grade_counts.items():
    print(f"Grade {grade}: {count} students")
  1. 파일을 저장합니다.

기술 결합: 고급 분석

마지막으로 그룹화 기술을 결합하여 더 복잡한 분석을 수행해 보겠습니다.

  1. student_analysis.py 파일에 다음 코드를 추가합니다.
print("\n--- Advanced Analysis: Grade Distribution by Subject ---")

## Group by subject and grade
subject_grade_counts = defaultdict(lambda: defaultdict(int))

for student in students:
    subject = student["subject"]
    grade = student["grade"]
    subject_grade_counts[subject][grade] += 1

## Print detailed grade distribution by subject
for subject, grades in subject_grade_counts.items():
    print(f"\n{subject}:")
    for grade, count in grades.items():
        print(f"  Grade {grade}: {count} students")
  1. 파일을 저장합니다.

  2. 전체 스크립트를 실행합니다.

python3 /home/labex/project/student_analysis.py

다음과 같은 학생 데이터에 대한 포괄적인 분석을 볼 수 있습니다.

  • 학생 기록
  • 과목별로 그룹화된 학생
  • 과목별 평균 점수
  • 전체 성적 분포
  • 과목별 성적 분포

이 예제는 다양한 그룹화 기술을 결합하여 비교적 간단한 코드로 복잡한 데이터 분석을 수행하는 방법을 보여줍니다. 각 접근 방식에는 장점이 있습니다.

  • defaultdict는 키 존재 여부를 확인할 필요 없이 간단한 그룹화에 적합합니다.
  • itertools.groupby()는 정렬된 데이터를 처리하는 데 효율적입니다.
  • 기술을 결합하면 다단계 그룹화 및 복잡한 분석이 가능합니다.

올바른 그룹화 기술을 선택하는 것은 특정 요구 사항과 데이터 구조에 따라 다릅니다.

요약

이 튜토리얼에서는 Python 에서 리스트를 그룹화하는 몇 가지 효율적인 방법을 배웠습니다.

  1. 기본 딕셔너리 그룹화: 특정 기준에 따라 그룹을 생성하기 위해 일반 딕셔너리를 사용하는 기본적인 접근 방식으로 시작했습니다.

  2. itertools.groupby(): 정렬된 데이터에서 연속된 요소를 효율적으로 그룹화하는 이 내장 함수를 탐구하고 장점과 한계를 이해했습니다.

  3. collections.defaultdict: 누락된 키를 자동으로 처리하여 그룹화 코드를 더 깔끔하고 간결하게 만드는 이 편리한 딕셔너리 서브클래스를 사용했습니다.

  4. 실용적인 데이터 분석: 이러한 기술을 데이터 세트에 적용하여 개별적으로 또는 조합하여 의미 있는 통찰력을 추출하는 방법을 살펴보았습니다.

이러한 각 방법에는 장점과 이상적인 사용 사례가 있습니다.

  • 명확성이 간결성보다 중요한 경우 간단한 그룹화에 기본 딕셔너리를 사용합니다.
  • 데이터가 정렬되었거나 그룹화 키로 정렬할 수 있는 경우 itertools.groupby()를 사용합니다.
  • 깔끔하고 간결한 코드를 원하고 모든 그룹 키를 미리 알 수 없는 경우 defaultdict를 사용합니다.
  • 복잡한 다단계 그룹화 및 분석을 위해 기술을 결합합니다.

이러한 그룹화 기술을 마스터함으로써 Python 프로그래밍 도구 상자에 데이터를 보다 효율적으로 구성, 분석 및 조작하는 데 도움이 되는 강력한 도구를 추가했습니다.