중급 데이터 과학자를 위한 파이썬 패턴: 입문 단계를 넘어서기

데이터 과학 입문자들에게 파이썬은 대부분 처음 배우는 언어입니다. 데이터 조작에 강력한 기능을 제공하는 범용성 높은 언어이기 때문에 많은 데이터 전문가들이 선호합니다.
그러나 실제 프로젝트에서는 기본적인 지식만으로는 부족합니다. 문제가 복잡해지면 기초적인 코드로는 해결하기 어렵고, 코드가 비효율적이고 이해하기 어려워질 수 있습니다.
입문 단계에서 벗어나려면 새로운 라이브러리나 복잡한 알고리즘만으로는 충분하지 않습니다. 프로그래밍 패턴을 워크플로우에 통합하여 파이썬 코드를 다르게 구조화해야 합니다.
중급 데이터 과학자로 성장하는 것은 단순히 기본 코드를 작성하는 것이 아닙니다. 더 스마트하고 구조화된 코드 작성이 필요합니다. 중급 단계에서는 모듈화되고 확장 가능한 코드를 목표로 합니다. 가독성과 유지보수성이 좋으면서 복잡한 워크플로우를 처리할 수 있는 코드를 지향합니다.
다양한 파이썬 패턴을 활용하면 기본 수준을 넘어서 코드와 워크플로우를 향상시킬 수 있습니다. 데이터 처리 파이프라인 패턴부터 시작해 이러한 패턴들을 살펴보겠습니다.
데이터 파이프라인 패턴
데이터를 다룰 때, 우리는 정제, 전처리, 특성 엔지니어링과 같은 많은 데이터 조작 작업을 수행합니다. 입문 단계의 스크립트에서는 이러한 작업들이 노트북 전체에 흩어져 있거나 같은 작업이 여러 번 반복되는 경우가 많습니다.
이런 방식을 계속 사용하면 디버깅이 매우 힘들어지고, 기술적 부채가 늘어나며 협업도 어려워집니다. 이런 이유로 워크플로우를 개선하기 위한 파이프라인 패턴이 필요합니다.
파이프라인은 데이터 작업에 필요한 단계를 순차적으로 구성하는 프로세스입니다. 각 단계는 데이터 로딩, 정제, 스케일링, 모델 훈련 등 특정 작업을 담당합니다. 한 단계에서 다음 단계로 체계적으로 진행됩니다.
Pandas DataFrame을 예로 들어보겠습니다. 판매 데이터를 시뮬레이션하겠습니다.
python
import pandas as pd
import numpy as np
data = {
'sales': [1000, 1500, np.nan, 2000, 2500],
'quantity': [50, 60, 70, np.nan, 90],
'product': ['A', 'B', 'C', 'D', 'E']
}
example_df = pd.DataFrame(data)
데이터 과학자로서 항상 수행하는 일반적인 데이터 처리 단계가 있습니다. 가장 일반적인 단계는 데이터 로딩, 정제, 특성 엔지니어링입니다.
python
Step 1: Load Data
def load_data(df: pd.DataFrame) -> pd.DataFrame:
return df
Step 2: Data Cleaning
def clean_data(df: pd.DataFrame) -> pd.DataFrame:
missing_before = df.isnull().sum().sum()
dfcleaned = df.dropna().resetindex(drop=True)
missingafter = dfcleaned.isnull().sum().sum()
return df_cleaned
Step 3: Feature Engineering
def engineer_features(df: pd.DataFrame) -> pd.DataFrame:
if 'sales' in df.columns and 'quantity' in df.columns:
df['avg_price'] = df['sales'] / df['quantity']
return df
각 데이터 처리 단계를 재사용 가능한 함수로 정의하면 매번 명시적으로 작성하지 않고도 여러 번 실행할 수 있습니다.
하지만 구조를 더 깔끔하게 만들기 위해서는 파이프라인으로 만들어야 합니다. 이전에 만든 모든 함수를 위한 파이프라인 실행 함수를 만들어 보겠습니다.
python
def execute_pipeline(df: pd.DataFrame, steps: list) -> pd.DataFrame:
for step in steps:
df = step(df)
return df
pipeline_steps = [
load_data,
clean_data,
engineer_features
]
finaldf = executepipeline(exampledf, pipelinesteps)
이처럼 간단한 파이프라인 함수를 사용하면 읽기 쉬운 재사용 가능한 코드를 만들 수 있습니다.
Scikit-Learn을 사용하여 데이터 처리 및 모델링 파이프라인을 만들 수도 있습니다. 다음은 사용할 수 있는 개념적 코드입니다.
python
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
pipeline = Pipeline([
('imputer', SimpleImputer(strategy='mean')),
('scaler', StandardScaler()),
('classifier', RandomForestClassifier(nestimators=100, randomstate=42))
])
pipeline.fit(X, y)
앞으로의 데이터 과학 작업에 유용하므로 파이프라인을 적절히 사용해 보세요.
팩토리 패턴
프로그래밍에서 초보자와 중급자를 구분하는 핵심은 코드를 재사용하고 최대한 적게 작성하는 능력입니다. 데이터 과학에서는 워크플로우가 더 복잡해지고 데이터 실험이 더 일반적이므로 이것이 더 어려울 수 있습니다.
데이터와 모델을 실험할 때 모델과 데이터셋을 자주 전환합니다. 각 코드를 매번 작성해야 하므로 지루하고 복잡해질 수 있습니다.
이때 팩토리 패턴이 유용합니다. 이 패턴은 머신러닝 모델과 같은 객체 생성을 중앙화하고, 별도의 코드를 작성하지 않고 매개변수를 조정하여 여러 출력을 생성할 수 있게 합니다.
팩토리 패턴을 사용하면 필요한 객체 생성을 중앙화하고, 쉽게 실험하며, 코드를 더 모듈화할 수 있습니다.
코드 예제를 살펴보겠습니다. 다음은 머신러닝 모델을 생성하기 위한 팩토리 패턴입니다.
python
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
def modelfactory(modeltype: str, kwargs):
models = {
'logistic_regression': LogisticRegression,
'decision_tree': DecisionTreeClassifier,
'random_forest': RandomForestClassifier,
'gradient_boosting': GradientBoostingClassifier
}
if model_type not in models:
raise ValueError(f"Unsupported model type '{model_type}'. Supported types: {list(models.keys())}")
return modelsmodel_type
매개변수를 변경하여 머신러닝 모델을 생성하는 함수만 정의했습니다.
그런 다음 함수를 쉽게 호출하여 원하는 모델을 생성할 수 있습니다.
python
logregmodel = modelfactory(
modeltype='logisticregression',
solver='liblinear',
random_state=42)
팩토리 패턴으로 만들지 않으면 복잡해질 것 같은 실험에 이 패턴을 적용해 보세요.
데코레이터 패턴
데이터 워크플로우가 커지면 로깅과 같은 추가 정보가 필요할 수 있습니다. 보통 스크립트 전체에 로거와 같은 코드를 수동으로 넣지만, 이는 중복되고 가독성이 떨어집니다.
이를 위해 데코레이터 패턴을 사용할 수 있습니다. 데코레이터 패턴은 다른 함수를 감싸는 특별한 함수입니다. 이를 통해 감싸진 함수의 실행에 추가 기능을 더할 수 있습니다.
데코레이터를 사용하면 코드의 핵심을 변경하지 않고도 다양한 함수에서 일관되게 코드를 처리하면서 코드를 깨끗하게 유지할 수 있습니다. 또한 필요하지 않을 때 쉽게 제거할 수 있습니다.
예를 들어, 함수 실행 시간을 이해하기 위한 래퍼 함수를 만들어 보겠습니다.
python
import functools
import time
def logandtime(func):
@functools.wraps(func)
def wrapper(args, *kwargs):
func_name = func.name
start_time = time.time()
result = func(args, *kwargs)
end_time = time.time()
duration = endtime - starttime
print(f"'{func_name}' completed in {duration:.4f} seconds.\n")
return result
return wrapper
이제 시뮬레이션 데이터를 생성하는 함수를 만들어 보겠습니다. 새 함수에 이전에 만든 데코레이터 패턴을 사용하겠습니다.
python
@logandtime
def loadsimulateddata(n_rows=10000):
np.random.seed(42)
df = pd.DataFrame({
'age': np.random.randint(18, 70, size=n_rows),
'income': np.random.normal(50000, 15000, size=n_rows),
'creditscore': np.random.randint(300, 850, size=nrows),
'loanamount': np.random.normal(20000, 5000, size=nrows),
'defaulted': np.random.binomial(1, 0.2, size=n_rows)
})
for col in ['income', 'credit_score']:
df.loc[df.sample(frac=0.05).index, col] = np.nan
return df
이전에 만든 함수를 실행해 보겠습니다.
python
dfloaded = loadsimulated_data()
아래와 유사한 실행 시간 결과가 표시됩니다.
'loadsimulateddata' completed in 0.0707 seconds.
데코레이터 함수는 함수 이름을 받아 실행 시간을 정확하게 제공합니다.
래핑하고 정보를 얻고자 하는 작업에 데코레이터 패턴을 사용해 보세요.
결론
초보자에서 중급 데이터 과학자로 발전하는 것은 입문 수준을 넘어 파이썬 코드를 더 효과적으로 사용할 수 있다는 의미입니다. 초보자보다 더 깔끔하고 효율적으로 데이터 워크플로우를 관리하는 코드를 작성할 수 있어야 합니다. 이 글에서는 데이터 파이프라인 패턴, 팩토리 패턴, 데코레이터 패턴을 포함한 중급 데이터 과학자를 위한 다양한 파이썬 패턴을 살펴보았습니다.
도움이 되셨길 바랍니다!