# 2026년 Python 데이터 사이언스: NumPy, Pandas, Scikit-Learn 완벽 가이드 > 2026년 기준 Python 데이터 사이언스 핵심 라이브러리인 NumPy, Pandas, Scikit-Learn의 실전 활용법을 코드 예제와 함께 상세히 다룹니다. - Published: 2026-04-07 - Updated: 2026-04-07 - Author: Anthony Fillion-Maillet - Tags: data-science, python, numpy, pandas, scikit-learn, machine-learning, tutorial - Reading time: 10 min --- 2026년 Python 데이터 사이언스 워크플로우는 전체 작업의 90%를 처리하는 세 가지 핵심 라이브러리에 의존합니다. 수치 연산을 위한 NumPy, 테이블 데이터 조작을 위한 Pandas, 그리고 머신러닝을 위한 Scikit-Learn이 그 주인공입니다. 이 튜토리얼에서는 각 라이브러리의 실전 코드를 단계별로 살펴보며, 원시 데이터를 불러오고 정제하고 피처를 생성하여 모델을 학습시키는 완전한 파이프라인을 구축하는 과정을 안내합니다. > **이 튜토리얼에서 사용하는 버전** > > 모든 코드는 Python 3.12+, NumPy 2.1, Pandas 2.2, Scikit-Learn 1.6 환경에서 실행됩니다. API 인터페이스는 마이너 버전 간에 안정적이지만, NumPy 2.x의 성능 개선 사항은 대규모 배열 연산에서 업그레이드할 가치가 충분합니다. ## NumPy 배열 연산을 활용한 고효율 계산 NumPy는 Python 리스트를 연속 메모리 블록으로 구성된 N차원 배열로 대체합니다. 성능 차이는 상당합니다. 100만 개 요소에 대한 벡터화된 NumPy 연산은 동일한 Python 반복문보다 50~100배 빠르게 수행됩니다. 모든 Pandas DataFrame과 Scikit-Learn 모델은 내부적으로 NumPy 배열을 사용합니다. ```python # numpy_basics.py import numpy as np # Create arrays from different sources prices = np.array([29.99, 49.99, 19.99, 99.99, 39.99]) quantities = np.arange(1, 6) # [1, 2, 3, 4, 5] # Vectorized arithmetic — no loops needed revenue = prices * quantities print(revenue) # [29.99, 99.98, 59.97, 399.96, 199.95] # Statistical aggregations print(f"Total revenue: ${revenue.sum():.2f}") # $789.85 print(f"Mean price: ${prices.mean():.2f}") # $47.99 print(f"Std deviation: ${prices.std():.2f}") # $27.64 # Boolean indexing — filter without loops premium_mask = prices > 40 premium_items = prices[premium_mask] # [49.99, 99.99] ``` 불리언 인덱싱은 실제 데이터 사이언스 코드에서 가장 빈번하게 등장하는 패턴입니다. 반복문 안에서 `if` 문을 작성하는 대신, 불리언 마스크가 조건을 만족하는 요소를 단일 연산으로 선택합니다. ## NumPy의 Reshape와 Broadcasting 2차원 연산을 수행하려면 NumPy가 서로 다른 형상(shape)의 배열을 브로드캐스팅하는 방식을 이해해야 합니다. 브로드캐스팅 규칙을 통해 명시적인 타일링 없이도 행렬과 벡터 간 연산이 가능합니다. ```python # numpy_reshape.py import numpy as np # Monthly sales data: 4 products x 3 months sales = np.array([ [120, 150, 130], # Product A [200, 180, 220], # Product B [90, 110, 95], # Product C [300, 280, 310], # Product D ]) # Column-wise mean (average per month) monthly_avg = sales.mean(axis=0) # [177.5, 180.0, 188.75] # Row-wise sum (total per product) product_totals = sales.sum(axis=1) # [400, 600, 295, 890] # Normalize each product relative to its own max normalized = sales / sales.max(axis=1, keepdims=True) # keepdims=True preserves the shape for broadcasting print(normalized[0]) # [0.8, 1.0, 0.867] — Product A relative to its peak # Reshape for Scikit-Learn (requires 2D input) flat_sales = sales.flatten() # 1D array of 12 values reshaped = flat_sales.reshape(-1, 1) # 12x1 column vector ``` `axis` 매개변수는 집계 방향을 제어합니다. `axis=0`은 행을 축소하여 열 방향으로 연산하고, `axis=1`은 열을 축소하여 행 방향으로 연산합니다. `keepdims=True` 인자는 브로드캐스팅 시 형상 불일치를 방지합니다. ## Pandas DataFrame 조작과 데이터 정제 Pandas는 NumPy 배열을 레이블이 부여된 축과 SQL 유사 연산으로 감싸는 라이브러리입니다. DataFrame은 핵심 데이터 구조로, 각 열이 서로 다른 데이터 타입을 가질 수 있는 테이블 형태입니다. Pandas 2.2는 기본적으로 PyArrow 기반 문자열 컬럼을 사용하며, object dtype 문자열에 비해 메모리 사용량이 현저히 적습니다. ```python # pandas_cleaning.py import pandas as pd import numpy as np # Load and inspect raw data df = pd.read_csv("candidates.csv") print(df.shape) # (1500, 8) print(df.dtypes) # Check column types print(df.isna().sum()) # Count missing values per column # Clean in a reproducible chain df_clean = ( df .dropna(subset=["salary", "experience_years"]) # Drop rows missing critical fields .assign( salary=lambda x: x["salary"].clip(lower=20000, upper=500000), # Cap outliers experience_years=lambda x: x["experience_years"].astype(int), hired_date=lambda x: pd.to_datetime(x["hired_date"], errors="coerce"), ) .drop_duplicates(subset=["email"]) # Remove duplicate candidates .query("experience_years >= 0") # Filter invalid entries .reset_index(drop=True) ) print(f"Cleaned: {len(df)} -> {len(df_clean)} rows") ``` `.assign()`을 활용한 메서드 체이닝은 변환 과정을 읽기 쉽고 재현 가능하게 만듭니다. 각 단계가 무엇이 변경되고 왜 변경되는지를 명확하게 기록합니다. `.assign()` 내부의 `lambda x:` 패턴은 체인의 해당 시점에서의 DataFrame을 참조하므로, 오래된 데이터를 참조하는 실수를 방지합니다. > **Pandas 2.2의 Copy-on-Write** > > Pandas 2.2는 Copy-on-Write를 기본으로 활성화합니다. 이를 통해 `SettingWithCopyWarning`이 제거되고 체이닝 연산의 안전성이 향상됩니다. 슬라이스에 대한 수정이 더 이상 원본 DataFrame을 무음으로 변경하지 않으며, 실제 쓰기가 발생할 때만 복사본이 생성됩니다. ## Pandas의 GroupBy 집계와 피처 엔지니어링 GroupBy는 데이터를 하위 집합으로 분할하고, 각 하위 집합에 함수를 적용한 뒤, 결과를 결합하는 패턴입니다. 이 패턴은 테이블 기반 머신러닝에서 대부분의 피처 엔지니어링 워크플로우를 주도합니다. ```python # pandas_groupby.py import pandas as pd # Aggregate candidate stats by department dept_stats = ( df_clean .groupby("department") .agg( avg_salary=("salary", "mean"), median_experience=("experience_years", "median"), headcount=("email", "count"), max_salary=("salary", "max"), ) .sort_values("avg_salary", ascending=False) ) print(dept_stats.head()) # Create features for ML: encode categorical + add aggregated stats df_features = ( df_clean .assign( # Ratio of individual salary to department average salary_ratio=lambda x: x["salary"] / x.groupby("department")["salary"].transform("mean"), # Time since hire in days tenure_days=lambda x: (pd.Timestamp.now() - x["hired_date"]).dt.days, # Binary encoding is_senior=lambda x: (x["experience_years"] >= 5).astype(int), ) ) ``` `.transform()` 메서드는 원본 인덱스에 정렬된 Series를 반환하므로 `.assign()` 내부에서 안전하게 사용할 수 있습니다. 집계된 결과를 원본 DataFrame에 수동으로 조인해야 하는 흔한 실수를 방지할 수 있습니다. ## Scikit-Learn 파이프라인 처음부터 구축하기 Scikit-Learn 파이프라인은 전처리 단계와 모델을 하나의 객체로 연결합니다. 파이프라인은 변환기를 학습 데이터에서만 적합(fit)시킨 후 동일한 변환을 테스트 데이터에 적용하므로, 데이터 누수를 방지합니다. ```python # sklearn_pipeline.py from sklearn.model_selection import train_test_split from sklearn.pipeline import Pipeline from sklearn.compose import ColumnTransformer from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.ensemble import GradientBoostingClassifier from sklearn.metrics import classification_report import pandas as pd # Define column groups numeric_features = ["salary", "experience_years", "salary_ratio", "tenure_days"] categorical_features = ["department", "role_level"] target = "promoted" # Split before any preprocessing X = df_features[numeric_features + categorical_features] y = df_features[target] X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) # Build the preprocessing + model pipeline preprocessor = ColumnTransformer( transformers=[ ("num", StandardScaler(), numeric_features), ("cat", OneHotEncoder(handle_unknown="ignore"), categorical_features), ] ) pipeline = Pipeline([ ("preprocessor", preprocessor), ("classifier", GradientBoostingClassifier( n_estimators=200, learning_rate=0.1, max_depth=4, random_state=42, )), ]) # Train and evaluate pipeline.fit(X_train, y_train) y_pred = pipeline.predict(X_test) print(classification_report(y_test, y_pred)) ``` `ColumnTransformer`는 서로 다른 열 유형에 대해 서로 다른 전처리를 단일 단계에서 적용합니다. `StandardScaler`는 수치 피처를 평균 0, 분산 1로 정규화합니다. `OneHotEncoder`는 범주형 문자열을 이진 열로 변환합니다. `handle_unknown="ignore"` 매개변수는 테스트 데이터에 학습 데이터에 없는 카테고리가 포함되어 있어도 파이프라인이 오류 없이 작동하도록 보장합니다. ## 교차 검증과 하이퍼파라미터 튜닝 단일 학습/테스트 분할은 어떤 샘플이 각 세트에 포함되는지에 따라 오해의 소지가 있는 결과를 만들어낼 수 있습니다. 교차 검증은 여러 번의 분할을 수행하고 점수의 평균을 계산하여 더 신뢰할 수 있는 성능 추정치를 제공합니다. ```python # sklearn_tuning.py from sklearn.model_selection import cross_val_score, GridSearchCV import numpy as np # 5-fold cross-validation on the full pipeline scores = cross_val_score(pipeline, X_train, y_train, cv=5, scoring="f1") print(f"F1 scores: {scores}") print(f"Mean F1: {scores.mean():.3f} (+/- {scores.std() * 2:.3f})") # Grid search over hyperparameters param_grid = { "classifier__n_estimators": [100, 200, 300], "classifier__max_depth": [3, 4, 5], "classifier__learning_rate": [0.05, 0.1, 0.2], } grid_search = GridSearchCV( pipeline, param_grid, cv=5, scoring="f1", n_jobs=-1, verbose=1, ) grid_search.fit(X_train, y_train) print(f"Best params: {grid_search.best_params_}") print(f"Best F1: {grid_search.best_score_:.3f}") # Evaluate the best model on held-out test set best_model = grid_search.best_estimator_ print(classification_report(y_test, best_model.predict(X_test))) ``` 이중 밑줄 구문(`classifier__max_depth`)은 파이프라인 내부의 중첩된 매개변수를 참조합니다. `n_jobs=-1`은 사용 가능한 모든 CPU 코어에 걸쳐 탐색을 병렬화합니다. > **교차 검증에서의 데이터 누수** > > 분할 전에 전체 데이터셋에 스케일러를 적합시키면, 검증 폴드의 정보가 학습 폴드로 누출됩니다. 스케일러를 Pipeline 내부에 배치하면 각 폴드의 학습 부분에서만 스케일링이 적합되도록 보장합니다. 이것은 머신러닝 면접 질문에서 가장 흔하게 출제되는 실수 패턴입니다. ## 프로덕션을 위한 모델 저장 및 불러오기 학습된 파이프라인은 배포를 위해 직렬화해야 합니다. `joblib` 라이브러리는 Python 내장 `pickle`보다 NumPy 배열을 더 효율적으로 처리합니다. ```python # sklearn_export.py import joblib from pathlib import Path # Save the complete pipeline (preprocessor + model) model_dir = Path("models") model_dir.mkdir(exist_ok=True) joblib.dump(best_model, model_dir / "promotion_model_v1.joblib") # Load and predict in a different process loaded_model = joblib.load(model_dir / "promotion_model_v1.joblib") new_data = pd.DataFrame({ "salary": [75000], "experience_years": [4], "salary_ratio": [1.05], "tenure_days": [730], "department": ["Engineering"], "role_level": ["Mid"], }) prediction = loaded_model.predict(new_data) probability = loaded_model.predict_proba(new_data)[:, 1] print(f"Promoted: {bool(prediction[0])}, Confidence: {probability[0]:.2%}") ``` 모델만 저장하는 대신 전체 파이프라인을 저장하면, 추론 시점에서도 전처리 단계가 동일하게 적용되는 것을 보장할 수 있습니다. ## 결론 - NumPy 벡터화 연산은 Python 반복문을 50~100배 빠른 배열 계산으로 대체하며, 불리언 인덱싱과 브로드캐스팅이 대부분의 필터링 및 변환 작업을 처리합니다 - Pandas의 `.assign()`과 `.transform()`을 활용한 메서드 체이닝은 읽기 쉽고 재현 가능한 데이터 정제 파이프라인을 생성하며, Pandas 2.2의 Copy-on-Write는 무음 변이 버그를 제거합니다 - Scikit-Learn Pipeline은 전처리와 모델링을 하나의 객체로 묶어 학습 세트와 테스트 세트 간의 데이터 누수를 방지합니다 - `GridSearchCV`를 활용한 교차 검증은 신뢰할 수 있는 성능 추정치를 제공하고, 모든 CPU 코어에 걸쳐 하이퍼파라미터 튜닝을 자동화합니다 - `joblib`으로 전체 파이프라인을 직렬화하면 학습 시점과 추론 시점에서 동일한 전처리가 보장됩니다 - 이러한 패턴들을 [데이터 사이언스 면접 문제](/technologies/data-science)로 연습하여 코딩 능력과 통계적 기초에 대한 이해를 모두 검증할 수 있습니다 --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/ko/blog/data-science/python-data-science-numpy-pandas-scikit-learn-2026