[Python 100일 챌린지] Day 19 - 딕셔너리 컴프리헨션
[Python 100일 챌린지] Day 19 - 딕셔너리 컴프리헨션
여러 줄의 딕셔너리 생성 코드를 단 1줄로! 리스트 컴프리헨션에 이어 딕셔너리 컴프리헨션을 마스터해봅시다. 키-값 쌍을 간결하고 우아하게 다루는 Pythonic한 방법으로, 데이터 변환과 필터링에 필수적인 문법입니다. (24분 완독 ⭐⭐)
🎯 오늘의 학습 목표
📚 사전 지식
🎯 학습 목표 1: 딕셔너리 컴프리헨션 이해하기
딕셔너리 컴프리헨션이란?
딕셔너리 컴프리헨션 = 딕셔너리를 한 줄로 간결하게 생성하는 문법
리스트 컴프리헨션의 딕셔너리 버전으로, 키-값 쌍을 효율적으로 만들 수 있습니다.
1
2
3
4
5
6
7
8
9
# 전통적인 방법 (5줄)
squares = {}
for i in range(5):
squares[i] = i ** 2
print(squares) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# 딕셔너리 컴프리헨션 (1줄)
squares = {i: i**2 for i in range(5)}
print(squares) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
3가지 핵심 장점
| 장점 | 설명 | 예시 |
|---|---|---|
| 간결성 | 여러 줄을 한 줄로 압축 | 5줄 → 1줄 |
| 가독성 | 의도가 명확하게 드러남 | “각 숫자를 키로, 제곱을 값으로” |
| 성능 | 일반 반복문보다 빠름 | 약 20-30% 빠름 |
사용하면 좋은 경우
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# ✅ 데이터 변환
scores = {"Alice": 85, "Bob": 92, "Charlie": 78}
grades = {name: "A" if score >= 90 else "B"
for name, score in scores.items()}
# ✅ 필터링
high_scores = {name: score for name, score in scores.items()
if score >= 80}
# ✅ 딕셔너리 생성
fruits = ["apple", "banana", "grape"]
lengths = {fruit: len(fruit) for fruit in fruits}
# ✅ 키-값 뒤집기
original = {"a": 1, "b": 2, "c": 3}
reversed_dict = {v: k for k, v in original.items()}
사용하지 않는 게 좋은 경우
1
2
3
4
5
6
7
8
9
10
11
12
# ❌ 복잡한 로직 (가독성 저하)
complex = {
k: v * 2 if v > 10 else v / 2 if v < 5 else v + 1
for k, v in data.items()
if k.startswith('prefix_') and len(k) > 10
}
# 이런 경우는 일반 for문이 더 읽기 쉬움
# ❌ 중첩된 변환
nested = {k1: {k2: v2 * 2 for k2, v2 in v1.items()}
for k1, v1 in data.items()}
# 중첩은 이해하기 어려움
🎯 학습 목표 2: 기본 문법 패턴 익히기
기본 문법 구조
1
2
3
4
5
6
7
8
# 기본 형태
{키_표현식: 값_표현식 for 변수 in 반복가능객체}
# 조건 추가
{키_표현식: 값_표현식 for 변수 in 반복가능객체 if 조건}
# 값 변환 (if-else)
{키: (값1 if 조건 else 값2) for 변수 in 반복가능객체}
패턴 1: 숫자 범위로 딕셔너리 생성
1
2
3
4
5
6
7
8
9
10
11
# 숫자와 제곱
squares = {i: i**2 for i in range(5)}
print(squares) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# 숫자와 세제곱
cubes = {i: i**3 for i in range(1, 6)}
print(cubes) # {1: 1, 2: 8, 3: 27, 4: 64, 5: 125}
# 10의 배수
multiples = {i: i*10 for i in range(1, 11)}
print(multiples) # {1: 10, 2: 20, ..., 10: 100}
패턴 2: 리스트에서 딕셔너리 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 과일 이름과 길이
fruits = ["apple", "banana", "grape", "kiwi"]
lengths = {fruit: len(fruit) for fruit in fruits}
print(lengths)
# {'apple': 5, 'banana': 6, 'grape': 5, 'kiwi': 4}
# 이름과 대문자 변환
names = ["alice", "bob", "charlie"]
upper_names = {name: name.upper() for name in names}
print(upper_names)
# {'alice': 'ALICE', 'bob': 'BOB', 'charlie': 'CHARLIE'}
# 단어와 첫 글자
words = ["Python", "Java", "Ruby"]
first_chars = {word: word[0] for word in words}
print(first_chars)
# {'Python': 'P', 'Java': 'J', 'Ruby': 'R'}
패턴 3: enumerate() 활용
1
2
3
4
5
6
7
8
9
10
# 인덱스를 키로, 값을 값으로
fruits = ["apple", "banana", "grape"]
indexed = {i: fruit for i, fruit in enumerate(fruits)}
print(indexed)
# {0: 'apple', 1: 'banana', 2: 'grape'}
# 1부터 시작하는 인덱스
numbered = {i+1: fruit for i, fruit in enumerate(fruits)}
print(numbered)
# {1: 'apple', 2: 'banana', 3: 'grape'}
패턴 4: zip() 활용
1
2
3
4
5
6
7
8
9
10
11
12
13
# 두 리스트를 딕셔너리로
keys = ["name", "age", "city"]
values = ["Alice", 25, "Seoul"]
person = {k: v for k, v in zip(keys, values)}
print(person)
# {'name': 'Alice', 'age': 25, 'city': 'Seoul'}
# 학생 이름과 점수
names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]
grade_dict = {name: score for name, score in zip(names, scores)}
print(grade_dict)
# {'Alice': 85, 'Bob': 92, 'Charlie': 78}
패턴 5: 문자열 활용
1
2
3
4
5
6
7
8
9
10
# 알파벳과 인덱스
alphabet = {chr(97+i): i for i in range(26)}
print(alphabet)
# {'a': 0, 'b': 1, 'c': 2, ..., 'z': 25}
# 문자와 ASCII 코드
chars = "ABC"
ascii_codes = {char: ord(char) for char in chars}
print(ascii_codes)
# {'A': 65, 'B': 66, 'C': 67}
🎯 학습 목표 3: 조건문 활용하기
if 조건 (필터링)
딕셔너리 컴프리헨션 끝에 if 조건을 추가하여 특정 항목만 포함할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 짝수의 제곱만
evens = {i: i**2 for i in range(10) if i % 2 == 0}
print(evens)
# {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}
# 홀수의 세제곱만
odds = {i: i**3 for i in range(10) if i % 2 == 1}
print(odds)
# {1: 1, 3: 27, 5: 125, 7: 343, 9: 729}
# 3의 배수만
threes = {i: i for i in range(20) if i % 3 == 0}
print(threes)
# {0: 0, 3: 3, 6: 6, 9: 9, 12: 12, 15: 15, 18: 18}
기존 딕셔너리 필터링
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 점수 80 이상만
scores = {"Alice": 95, "Bob": 78, "Charlie": 88, "David": 65}
high_scores = {name: score for name, score in scores.items()
if score >= 80}
print(high_scores)
# {'Alice': 95, 'Charlie': 88}
# 5글자 이상 과일만
fruits = {"apple": 5, "banana": 6, "kiwi": 4, "grape": 5}
long_fruits = {fruit: length for fruit, length in fruits.items()
if length >= 5}
print(long_fruits)
# {'apple': 5, 'banana': 6, 'grape': 5}
# 양수만
numbers = {"a": -5, "b": 3, "c": -2, "d": 7}
positives = {k: v for k, v in numbers.items() if v > 0}
print(positives)
# {'b': 3, 'd': 7}
if-else (값 변환)
조건에 따라 다른 값을 할당할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 짝수/홀수 구분
numbers = {i: ("짝수" if i % 2 == 0 else "홀수")
for i in range(5)}
print(numbers)
# {0: '짝수', 1: '홀수', 2: '짝수', 3: '홀수', 4: '짝수'}
# 점수를 등급으로 변환
scores = {"Alice": 95, "Bob": 78, "Charlie": 88, "David": 65}
grades = {name: ("A" if score >= 90 else
"B" if score >= 80 else
"C" if score >= 70 else "D")
for name, score in scores.items()}
print(grades)
# {'Alice': 'A', 'Bob': 'C', 'Charlie': 'B', 'David': 'D'}
# 온도를 상태로 변환
temps = {"Seoul": 25, "Busan": 30, "Jeju": 28}
status = {city: ("뜨거움" if temp >= 30 else
"따뜻함" if temp >= 20 else "추움")
for city, temp in temps.items()}
print(status)
# {'Seoul': '따뜻함', 'Busan': '뜨거움', 'Jeju': '따뜻함'}
복잡한 조건
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 짝수이면서 3의 배수
special = {i: i**2 for i in range(20)
if i % 2 == 0 and i % 3 == 0}
print(special)
# {0: 0, 6: 36, 12: 144, 18: 324}
# 특정 문자로 시작하는 이름만
names = {"Alice": 25, "Bob": 30, "Anna": 28, "Alex": 35}
a_names = {name: age for name, age in names.items()
if name.startswith("A")}
print(a_names)
# {'Alice': 25, 'Anna': 28, 'Alex': 35}
# 길이가 5 이상이고 모음으로 시작
fruits = ["apple", "banana", "kiwi", "orange", "grape"]
vowel_fruits = {fruit: len(fruit) for fruit in fruits
if len(fruit) >= 5 and fruit[0] in "aeiou"}
print(vowel_fruits)
# {'apple': 5, 'orange': 6}
🎯 학습 목표 4: 딕셔너리 변환 기법
키-값 뒤집기 (Reverse)
딕셔너리의 키와 값을 바꾸는 패턴입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 기본 뒤집기
original = {"a": 1, "b": 2, "c": 3}
reversed_dict = {v: k for k, v in original.items()}
print(reversed_dict)
# {1: 'a', 2: 'b', 3: 'c'}
# 학생 이름 ↔ 번호
students = {"Alice": 1, "Bob": 2, "Charlie": 3}
numbers = {num: name for name, num in students.items()}
print(numbers)
# {1: 'Alice', 2: 'Bob', 3: 'Charlie'}
# 도시 ↔ 국가
cities = {"Seoul": "Korea", "Tokyo": "Japan", "Paris": "France"}
countries = {country: city for city, country in cities.items()}
print(countries)
# {'Korea': 'Seoul', 'Japan': 'Tokyo', 'France': 'Paris'}
주의: 값이 중복되면 마지막 값만 남습니다!
1
2
3
4
5
# 중복 값 문제
original = {"a": 1, "b": 1, "c": 2}
reversed_dict = {v: k for k, v in original.items()}
print(reversed_dict)
# {1: 'b', 2: 'c'} # "a"는 사라짐!
키 또는 값 변환
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 키를 대문자로 변환
data = {"name": "Alice", "age": 25, "city": "Seoul"}
upper_keys = {k.upper(): v for k, v in data.items()}
print(upper_keys)
# {'NAME': 'Alice', 'AGE': 25, 'CITY': 'Seoul'}
# 값을 문자열로 변환
numbers = {"a": 1, "b": 2, "c": 3}
strings = {k: str(v) for k, v in numbers.items()}
print(strings)
# {'a': '1', 'b': '2', 'c': '3'}
# 키에 접두사 추가
scores = {"math": 85, "english": 92, "science": 78}
prefixed = {f"score_{k}": v for k, v in scores.items()}
print(prefixed)
# {'score_math': 85, 'score_english': 92, 'score_science': 78}
딕셔너리 병합 및 필터링
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 두 딕셔너리 병합
dict1 = {"a": 1, "b": 2}
dict2 = {"c": 3, "d": 4}
merged = {**dict1, **dict2}
print(merged)
# {'a': 1, 'b': 2, 'c': 3, 'd': 4}
# 특정 키만 추출
person = {"name": "Alice", "age": 25, "city": "Seoul", "country": "Korea"}
basic_info = {k: v for k, v in person.items() if k in ["name", "age"]}
print(basic_info)
# {'name': 'Alice', 'age': 25}
# 특정 키 제외
filtered = {k: v for k, v in person.items() if k != "country"}
print(filtered)
# {'name': 'Alice', 'age': 25, 'city': 'Seoul'}
중첩 딕셔너리 다루기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 중첩 딕셔너리의 특정 값 추출
students = {
"Alice": {"math": 85, "english": 92},
"Bob": {"math": 78, "english": 88},
"Charlie": {"math": 95, "english": 82}
}
# 수학 점수만 추출
math_scores = {name: scores["math"]
for name, scores in students.items()}
print(math_scores)
# {'Alice': 85, 'Bob': 78, 'Charlie': 95}
# 평균 점수 계산
averages = {name: sum(scores.values()) / len(scores)
for name, scores in students.items()}
print(averages)
# {'Alice': 88.5, 'Bob': 83.0, 'Charlie': 88.5}
🎯 학습 목표 5: 실전 활용 패턴
패턴 1: 단어 빈도수 계산
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 단어 빈도수
text = "hello world hello python world hello"
words = text.split()
# 방법 1: count() 사용 (간단하지만 느림)
frequency = {word: words.count(word) for word in set(words)}
print(frequency)
# {'hello': 3, 'world': 2, 'python': 1}
# 방법 2: get() 사용 (효율적)
frequency = {}
for word in words:
frequency[word] = frequency.get(word, 0) + 1
print(frequency)
# {'hello': 3, 'world': 2, 'python': 1}
패턴 2: 그룹화 (Grouping)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 학생들을 등급별로 그룹화
students = {"Alice": "A", "Bob": "B", "Charlie": "A", "David": "C"}
# 등급별 학생 리스트
grade_groups = {}
for name, grade in students.items():
if grade not in grade_groups:
grade_groups[grade] = []
grade_groups[grade].append(name)
print(grade_groups)
# {'A': ['Alice', 'Charlie'], 'B': ['Bob'], 'C': ['David']}
# 딕셔너리 컴프리헨션 활용
grades = set(students.values())
grade_groups = {grade: [name for name, g in students.items() if g == grade]
for grade in grades}
print(grade_groups)
# {'A': ['Alice', 'Charlie'], 'B': ['Bob'], 'C': ['David']}
패턴 3: 데이터 정규화
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 점수 정규화 (0-100 → 0-1)
scores = {"Alice": 85, "Bob": 92, "Charlie": 78}
max_score = max(scores.values())
normalized = {name: score / max_score for name, score in scores.items()}
print(normalized)
# {'Alice': 0.924, 'Bob': 1.0, 'Charlie': 0.848}
# 가격 할인 적용
prices = {"apple": 1000, "banana": 500, "grape": 2000}
discount = 0.1 # 10% 할인
discounted = {item: int(price * (1 - discount))
for item, price in prices.items()}
print(discounted)
# {'apple': 900, 'banana': 450, 'grape': 1800}
패턴 4: 데이터 검증
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 유효한 이메일만 필터링
emails = {
"user1": "alice@example.com",
"user2": "bob@invalid",
"user3": "charlie@test.com"
}
valid_emails = {k: v for k, v in emails.items() if "@" in v and "." in v}
print(valid_emails)
# {'user1': 'alice@example.com', 'user3': 'charlie@test.com'}
# 범위 내 값만 유지
data = {"a": 5, "b": 15, "c": 25, "d": 35}
in_range = {k: v for k, v in data.items() if 10 <= v <= 30}
print(in_range)
# {'b': 15, 'c': 25}
패턴 5: 환경 설정 파싱
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 환경 변수 파싱
env_vars = [
"DB_HOST=localhost",
"DB_PORT=5432",
"DB_NAME=mydb"
]
config = {var.split("=")[0]: var.split("=")[1] for var in env_vars}
print(config)
# {'DB_HOST': 'localhost', 'DB_PORT': '5432', 'DB_NAME': 'mydb'}
# 타입 변환 포함
config_with_types = {
var.split("=")[0]: int(var.split("=")[1])
if var.split("=")[1].isdigit()
else var.split("=")[1]
for var in env_vars
}
print(config_with_types)
# {'DB_HOST': 'localhost', 'DB_PORT': 5432, 'DB_NAME': 'mydb'}
💡 실전 팁 & 주의사항
Tip 1: items() vs keys() vs values()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
data = {"a": 1, "b": 2, "c": 3}
# ✅ items() - 키와 값 모두 필요
result1 = {k: v*2 for k, v in data.items()}
print(result1) # {'a': 2, 'b': 4, 'c': 6}
# ✅ keys() - 키만 필요 (생략 가능)
result2 = {k: len(k) for k in data.keys()}
result3 = {k: len(k) for k in data} # 동일
print(result2) # {'a': 1, 'b': 1, 'c': 1}
# ✅ values() - 값만 필요 (딕셔너리로는 불가능, 리스트로)
result4 = [v*2 for v in data.values()]
print(result4) # [2, 4, 6]
Tip 2: 중복 키 처리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 중복 키가 있으면 마지막 값이 유지됨
items = [("a", 1), ("b", 2), ("a", 3)]
result = {k: v for k, v in items}
print(result)
# {'a': 3, 'b': 2} # "a"의 값이 3으로 덮어씌워짐
# 중복 키의 값을 리스트로 모으기
result_list = {}
for k, v in items:
if k in result_list:
result_list[k].append(v)
else:
result_list[k] = [v]
print(result_list)
# {'a': [1, 3], 'b': [2]}
Tip 3: 기본값 설정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# get() 활용
keys = ["a", "b", "c", "d"]
data = {"a": 1, "c": 3}
# ❌ KeyError 발생 가능
# result = {k: data[k] for k in keys} # KeyError!
# ✅ get()으로 기본값 설정
result = {k: data.get(k, 0) for k in keys}
print(result)
# {'a': 1, 'b': 0, 'c': 3, 'd': 0}
# ✅ 존재하는 키만 포함
result = {k: data[k] for k in keys if k in data}
print(result)
# {'a': 1, 'c': 3}
Tip 4: 복잡한 표현식 분리하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# ❌ 가독성 나쁨
complex = {k: v*2 if v > 10 else v/2 if v < 5 else v+1
for k, v in data.items()}
# ✅ 함수로 분리
def transform_value(v):
if v > 10:
return v * 2
elif v < 5:
return v / 2
else:
return v + 1
simple = {k: transform_value(v) for k, v in data.items()}
Tip 5: 성능 고려사항
1
2
3
4
5
6
7
8
9
10
11
12
# ❌ count()는 매번 전체 리스트를 순회 (O(n²))
words = ["apple", "banana", "apple", "grape", "apple"]
frequency = {word: words.count(word) for word in set(words)}
# ✅ 딕셔너리로 한 번만 순회 (O(n))
frequency = {}
for word in words:
frequency[word] = frequency.get(word, 0) + 1
# ✅ Counter 사용 (가장 효율적)
from collections import Counter
frequency = dict(Counter(words))
Tip 6: None 값 처리
1
2
3
4
5
6
7
8
9
10
11
data = {"a": 1, "b": None, "c": 3, "d": None}
# None이 아닌 값만 포함
filtered = {k: v for k, v in data.items() if v is not None}
print(filtered)
# {'a': 1, 'c': 3}
# None을 0으로 대체
replaced = {k: (v if v is not None else 0) for k, v in data.items()}
print(replaced)
# {'a': 1, 'b': 0, 'c': 3, 'd': 0}
🧪 연습 문제
문제 1: 단어 빈도 분석 및 필터링 시스템
과제: 텍스트에서 단어의 빈도를 분석하고, 다양한 기준으로 필터링하는 프로그램을 작성하세요. 딕셔너리 컴프리헨션의 여러 패턴을 활용합니다.
초기 데이터:
1
2
3
4
5
text = """
Python is easy Python is powerful Python is fun
Learn Python today Start your Python journey
Python programming Python development
"""
요구사항:
- 모든 단어를 소문자로 변환하고 빈도수 계산하기
- 빈도수가 2회 이상인 단어만 필터링하기
- 빈도수를 내림차순으로 정렬한 딕셔너리 만들기
- 각 문자의 빈도수 계산하기 (공백, 줄바꿈 제외)
- 단어 길이별 그룹화하기:
{길이: [단어들], ...} - 특정 문자로 시작하는 단어만 추출하기 (예: ‘p’로 시작)
- 단어와 그 길이를 매핑하는 딕셔너리 만들기
💡 힌트
- 단어 분리:
split() - 소문자 변환:
lower() - 빈도수 계산:
count()또는 딕셔너리 누적 - 필터링:
{k: v for k, v in dict.items() if 조건} - 정렬:
sorted(dict.items(), key=lambda x: x[1], reverse=True) - 그룹화: 길이를 키로, 단어 리스트를 값으로
✅ 정답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
text = """
Python is easy Python is powerful Python is fun
Learn Python today Start your Python journey
Python programming Python development
"""
print("===== 단어 빈도 분석 시스템 =====\n")
# 1. 전처리: 소문자 변환 및 단어 분리
clean_text = text.lower().strip()
words = clean_text.split()
print(f"원본 텍스트:\n{text}")
print(f"\n전처리 후 단어 수: {len(words)}개")
print(f"고유 단어 수: {len(set(words))}개\n")
# 2. 단어 빈도수 계산
word_freq = {word: words.count(word) for word in set(words)}
print("1. 전체 단어 빈도수:")
for word, count in sorted(word_freq.items()):
print(f" '{word}': {count}회")
print()
# 3. 빈도수 2회 이상인 단어만 필터링
frequent_words = {word: count for word, count in word_freq.items() if count >= 2}
print("2. 빈도수 2회 이상인 단어:")
for word, count in sorted(frequent_words.items(), key=lambda x: x[1], reverse=True):
print(f" '{word}': {count}회")
print()
# 4. 빈도수 내림차순 정렬 (Top 5)
sorted_freq = dict(sorted(word_freq.items(), key=lambda x: x[1], reverse=True)[:5])
print("3. 빈도수 TOP 5:")
for i, (word, count) in enumerate(sorted_freq.items(), 1):
print(f" {i}위: '{word}' ({count}회)")
print()
# 5. 문자 빈도수 (공백, 줄바꿈 제외)
clean_chars = clean_text.replace(" ", "").replace("\n", "")
char_freq = {char: clean_chars.count(char) for char in set(clean_chars)}
sorted_char_freq = dict(sorted(char_freq.items(), key=lambda x: x[1], reverse=True)[:10])
print("4. 문자 빈도수 TOP 10:")
for char, count in sorted_char_freq.items():
print(f" '{char}': {count}회")
print()
# 6. 단어 길이별 그룹화
words_by_length = {}
for word in set(words):
length = len(word)
if length not in words_by_length:
words_by_length[length] = []
words_by_length[length].append(word)
# 딕셔너리 컴프리헨션 버전
words_by_length_comp = {
length: [w for w in set(words) if len(w) == length]
for length in sorted(set(len(w) for w in words))
}
print("5. 단어 길이별 그룹화:")
for length in sorted(words_by_length_comp.keys()):
word_list = sorted(words_by_length_comp[length])
print(f" {length}글자 ({len(word_list)}개): {', '.join(word_list)}")
print()
# 7. 'p'로 시작하는 단어만 추출
p_words = {word: word_freq[word] for word in word_freq if word.startswith('p')}
print("6. 'p'로 시작하는 단어:")
for word, count in sorted(p_words.items(), key=lambda x: x[1], reverse=True):
print(f" '{word}': {count}회")
print()
# 8. 단어와 길이 매핑
word_length = {word: len(word) for word in set(words)}
print("7. 단어와 길이 매핑 (알파벳 순):")
for word in sorted(word_length.keys())[:10]:
print(f" '{word}': {word_length[word]}글자")
print(f" ... (총 {len(word_length)}개 단어)")
print()
# 추가: 통계
print("8. 전체 통계:")
total_chars = len(clean_chars)
avg_word_length = sum(word_length.values()) / len(word_length)
longest_word = max(word_length.items(), key=lambda x: x[1])
shortest_word = min(word_length.items(), key=lambda x: x[1])
print(f" 총 문자 수: {total_chars}개")
print(f" 평균 단어 길이: {avg_word_length:.1f}글자")
print(f" 가장 긴 단어: '{longest_word[0]}' ({longest_word[1]}글자)")
print(f" 가장 짧은 단어: '{shortest_word[0]}' ({shortest_word[1]}글자)")
print(f" 'python' 출현 비율: {word_freq.get('python', 0) / len(words) * 100:.1f}%")
출력:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
===== 단어 빈도 분석 시스템 =====
원본 텍스트:
Python is easy Python is powerful Python is fun
Learn Python today Start your Python journey
Python programming Python development
전처리 후 단어 수: 17개
고유 단어 수: 12개
1. 전체 단어 빈도수:
'development': 1회
'easy': 1회
'fun': 1회
'is': 3회
'journey': 1회
'learn': 1회
'powerful': 1회
'programming': 1회
'python': 7회
'start': 1회
'today': 1회
'your': 1회
2. 빈도수 2회 이상인 단어:
'python': 7회
'is': 3회
3. 빈도수 TOP 5:
1위: 'python' (7회)
2위: 'is' (3회)
3위: 'learn' (1회)
4위: 'start' (1회)
5위: 'today' (1회)
4. 문자 빈도수 TOP 10:
'o': 10회
'n': 9회
'y': 7회
't': 6회
'p': 6회
'r': 6회
'h': 5회
'e': 5회
's': 4회
'a': 4회
5. 단어 길이별 그룹화:
2글자 (1개): is
3글자 (1개): fun
4글자 (2개): easy, your
5글자 (3개): learn, start, today
6글자 (1개): python
7글자 (2개): journey, powerful
11글자 (2개): development, programming
6. 'p'로 시작하는 단어:
'python': 7회
'powerful': 1회
'programming': 1회
7. 단어와 길이 매핑 (알파벳 순):
'development': 11글자
'easy': 4글자
'fun': 3글자
'is': 2글자
'journey': 7글자
'learn': 5글자
'powerful': 8글자
'programming': 11글자
'python': 6글자
'start': 5글자
... (총 12개 단어)
8. 전체 통계:
총 문자 수: 97개
평균 단어 길이: 6.2글자
가장 긴 단어: 'development' (11글자)
가장 짧은 단어: 'is' (2글자)
'python' 출현 비율: 41.2%
문제 2: 학생 성적 데이터 변환 및 분석
과제: 학생 성적 데이터를 다양한 형태로 변환하고 분석하는 프로그램을 작성하세요. 중첩 딕셔너리와 복잡한 변환 패턴을 활용합니다.
초기 데이터:
1
2
3
4
5
6
7
students = {
"홍길동": {"korean": 85, "english": 92, "math": 78},
"김철수": {"korean": 90, "english": 88, "math": 95},
"이영희": {"korean": 78, "english": 85, "math": 90},
"박민수": {"korean": 95, "english": 90, "math": 88},
"정수진": {"korean": 67, "english": 75, "math": 72}
}
요구사항:
- 각 학생의 평균 점수를 계산하여 딕셔너리로 만들기
- 평균 80점 이상인 학생만 필터링하기
- 학점 판정 딕셔너리 만들기 (90 이상 A, 80 이상 B, 70 이상 C, 나머지 D)
- 과목별 최고 점수 학생 찾기:
{"korean": ("이름", 점수), ...} - 학생별 상세 정보 딕셔너리 만들기: 평균, 학점, 석차 포함
- 과목별 평균 점수 계산하기
- 키와 값을 바꾼 딕셔너리 만들기 (역변환)
💡 힌트
- 평균 계산:
sum(딕셔너리.values()) / len(딕셔너리) - 중첩 딕셔너리 접근:
student["korean"] - 최댓값 찾기:
max(딕셔너리.items(), key=lambda x: x[1]) - 학점 판정: if-elif-else 체이닝
- 역변환:
{v: k for k, v in dict.items()}
✅ 정답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
students = {
"홍길동": {"korean": 85, "english": 92, "math": 78},
"김철수": {"korean": 90, "english": 88, "math": 95},
"이영희": {"korean": 78, "english": 85, "math": 90},
"박민수": {"korean": 95, "english": 90, "math": 88},
"정수진": {"korean": 67, "english": 75, "math": 72}
}
print("===== 학생 성적 데이터 변환 및 분석 =====\n")
# 1. 각 학생의 평균 점수
averages = {
name: sum(scores.values()) / len(scores)
for name, scores in students.items()
}
print("1. 학생별 평균 점수:")
for name, avg in sorted(averages.items(), key=lambda x: x[1], reverse=True):
print(f" {name}: {avg:.1f}점")
print()
# 2. 평균 80점 이상인 학생만 필터링
high_performers = {
name: avg for name, avg in averages.items()
if avg >= 80
}
print("2. 평균 80점 이상 학생:")
for name, avg in sorted(high_performers.items(), key=lambda x: x[1], reverse=True):
print(f" {name}: {avg:.1f}점")
print()
# 3. 학점 판정
grades = {
name: ("A" if avg >= 90 else
"B" if avg >= 80 else
"C" if avg >= 70 else
"D")
for name, avg in averages.items()
}
print("3. 학생별 학점:")
for name, grade in sorted(grades.items()):
print(f" {name}: {grade}학점 (평균 {averages[name]:.1f}점)")
print()
# 4. 과목별 최고 점수 학생
subjects = ["korean", "english", "math"]
subject_names = {"korean": "국어", "english": "영어", "math": "수학"}
top_students = {
subject: max(
((name, scores[subject]) for name, scores in students.items()),
key=lambda x: x[1]
)
for subject in subjects
}
print("4. 과목별 최고 점수:")
for subject, (name, score) in top_students.items():
print(f" {subject_names[subject]}: {name} ({score}점)")
print()
# 5. 학생별 상세 정보 (평균, 학점, 석차)
# 석차 계산
sorted_averages = sorted(averages.items(), key=lambda x: x[1], reverse=True)
ranks = {name: i+1 for i, (name, _) in enumerate(sorted_averages)}
detailed_info = {
name: {
"평균": averages[name],
"학점": grades[name],
"석차": ranks[name],
"과목별": students[name]
}
for name in students
}
print("5. 학생별 상세 정보:")
for name in sorted(detailed_info.keys(), key=lambda x: ranks[x]):
info = detailed_info[name]
print(f" {name} [{info['석차']}등]:")
print(f" 평균: {info['평균']:.1f}점, 학점: {info['학점']}")
print(f" 국어: {info['과목별']['korean']}점, "
f"영어: {info['과목별']['english']}점, "
f"수학: {info['과목별']['math']}점")
print()
# 6. 과목별 평균 점수
subject_averages = {
subject: sum(students[name][subject] for name in students) / len(students)
for subject in subjects
}
print("6. 과목별 평균 점수:")
for subject, avg in subject_averages.items():
print(f" {subject_names[subject]}: {avg:.1f}점")
print()
# 7. 평균 점수를 키로, 이름을 값으로 역변환 (같은 평균이면 리스트로)
# 주의: 평균이 같으면 덮어쓰이므로, 실전에서는 리스트로 처리 필요
reversed_averages = {
round(avg, 1): name
for name, avg in averages.items()
}
print("7. 평균 점수 → 이름 역변환:")
for avg in sorted(reversed_averages.keys(), reverse=True):
print(f" {avg}점: {reversed_averages[avg]}")
print()
# 추가: 통계
print("8. 전체 통계:")
overall_avg = sum(averages.values()) / len(averages)
max_student = max(averages.items(), key=lambda x: x[1])
min_student = min(averages.items(), key=lambda x: x[1])
grade_counts = {"A": 0, "B": 0, "C": 0, "D": 0}
for grade in grades.values():
grade_counts[grade] += 1
print(f" 전체 평균: {overall_avg:.1f}점")
print(f" 최고 점수: {max_student[0]} ({max_student[1]:.1f}점)")
print(f" 최저 점수: {min_student[0]} ({min_student[1]:.1f}점)")
print(f" 학점 분포:")
for grade in ["A", "B", "C", "D"]:
if grade_counts[grade] > 0:
print(f" {grade}학점: {grade_counts[grade]}명")
출력:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
===== 학생 성적 데이터 변환 및 분석 =====
1. 학생별 평균 점수:
박민수: 91.0점
김철수: 91.0점
홍길동: 85.0점
이영희: 84.3점
정수진: 71.3점
2. 평균 80점 이상 학생:
박민수: 91.0점
김철수: 91.0점
홍길동: 85.0점
이영희: 84.3점
3. 학생별 학점:
김철수: A학점 (평균 91.0점)
박민수: A학점 (평균 91.0점)
이영희: B학점 (평균 84.3점)
정수진: C학점 (평균 71.3점)
홍길동: B학점 (평균 85.0점)
4. 과목별 최고 점수:
국어: 박민수 (95점)
영어: 홍길동 (92점)
수학: 김철수 (95점)
5. 학생별 상세 정보:
김철수 [1등]:
평균: 91.0점, 학점: A
국어: 90점, 영어: 88점, 수학: 95점
박민수 [1등]:
평균: 91.0점, 학점: A
국어: 95점, 영어: 90점, 수학: 88점
홍길동 [3등]:
평균: 85.0점, 학점: B
국어: 85점, 영어: 92점, 수학: 78점
이영희 [4등]:
평균: 84.3점, 학점: B
국어: 78점, 영어: 85점, 수학: 90점
정수진 [5등]:
평균: 71.3점, 학점: C
국어: 67점, 영어: 75점, 수학: 72점
6. 과목별 평균 점수:
국어: 83.0점
영어: 86.0점
수학: 84.6점
7. 평균 점수 → 이름 역변환:
91.0점: 박민수
85.0점: 홍길동
84.3점: 이영희
71.3점: 정수진
8. 전체 통계:
전체 평균: 84.5점
최고 점수: 김철수 (91.0점)
최저 점수: 정수진 (71.3점)
학점 분포:
A학점: 2명
B학점: 2명
C학점: 1명
🎯 연습 문제 핵심 포인트
문제 1에서 배운 것:
- 기본 패턴:
{키: 값 for 변수 in 반복자} - 조건 필터링:
{k: v for k, v in dict.items() if 조건} - 변환과 집계: 빈도수, 길이, 그룹화
- 정렬된 딕셔너리:
sorted()+dict() - 문자열 처리:
split(),lower(),count()조합
문제 2에서 배운 것:
- 중첩 딕셔너리 처리: 값이 딕셔너리인 구조
- 복잡한 계산: 평균, 학점, 석차
- 역변환 패턴: 키와 값 교환
- 다중 기준 변환: 여러 조건을 동시에 적용
- 실전 데이터 구조: 학생 관리 시스템 패턴
📝 오늘 배운 내용 정리
- 기본 문법:
{키: 값 for 변수 in 반복가능객체}로 딕셔너리 생성 - 조건 필터링:
if조건으로 특정 항목만 포함,if-else로 값 변환 - 딕셔너리 변환:
items(),keys(),values()로 다양한 변환 패턴 - 키-값 뒤집기: 딕셔너리의 키와 값을 간단하게 교환
- 실전 패턴: 빈도수 계산, 그룹화, 정규화, 검증, CSV 파싱 등
- 성능: 리스트 컴프리헨션과 유사하게 20-30% 빠른 성능
- 주의사항: 중복 키 처리, None 값, 복잡한 표현식 분리, get() 활용
🔗 관련 자료
📚 이전 학습
Day 18: 리스트 컴프리헨션 ⭐⭐
어제는 리스트 컴프리헨션으로 간결하게 리스트를 만드는 방법을 배웠습니다!
📚 다음 학습
Day 20: 미니 프로젝트: 영어 단어장 ⭐⭐⭐
Phase 2의 마지막 날! 지금까지 배운 딕셔너리, 리스트, 컴프리헨션을 활용해 실전 영어 단어장 프로젝트를 완성합니다!
“늦었다고 생각할 때가 가장 빠른 시기입니다!” 🚀
Day 19/100 Phase 2: 자료형 마스터하기 #100DaysOfPython
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.
