데이터 분석을 위한 파이썬 1
1) 시퀀스 슬라이싱(Slicing)
핵심 개념
- 슬라이싱: 시퀀스(리스트/튜플/문자열 등)에서 부분 시퀀스를 seq[start:stop:step]으로 선택/복사/수정.
- 경계 규칙: start 포함, stop 미포함, step 기본 1, 음수 가능(역순).
주요 패턴
numbers = [10, 20, 30, 40, 50, 60, 70]
# 1) 기본 범위 선택
subset1 = numbers[0:3] # [10, 20, 30]
subset2 = numbers[:3] # 시작 생략 → 0부터
subset3 = numbers[-3:] # 끝에서 3개 [50, 60, 70]
# 2) 간격/역순
evens = numbers[::2] # [10, 30, 50, 70] # step=2
reversed_numbers = numbers[::-1] # 역순 복사
# 3) 슬라이스 대입(제자리 수정)
nums = [0, 1, 2, 3, 4]
nums[1:4] = [10, 11] # 길이 달라도 치환 가능 → [0, 10, 11, 4]
실무 팁 & 주의점
- 얕은 복사(shallow copy): new = old[:]는 얕은 복사. 중첩 리스트가 있으면 내부는 참조 공유됨.
- 범위를 벗어나도 IndexError 없음(조용히 잘리는 점이 장점이자 함정).
- 문자열도 슬라이싱 가능(불변이라 새 문자열 생성).
2) 컴프리헨션(Comprehension)
리스트 컴프리헨션
# [표현식 for 변수 in 반복가능객체 if 조건]
nums = [1, 2, 3, 4, 5]
squares = [n ** 2 for n in nums] # 원소 변환
evens = [n for n in nums if n % 2 == 0] # 조건 필터
pairs = [(i, j) for i in range(3) for j in range(2)] # 중첩 루프
딕셔너리 컴프리헨션
# {키:값 for ...} / {k:v for k,v in dict_.items()}
# 타입: dict[int,int]
squares_map = {i: i**2 for i in range(5)} # {0:0, 1:1, 2:4, ...}
prices = {"apple": 1200, "banana": 800, "melon": 5000}
# 1000원 이상만 10% 인상
inc_prices = {k: int(v * 1.1) for k, v in prices.items() if v >= 1000}
세트/제너레이터 컴프리헨션
# set comprehension: 중복 제거한 제곱값
sq_set = {n**2 for n in [1,2,2,3]} # {1,4,9}
# generator expression: 지연 평가(메모리 절약)
gen = (n**2 for n in range(10))
실무 팁
- 한 줄에 조건/중첩이 과하면 가독성↓ → 일반 for로 분해 권장.
- 딕셔너리/세트 컴프리헨션으로 중복 제거/매핑 변환을 깔끔하게 처리.
3) 문자열 형식 지정 — % / str.format() / f-문자열
선택 가이드
- f-문자열(추천): 파이썬 3.6+ 권장. 가독성 최고, 표현식 직접 삽입.
- str.format(): 이름/인덱스 기반 포맷이 필요할 때.
- % 포맷: C 스타일 익숙할 때, 간단한 경우만.
예제 비교
name: str = "홍길동"
age: int = 30
salary: int = 3_600_000
tax_rate: float = 0.1234
# 1) C 스타일 (%)
print("이름: %s, 나이: %d, 월급: %d원" % (name, age, salary))
# 2) str.format()
print("이름: {}, 나이: {}, 월급: {:,}원".format(name, age, salary))
print("직원 {n}: 세후 {net:,}원".format(n=name, net=int(salary*(1-tax_rate))))
# 3) f-문자열
print(f"이름: {name}, 나이: {age}, 월급: {salary:,}원")
print(f"세율: {tax_rate:.1%}, 세후: {int(salary*(1-tax_rate)):,}원")
# 디버깅 편의: f"{변수=}"
print(f"{salary=:,}, {tax_rate=:.3%}")
포맷 미세 조정(자주 쓰는 규칙)
- 천단위 구분자: :, → f"{salary:,}"
- 소수점 자리: :.2f → f"{pi:.2f}"
- 퍼센트: :.1% → 0.1234 → 12.3%
- 정렬/폭: :>10, :<10, :^10 → 우/좌/가운데 정렬
4) 컨텍스트 관리(with) — 파일/네트워크/DB 자원 안전 관리
핵심 개념
- 컨텍스트 관리자: 자원 획득(open)→사용(write)→해제(close)를 자동화.
- with 블록을 벗어날 때 예외 발생 여부와 관계없이 정리 보장.
파일 입출력 예제
# 역할/타입:
# path: str - 파일 경로
# mode: str - 파일 모드("w", "r", "a", "wb" 등)
# encoding: str - 텍스트 인코딩(한국어 권장: "utf-8")
path: str = "output.txt"
# 쓰기
with open(path, "w", encoding="utf-8") as f:
f.write("Hello, world!\n")
# 읽기
with open(path, "r", encoding="utf-8") as f:
data: str = f.read()
직접 컨텍스트 만들기(심화)
class managed_file:
# file: typing.Optional[io.TextIOWrapper]
def __init__(self, path: str, mode: str = "w", encoding: str = "utf-8"):
self.path = path
self.mode = mode
self.encoding = encoding
self.file = None
def __enter__(self):
self.file = open(self.path, self.mode, encoding=self.encoding)
return self.file # with ... as file 에서 바인딩되는 객체
def __exit__(self, exc_type, exc, tb):
if self.file and not self.file.closed:
self.file.close()
# False 반환 → 예외를 상위로 전파(일반적 선택)
return False
with managed_file("sample.txt", "w") as f:
f.write("context manager by class\n")
보너스: contextlib.contextmanager 데코레이터로 제너레이터 기반 컨텍스트를 간단히 만들 수 있습니다.
5) 미니 실습(체크리스트와 연계)
A. 리스트/딕셔너리 조작(학습목표 1)
- data = list(range(1, 21))에서 짝수만 역순으로 뽑아 보세요.
- 힌트: 슬라이싱 + 조건, 혹은 컴프리헨션
- scores = {"kim":88,"lee":92,"park":75,"choi":96}에서
90점 이상만 5점 가산한 새 딕셔너리를 컴프리헨션으로 만드세요.
B. 문자열 형식 지정(학습목표 2)
- 사원정보(name, salary, tax_rate)를 f-문자열로
이름: OOO, 세전: 1,234,567원, 세후: 1,111,111원(세율 12.3%) 형태로 출력. - 동일 정보를 %와 str.format()으로도 각각 출력.
C. 컨텍스트 관리(학습목표 3)
- with open(..., "w", encoding="utf-8")로 실습 A/B 결과를 한 줄씩 파일에 저장.
- 다시 with open(..., "r", encoding="utf-8")로 읽어와 화면에 출력.
6) 자주 하는 실수
- 슬라이싱 stop 미포함을 잊고 한 칸 모자라거나 넘기는 경우 → 항상 인덱스 범위 확인.
- 중첩 컴프리헨션이 길어지면 가독성↓ → 단계 분리.
- 금액/퍼센트 표기는 f-문자열의 :,, :.1%를 습관화.
- 파일 I/O는 반드시 with 사용(예외 발생 시에도 닫힘 보장).
- 텍스트 파일은 encoding="utf-8"을 명시해 한글 깨짐 방지.
7) 60초 복습 요약
- 슬라이싱: seq[start:stop:step], 역순은 [::-1], 대입으로 부분 수정 가능.
- 컴프리헨션: 변환/필터/매핑을 간결하게(리스트·딕셔너리·세트·제너레이터).
- 문자열 포맷팅: 기본은 f-문자열(표현식·가독성↑), 상황에 따라 format/%.
- 컨텍스트 관리: with로 자원 자동 정리(파일/소켓/DB).
오픈소스 기반 데이터 분석 2강 — 데이터 분석을 위한 파이썬 1
Data-Analysis-with-Open-Source/오픈소스_데이터_분석_2강.ipynb at main · mors119/Data-Analysis-with-Open-Source
Data Analysis with Open Source. Contribute to mors119/Data-Analysis-with-Open-Source development by creating an account on GitHub.
github.com
2-1 리스트 슬라이싱
# 리스트 슬라이싱 기본 예시
numbers = [10, 20, 30, 40, 50, 60, 70]
# 1) 첫 4개 원소 [:4] — 시작 인덱스 생략 시 0부터
first_four = numbers[:4] # => [10, 20, 30, 40]
print(first_four)
# 대안: itertools.islice (이터레이터 기반으로 메모리 절약)
# [import itertools]
# from itertools import islice
# first_four_iter = list(islice(numbers, 4)) # => [10, 20, 30, 40]
# 2) 뒤 3개 원소 [-3:] — 음수 인덱스는 뒤에서부터
last_three = numbers[-3:] # => [50, 60, 70]
print(last_three)
# 옵션: step(간격) 지정 [start:stop:step]
# 3) 홀수번째(0,2,4...) — step=2
every_other_list = numbers[::2] # => [10, 30, 50, 70]
print(every_other_list)
# 4) 역순 — step=-1
reverse_list = numbers[::-1] # => [70, 60, 50, 40, 30, 20, 10]
print(reverse_list)
# 2차원(중첩) 리스트 슬라이싱
matrix = [
[1, 2, 3, 4 ],
[5, 6, 7, 8 ],
[9, 10, 11, 12],
[13,14, 15, 16],
]
# 5) 첫 두 행 [:2]
first_two_rows = matrix[:2] # => [[1,2,3,4],[5,6,7,8]]
print(first_two_rows)
# 6) 첫 두 열 — 행 단위로 열 슬라이싱
first_two_columns = [row[:2] for row in matrix] # => [[1,2],[5,6],[9,10],[13,14]]
print(first_two_columns)
# 대안/옵션:
# - numpy 슬라이싱 (대규모 수치연산에서 권장)
# [import numpy as np]
# arr = np.array(matrix)
# arr[:2, :] # 행 슬라이싱
# arr[:, :2] # 열 슬라이싱
2-2 리스트/딕셔너리 컴프리헨션
# 1) 기본 리스트 컴프리헨션 — range로 0~4 생성
numbers = [i for i in range(5)] # => [0,1,2,3,4]
print(numbers)
# 대안:
# - 전통적 for-append 패턴 (가독성 ↑, 길이 ↑)
# out = []
# for i in range(5):
# out.append(i)
# 2) 변환(map) — 모든 항목 제곱
base = [1, 2, 3, 4, 5]
squares = [x**2 for x in base] # => [1,4,9,16,25]
print(squares)
# 대안:
# - map 함수 + lambda
# squares = list(map(lambda x: x**2, base)) # [import (built-in) — 별도 import 불필요]
# 3) 필터링(filter) — 짝수만 추출
nums = [1,2,3,4,5,6,7,8,9,10]
even_numbers = [x for x in nums if x % 2 == 0] # => [2,4,6,8,10]
print(even_numbers)
# 대안:
# - filter 함수 + lambda
# even_numbers = list(filter(lambda x: x % 2 == 0, nums))
# 4) 중첩 루프 — 모든 조합(카테시안 곱)
list1 = ['사과', '복숭아', '바나나']
list2 = ['주스', '잼', '통조림']
pairs = [(f, p) for f in list1 for p in list2]
print(pairs)
# 대안:
# - itertools.product
# [import itertools]
# pairs = list(itertools.product(list1, list2))
# 5) 딕셔너리 컴프리헨션 — 키:값 변환
squares_map = {i: i**2 for i in range(5)} # => {0:0,1:1,2:4,3:9,4:16}
print(squares_map)
# 6) 조건부 딕셔너리 필터링
city_population = {
'서울': 957, '부산': 339, '인천': 294, '대구': 242, '광주': 145, '대전': 147,
'울산': 114, '세종': 36, '수원': 115, '창원': 103, '고양': 105, '용인': 108, '성남': 94
}
# (예시) 인구 200 이상 도시만 — .items()로 (키,값) 튜플 순회
large_cities = {city: pop for city, pop in city_population.items() if pop >= 200}
print(large_cities) # => {'서울': 957, '부산': 339, '인천': 294, '대구': 242}
# (예시) 인구 300 이상 + 이름에 '산' 포함
large_short_name_cities = {
city: pop for city, pop in city_population.items()
if pop >= 300 and '산' in city
}
print(large_short_name_cities) # => {'부산': 339}
# 옵션/주의:
# - 컴프리헨션이 너무 복잡해지면(중첩+조건 다수) 가독성 저하 → 일반 for문 권장
# - set 컴프리헨션도 동일 문법 {expr for ...} (중복 제거 필요 시)
2-3 문자열 형식화(포매팅)
name = '홍길동'
age = 30
salary = 3_500_000
tax_rate = 0.10
# 1) C 스타일(구식) — % 연산자
basic_format = '이름: %s, 나이: %d, 월급: %d원' % (name, age, salary)
print(basic_format)
# 옵션(형식 지정자):
# %s(문자열), %d(정수), %f(부동소수), %.2f(소수점 2자리), %10d(폭), %-10s(좌측정렬)
# 2) str.format — 위치/키워드 인자
index_format = '이름: {0}, 나이: {1}, 월급: {2}원'.format(name, age, salary)
print(index_format)
# 대안(키워드):
# index_format_kw = '이름: {n}, 나이: {a}, 월급: {s}원'.format(n=name, a=age, s=salary)
# 3) f-문자열 — 가장 가독성 높고 빠름(파이썬 3.6+)
keyword_format = f'이름: {name}, 나이: {age}, 월급: {salary}원'
print(keyword_format)
# 4) 계산 포함 + 서식 옵션(세후 월급, 세금, 실수령액)
tax = salary * tax_rate
net = salary - tax
# - 천 단위 구분자: :, 소수점 자리: .2f, 정렬/폭: >10, <10, ^10
line1 = f'세율: {tax_rate:.0%}, 세금: {tax:,.0f}원, 실수령액: {net:,.0f}원'
print(line1)
# 5) 동일 내용의 다른 방식들 (학습용 비교)
line1_percent = '세율: %.0f%%, 세금: %s원, 실수령액: %s원' % (
tax_rate*100, format(tax, ',.0f'), format(net, ',.0f')
)
print(line1_percent)
line1_format = '세율: {rate:.0%}, 세금: {tax:,.0f}원, 실수령액: {net:,.0f}원'.format(
rate=tax_rate, tax=tax, net=net
)
print(line1_format)
# 옵션/팁:
# - {:,} : 천 단위 구분
# - {:.2f} : 소수점 둘째 자리까지
# - {var:>10} : 폭 10, 우측정렬 / < 좌측 / ^ 중앙
# - 날짜/시간: datetime + f-string 또는 strftime 사용
# [import datetime]
# from datetime import datetime
# now = datetime.now()
# f'오늘은 {now:%Y-%m-%d %H:%M:%S}'
2-4 컨텍스트 관리 (Context Manager)
# 1) 파일 열기/닫기 — 수동 관리 (권장 X: 예외 시 close 누락 위험)
file = open('output.txt', 'w')
file.write('Hello, world!\n')
file.close()
# 2) with 문(권장) — 예외 발생해도 안전하게 close 보장
with open('output.txt', 'w', encoding='utf-8') as f: # 옵션: mode='a' 추가 시 append
f.write('Hello, world!\n')
# 옵션:
# - open 모드: 'r'(읽기), 'w'(새로쓰기), 'a'(덧붙이기), 'x'(신규 생성), 'b'(바이너리), '+’(읽기/쓰기)
# 예) 'rb' 바이너리 읽기, 'w+' 읽기/쓰기
# - encoding: 텍스트 파일은 'utf-8' 권장
# 3) URL 연결 관리 — urllib.request (표준 라이브러리)
# with 문으로 HTTP 연결 객체도 안전하게 정리
# [import urllib.request]
import urllib.request
url = 'https://raw.githubusercontent.com/jaehwachung/Data-Analysis-with-Open-Source/refs/heads/main/Chapter%203/students.csv'
# 바이너리 스트림을 열고, UTF-8로 디코드
with urllib.request.urlopen(url) as response: # 옵션: timeout=10 등
data = response.read().decode('utf-8') # 옵션: 인코딩 다르면 'cp949' 등
print(data[:200]) # (미리보기) 처음 200자만 출력
# 대안 1) requests (서드파티, 간단 API)
# [import requests]
# import requests
# resp = requests.get(url, timeout=10)
# resp.raise_for_status() # HTTP 에러 시 예외
# data = resp.text # 자동 인코딩 추정
# print(data[:200])
# 대안 2) pandas로 바로 읽기 (CSV 처리에 강력)
# [import pandas]
# import pandas as pd
# df = pd.read_csv(url) # 옵션: sep, header, encoding, dtype, usecols 등
# print(df.head())
# 옵션/주의:
# - 네트워크 I/O는 예외 대비 try/except 권장
# - 대용량 파일은 스트리밍(iter_content) 또는 chunk 단위 처리 고려(requests)
# - 인증/헤더 필요 시: urllib.request.Request 사용, 혹은 requests의 headers 파라미터
# 4) 사용자 정의 컨텍스트 매니저 — contextlib.contextmanager
# 파일/락/세션 등 "열고-사용하고-정리" 패턴을 캡슐화할 때 유용
# [import contextlib]
from contextlib import contextmanager
@contextmanager
def opened(path, mode='r', encoding=None):
f = open(path, mode, encoding=encoding)
try:
yield f # with 블록 내부로 자원(f) 넘김
finally:
f.close() # 블록 종료 시 정리 보장
with opened('sample.txt', 'w', encoding='utf-8') as fp:
fp.write('컨텍스트 매니저 예시\n')
# 대안:
# - 클래스 기반 __enter__/__exit__ 구현
# - 파일 외에도 DB 커넥션, 스레드 락, 임시 디렉토리 등 관리 가능
# 5) 여러 파일/자원 동시 관리 — 여러 with를 한 줄로
# (파이썬 3.10+에서는 괄호로 줄바꿈 가능)
with open('in.txt', 'r', encoding='utf-8') as fin, \
open('out.txt', 'w', encoding='utf-8') as fout:
for line in fin:
fout.write(line.upper())
# 대안:
# - contextlib.ExitStack (가변 개수의 자원 동적 관리 시 유용)
# [import contextlib]
# from contextlib import ExitStack
# with ExitStack() as stack:
# files = [stack.enter_context(open(p, 'r', encoding='utf-8')) for p in paths]
# # files 리스트로 일괄 처리
# 6) 임시 파일/디렉터리 — 자동 정리되는 자원
# [import tempfile]
import tempfile
import os
# 임시 파일
with tempfile.NamedTemporaryFile(mode='w+', delete=True, encoding='utf-8') as tmp:
tmp.write('임시 데이터\n')
tmp.seek(0)
print(tmp.read())
# 임시 디렉터리
with tempfile.TemporaryDirectory() as tmpdir:
path = os.path.join(tmpdir, 'data.txt')
with open(path, 'w', encoding='utf-8') as f:
f.write('tempdir 예시')
# 블록 종료 시 디렉터리/내용 모두 삭제
# 7) 표준 입출력 리다이렉션 — contextlib.redirect_stdout/redirect_stderr
# [import contextlib, io]
import io
from contextlib import redirect_stdout
buf = io.StringIO()
with redirect_stdout(buf):
print("이 출력은 화면 대신 버퍼로 갑니다.")
captured = buf.getvalue()
print("캡쳐된 출력:", captured)
'Python > Data analysis' 카테고리의 다른 글
| 데이터 전처리 1 (0) | 2025.10.10 |
|---|---|
| 데이터 저장 (0) | 2025.10.07 |
| 데이터 수집 (0) | 2025.10.07 |
| 언패킹, 예외처리, 함수형 프로그래밍 (0) | 2025.10.03 |
| 데이터 분석과 오픈소스 (0) | 2025.10.03 |