🚢 titanic

[python] 탐사일지 01. 닻을 내리기 전에: 질문부터 던지고 데이터 첫인상 파악하기

Floaty 2026. 6. 29. 10:00

질문부터

튜토리얼에서는 'matplotlib으로 그래프를 그려보자!'처럼 도구가 먼저 정해지고 그 다음 어떤 컬럼에 적용해볼지 골랐습니다. 실제로는 순서가 좀 달라요. 먼저 답해야 할 질문이 존재하고, 그 질문에 가장 빠르고 정확하게 답해주는 도구를 그때그때 골라서 사용합니다.

  • 항해일지(튜토리얼): 라이브러리 정하기 → 사용법 익히기 → 일단 그려보기 
  • 탐사일지(분석시도): 질문 정의 → 데이터로 검증 → 필요하 경우 시각화

 

탐사일지에서는 하나의 질문을 합니다. (사실 하나인데 물음표는 3개입니다.) 
타이타닉 침몰에서 생존을 가른 요인은 무엇이었는가? 흔히 알려진 '여성과 아이 먼저(Women and Children First)'라는 통념은 모든 승객에게 동일하게 적용되었는가? 아니면 계급에 따라 다르게 작동했는가?

 

 

 

그리고 탐사일지의 이야기는 이렇게 흘러갑니다. 물론 블로그 글이 9개로 길어지진 않겠지만 일단 흐름은 이렇게 생각하고 있어요.

  1. 데이터 첫인상 파악: 크기, 자료형, 결측치, 기초 통계
  2. 컬럼 감별: 컬럼 중 무엇을 쓰고, 무엇을 버리고, 무엇을 가공할지
  3. 결측치 처리 전략: 그냥 dropna()로 지우지 않기
  4. 특성 공학: 원본 컬럼을 조합해 분석에 더 유용한 새 변수 만들기
  5. 가설 수립: 데이터를 보기 전에 역사적/사회적 맥락에 기반해 검증 가능한 가설 세우기
  6. 가설 검증: 가설 하나당 통계 검정 + 시각화 1개
  7. 다변량으로 교차 검증: 변수 하나씩 본 결과가 다른 변수와 얽혀있는 착시가 아닌지 확인
  8. 최종 변수 선택: 지금까지의 근거를 표로 정리해 이후 모델링이나 보고서에 쓸 변수 목록 확정
  9. 결론과 한계:가설별 결과를 요약

 

 

 

데이터 첫인상 파악하기

가장 먼저 할 일은 아무것도 가공하지 않은, 날 것의 데이터를 있는 그대로 보는 것입니다. 크기가 얼마나 되는지, 컬럼은 몇 개인지, 자료형은 무엇이고, 결측치는 어디에 얼마나 있는지 등등. 이것들을 확인하기 전에는 시각화도 가설도 의미가 없다고 봐도 무방하죠. (물론 저는 미리 파악했으니까 위에서 질문부터 꺼낼 수 있었던 겁니다.)

 

그래서 먼저 CSV 파일을 읽어올게요.

import pandas as pd
import numpy as np

# [1] csv 파일 읽기: 타이타닉 원본 데이터를 DataFrame으로 불러옴
DATA_PATH = "data/titanic.csv"
df = pd.read_csv(DATA_PATH)

print(f"shape: {df.shape}")  # shape: (891, 15)
print(df.info())
"""
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 15 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   survived     891 non-null    int64  
 1   pclass       891 non-null    int64  
 2   sex          891 non-null    object 
 3   age          714 non-null    float64 *
 4   sibsp        891 non-null    int64  
 5   parch        891 non-null    int64  
 6   fare         891 non-null    float64
 7   embarked     889 non-null    object *
 8   class        891 non-null    object 
 9   who          891 non-null    object 
 10  adult_male   891 non-null    bool   
 11  deck         203 non-null    object *
 12  embark_town  889 non-null    object 
 13  alive        891 non-null    object 
 14  alone        891 non-null    bool   
dtypes: bool(2), float64(2), int64(4), object(7)
"""

읽어오는 김에 겸사겸사 데이터 크기와 정보를 좀 찍어봤습니다. 여기서 `*`이 찍혀있는 부분을 잘 봐야 해요. 실제로 저걸 출력했을 때 `*`이 찍히진 않습니다. 포인트가 될 부분이라 제가 표시해둔 겁니다. `shape`을 보면 891명의 승객에 대해 15개의 컬럼이 있는데, `age`, `deck`, `embarked`는 891보다 작습니다. 그러니까 `NaN`이 존재한다는거죠. 이걸 좀 더 확인해봐야겠어요.

 

 

# [2] 결측치 확인: 어떤 컬럼이 비어있는지 값이 얼마나 있는지 파악
missing = df.isnull().sum()
missing_pct = (df.isnull().mean() * 100).round(1)

df_missing = pd.DataFrame({"결측치 수": missing, "결측 비율(%)": missing_pct})[
    missing > 0
]
print(df_missing)
"""
             결측치 수  결측 비율(%)
age              177       19.9
embarked           2        0.2
deck             688       77.2
embark_town        2        0.2
"""

보기 편하게 데이터프레임 형태로 만들어봤어요. `missing > 0` 조건을 준 건 결측치가 없는 컬럼은 지금 볼 필요가 없기 때문입니다. 결과를 보면 `age`는 약 20%가 결측치이고, `deck`는 무려 77.2%가 결측치네요. 사실상 `deck`는 원본 그대로는 사용하기 어렵습니다. `embarked`와 `embark_town`은 0.2%니까 나중에 충분히 쓸 수 잇는 수준이에요. 물론 이것드이 전부 다 쓰이는지는 컬럼 감별 단계에서 따져봐야겠죠.

 

 

 

 

일단 오늘은 여기까지입니다. 인트로를 겸한 글이라 짧게 끝났지만, 다음 글부터는 컬럼 하나하나를 들여다보며 쓸 것과 버릴 것을 가려내는 작업을 시작합니다. 결측치를 어떻게 처리할지도 그때 같이 다룰게요.