💻 준비 코드

Hide code cell source
import pandas as pd

train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')
submission = pd.read_csv('gender_submission.csv')

for df in [train, test]:
    df['Gender'] = df['Sex'].map({'male': 0, 'female': 1})
Hide code cell source
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score

def train_and_predict(train, test):
    # 데이터 준비
    X = train[inc_fts]     # 선택한 특성들
    y = train['Survived']  # 생존 여부
    X_test = test[inc_fts] # 예측해야 할 데이터의 정보들

    # 학습/검증 데이터 분할
    X_train, X_valid, y_train, y_valid = train_test_split(X, y, test_size=0.2, random_state=42)

    # 모델 학습
    model = RandomForestClassifier(random_state=42)
    model.fit(X_train, y_train)

    # 성능 평가
    y_pred = model.predict(X_valid)
    accuracy = accuracy_score(y_valid, y_pred)
    print(f"Validation Score: {accuracy:.5f}")

    # 테스트 데이터 예측 및 저장
    y_test_pred = model.predict(X_test)
    submission['Survived'] = y_test_pred
    submission.to_csv('titanic_pred.csv', index=False)

2. 승선 항구가 비어 있는 두 여성은 누구지?#

데이터 과학 동아리 - 다섯 번째 모임


프롬: 다안 선배! 저번 시간에 성별 정보를 모델에 추가했더니 정확도가 크게 올랐잖아요. 10% 포인트나 향상됐다니 정말 놀라웠어요. 오늘은 또 어떤 변수를 추가해볼 예정인가요?

다안: 지난 번에 Gender 변수 하나로 정확도가 크게 향상된 것을 확인했지. 오늘은 승객들의 승선 항구를 나타내는 ‘Embarked’ 변수를 살펴볼 거야. 타이타닉호는 여러 항구에 정박하면서 승객들을 태웠거든. 각 항구마다 어떤 특성을 가진 승객들이 탑승했는지, 그리고 그것이 생존율과 어떤 관련이 있는지 분석해볼 거야.

코더블: 흥미로운데요. 각 항구별로 탑승한 승객들의 특성이 달랐다면, 생존율에도 차이가 있었을 것 같아요. 먼저 Embarked 변수가 어떤 값들을 가지고 있는지부터 확인해볼까요?

Embarked 피쳐 개요#

프롬: 승선 항구요? 타이타닉호가 어디서 출발했는지 궁금한데, Embarked 피쳐에는 어떤 값들이 있는지 확인해볼 수 있을까요?

다안: 좋은 질문이야! Embarked 변수는 승객들이 어느 항구에서 타이타닉호에 탑승했는지를 나타내는 정보야. 한번 어떤 값들이 있는지 확인해보자.

프롬: 제가 프롬프트로 한번 확인해볼게요!

# 프롬프트
Embarked 피쳐에 어떤 값들이 있는지 보여줘

코더블: 저는 코드로 확인해볼게요. unique() 함수를 사용하면 중복 없이 모든 값을 한 번에 볼 수 있어요.

print(train['Embarked'].unique())
['S' 'C' 'Q' nan]

다안: 보다시피 네 가지 값이 있어:

  • ‘S’: Southampton(영국)

  • ‘C’: Cherbourg(프랑스)

  • ‘Q’: Queenstown(아일랜드)

  • nan: 결측치(Not a Number)

타이타닉호의 항로를 보면, Southampton에서 출발해서 Cherbourg, Queenstown을 거쳐 뉴욕으로 향하는 경로였어.

../_images/22-1.png

프롬: 아하! 그럼 타이타닉은 영국에서 출발해서 프랑스, 아일랜드를 거쳐 미국으로 가는 배였군요! 각 항구마다 서로 다른 나라의 사람들이 탑승했겠네요. 그런데 결측치라는 건 뭔가요? 기록이 없다는 뜻인가요?

다안: 맞아. 결측치란 데이터가 누락된 경우를 말해. 어떤 이유에서든 일부 승객들의 승선 항구 정보가 기록되지 않은 거지. 데이터 분석에서는 이런 결측치를 어떻게 처리할지가 중요한 문제 중 하나야.

코더블: 결측치는 프로그래밍할 때도 자주 문제가 되는데요. 값이 없으면 코드가 오류를 일으키거나 계산 결과가 이상해질 수 있어요. 이런 누락된 값들을 어떻게 채우느냐에 따라 나중에 모델 결과도 달라질 수 있죠?

다안: 정확해! 데이터 전처리에서 결측치 처리는 정말 중요한 단계야. 어떻게 처리하느냐에 따라 모델의 성능이 크게 달라질 수 있지.

가장 간단한 방법은 결측치가 있는 행을 제거하는 거지만, 그러면 귀중한 데이터를 잃을 수 있어. 그래서 보통은 적절한 값으로 대체하는 방법을 많이 사용해.

출발지별 생존율 분석#

프롬: 그렇다면 각 항구에서 탑승한 승객들의 생존율은 어땠을까요? 혹시 특정 항구에서 탑승한 승객들의 생존 확률이 더 높았나요?

다안: 좋은 질문이야! 각 항구에서 탑승한 승객들의 수와 생존율을 분석해보자.

프롬: 그럼 한번 프롬프트로 확인해볼게요!

# 프롬프트
출발지(Embarked)별 승객 수와 생존율을 계산해줘

코더블: 저는 groupby 함수를 사용해서 Embarked 값별로 그룹을 나누고, 승객 수와 생존율을 계산해볼게요.

embarked_stats = train.groupby('Embarked')['Survived'].agg(['count', 'mean'])
embarked_stats
count mean
Embarked
C 168 0.553571
Q 77 0.389610
S 644 0.336957

다안: 결과를 보면 흥미로운 패턴이 보이네:

  1. 승객 분포:

    • Southampton(S)에서 가장 많은 644명이 승선했어

    • Cherbourg(C)에서 168명이 승선했고

    • Queenstown(Q)에서는 가장 적은 77명이 승선했어

    • 그리고 결측치(NaN)가 2명이 있네

  2. 생존율:

    • Cherbourg 승선객의 생존율이 55.4%로 가장 높아

    • Queenstown 승선객의 생존율은 39.0%야

    • Southampton 승선객의 생존율은 33.7%로 가장 낮아

프롬: 와, 탑승 항구에 따라 생존율이 이렇게 차이가 나다니 놀라워요! Cherbourg에서 탑승한 승객들의 생존율이 Southampton보다 20% 포인트 이상 높네요. 왜 이런 차이가 생겼을까요?

다안: 여러 가지 이유가 있을 수 있어. Cherbourg는 프랑스의 부유한 항구도시였어. 그래서 이곳에서 승선한 승객들 중 상당수가 1등실을 이용했을 가능성이 높아. 이전 분석에서 객실 등급과 생존율이 관련이 있다는 걸 확인했잖아? 반면 Southampton은 가장 많은 승객이 탑승한 곳으로, 다양한 계층의 사람들이 섞여 있었을 거야.

코더블: 또 다른 가능성은 승선 순서와 관련이 있을 수도 있어요. Southampton이 첫 번째 항구였으니 그곳에서 탑승한 승객들은 배의 내부 깊숙이 위치한 객실을 배정받았을 수도 있고, 나중에 탑승한 승객들은 상대적으로 출구와 가까운 객실에 배정됐을 수도 있죠. 그게 침몰 상황에서 탈출 가능성에 영향을 미쳤을 수 있어요.

프롬: 아! 그럼 지금 살펴본 각 항구별 생존율 차이가 실제로는 객실 등급 때문이었을 수도 있겠네요. Cherbourg에서 탑승한 사람들이 더 부유해서 더 좋은 객실을 이용했고, 그래서 생존율이 높았을 수 있겠군요!

결측치의 수 확인#

프롬: 그런데 NaN 값이 있다는 건 몇몇 승객들의 승선 항구 정보가 누락됐다는 거잖아요. 정확히 몇 명이나 그런 건가요? 그리고 이런 결측치는 어떻게 처리해야 하나요?

다안: 좋은 질문이야! 먼저 결측치가 정확히 몇 개나 있는지 train 데이터와 test 데이터 각각에서 확인해보자.

프롬: 프롬프트로 알아볼게요!

# 프롬프트
train과 test의 Embarked 결측치 개수를 알려줘

코더블: 저는 isnull() 함수와 sum() 함수를 사용해서 결측치 개수를 계산해볼게요.

print(f"train 데이터의 Embarked 결측치 개수: {train['Embarked'].isnull().sum()}")
print(f"test 데이터의 Embarked 결측치 개수: {test['Embarked'].isnull().sum()}")
train 데이터의 Embarked 결측치 개수: 2
test 데이터의 Embarked 결측치 개수: 0

다안: 결과를 보니 train 데이터에는 2개, test 데이터에는 0개의 결측치가 있네. train 데이터의 결측치 비율은 전체 891명 중 2명으로 약 0.2%에 불과해. 굉장히 적은 비율이지만, 정확한 예측을 위해서는 이 결측치들도 적절히 처리하는 게 좋아.

프롬: 아, 그럼 train 데이터에만 결측치가 있고 test 데이터에는 없군요. 이 결측치가 있는 두 승객은 누구인지 궁금해요. 어떤 특별한 이유가 있었을까요?

결측치의 비밀 추적하기#

다안: 그래, 이제 승선 항구 정보가 누락된 두 승객의 정보를 자세히 살펴보자. 이들이 어떤 사람들이었는지 알면 결측치를 어떻게 처리할지 더 좋은 결정을 내릴 수 있을 거야.

프롬: 그 승객들의 정보를 확인해볼게요!

# 프롬프트
Embarked가 결측치인 승객들의 정보를 보여줘

코더블: 저는 다음 코드로 결측치인 행만 선택해서 확인해볼게요.

train[train['Embarked'].isnull()]
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked Gender
61 62 1 1 Icard, Miss. Amelie female 38.0 0 0 113572 80.0 B28 NaN 1
829 830 1 1 Stone, Mrs. George Nelson (Martha Evelyn) female 62.0 0 0 113572 80.0 B28 NaN 1

다안: 흥미로운 정보들이 보이네! 이 두 승객의 공통점과 차이점을 정리해보면:

  1. 공통점:

    • 두 승객 모두 1등실 여성 승객이야

    • 운임이 모두 80파운드로 정확히 동일해

    • 같은 티켓 번호(PC 17755)를 가지고 있어

    • 두 승객 모두 생존했어

    • 둘 다 가족(SibSp=0, Parch=0)과 함께 타지 않았어

  2. 차이점:

    • 다른 성(last name)을 가지고 있어 (Stone, Icard)

    • 나이는 38세(Icard)와 62세(Stone)로 24살 차이가 나

프롬: 우와! 이거 마치 추리 소설 같아요! 같은 티켓을 썼고, 운임도 똑같고… 근데 성이 다르니 가족은 아닐 것 같고… 나이 차이도 꽤 나고… 이 두 사람은 도대체 어떤 관계였을까요? 너무 궁금해요!

다안: 흥미로운 질문이야! 1912년 타이타닉호가 항해하던 시절에는 부유한 여성이 혼자 여행하는 경우가 드물었어. 두 사람의 관계에 대해 여러 가지 가능성을 생각해볼 수 있겠지.

이런 추리는 데이터 분석을 더 재미있게 만들어주기도 하지만, 실제적인 도움도 줘. 나중에 우리가 이 두 승객의 결측치를 어떻게 처리할지 결정할 때, 그들이 같은 항구에서 승선했을 거라는 점은 중요한 단서가 될 거야.

생각해보기

이 두 여성의 관계는 무엇이었을까요? 다음과 같은 가능성들을 생각해볼 수 있습니다:

  • 부유한 여성과 그의 여행 동반자(companion)

  • 귀부인과 그의 가정교사 또는 개인비서

  • 친구 사이였을까요? 아니면 다른 관계였을까요?

여러분은 어떤 관계였을 것 같나요?

코더블: 이런 분석은 단순한 호기심을 넘어서 실제 데이터 처리에도 도움이 돼요. 이 두 승객이 함께 여행했다면, 분명히 같은 항구에서 승선했을 테니까요. 그러므로 둘 다 같은 값으로 결측치를 채워야 할 것 같아요.

1등석 승객들의 승선 항구 분석#

프롬: 그럼 두 사람이 어떤 항구에서 탑승했을지 어떻게 알 수 있을까요? 단서가 있을까요?

다안: 좋은 질문이야! 이 두 승객이 1등석을 이용했다는 점에 주목해보자. 1등석 승객들은 주로 어느 항구에서 승선했는지 확인하면, 이 두 승객의 승선 항구를 추정하는 데 도움이 될 거야.

프롬: 한번 1등석 승객들의 승선 항구를 확인해볼게요!

# 프롬프트
Pclass가 1인 승객들의 Embarked 값이 몇 명인지 세어 줘

코더블: 저는 다음 코드로 확인해볼게요. value_counts() 메서드를 사용하면 각 값별 개수를 쉽게 구할 수 있어요.

train[train['Pclass'] == 1]['Embarked'].value_counts()
Embarked
S    127
C     85
Q      2
Name: count, dtype: int64

다안: 결과를 보면 1등석 승객들의 승선 패턴이 명확하게 나타나네:

  • Southampton(S)에서 127명이 승선했어

  • Cherbourg(C)에서 85명이 승선했어

  • Queenstown(Q)에서는 단 2명만 승선했어

이 패턴은 우리의 결측치 승객들이 어느 항구에서 승선했을지 추정하는 데 중요한 단서가 돼. Queenstown에서는 거의 1등석 승객이 없었으니 이 두 승객이 그곳에서 탑승했을 가능성은 매우 낮아 보여.

프롬: 그럼 Southampton이나 Cherbourg 중 하나겠네요. Southampton에서 더 많은 1등석 승객이 탑승했으니, 이 두 승객도 Southampton에서 탑승했을 가능성이 더 높을 것 같아요!

코더블: 음, 그렇게만 볼 수는 없을 것 같아요. 숫자로만 보면 Southampton의 1등석 승객이 많지만, 비율로 따지면 좀 다를 수도 있어요. Cherbourg에서 탑승한 승객들 중에는 1등석 비율이 더 높을 수도 있으니까요. 아까 승선 항구별 생존율을 봤을 때, Cherbourg 승객의 생존율이 훨씬 높았잖아요. 그건 어쩌면 1등석 승객 비율이 높았기 때문일 수도 있지 않을까요?

둘 다 좋은 의견을 제시했어. 실제로 데이터를 분석할 때는 이렇게 여러 관점에서 생각해보는 게 중요해. 우리가 가진 정보만으로는 두 가지 가능성이 모두 있어 보이지만, 일단 통계적으로 더 가능성이 높은 쪽을 선택해야 할 때가 있지. 결측치를 처리하는 건 때로는 이런 추정과 결정이 필요한 과정이야.

프롬: 아하! 그럼 항구별 생존율 차이가 사실은 객실 등급 때문이었을 수도 있겠네요. 그래도 수치상으로는 Southampton에서 더 많은 1등석 승객이 탑승했으니, 확률적으로는 여전히 Southampton이 더 가능성이 높아 보여요.

결측치 처리하기#

다안: 자, 이제 우리가 수집한 단서들을 종합해볼까? 두 승객이 함께 여행했고 같은 항구에서 승선했을 것이라는 점, 그리고 1등석 승객 분포를 봤을 때 어느 쪽이 더 가능성이 높아 보여? 결측치는 어떻게 채우는 게 좋을까?

프롬: 음… 두 가지 가능성이 있지만, 더 많은 1등석 승객이 Southampton에서 탑승했다는 게 결정적인 단서인 것 같아요. 저는 ‘S’로 채우는 게 좋을 것 같아요!

코더블: 저도 동의해요. Cherbourg의 가능성도 있지만, 절대적인 숫자로는 Southampton이 더 높아 보여요. 127명 대 85명이니까요. 결측치를 처리할 때는 가장 가능성이 높은 값으로 채우는 게 일반적인 방법이라고 알고 있어요.

다안: 좋아, 그럼 의견이 모였네. 두 승객의 결측치를 ‘S’로 채워넣도록 하자.

프롬: 그럼 결측치를 ‘S’로 채워볼게요!

# 프롬프트
1. Embarked 값이 비어 있는 승객들의 값을 'S'로 채워 줘
2. PassengerId가 62 또는 830인 승객들의 정보를 보여 줘

코더블: 저는 fillna 메서드를 사용해서 결측치를 ‘S’로 채워보겠습니다. 이 메서드는 결측치(NaN)를 지정한 값으로 대체해주는 함수예요.

train['Embarked'] = train['Embarked'].fillna('S')
train[train['PassengerId'].isin([62, 830])]
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked Gender
61 62 1 1 Icard, Miss. Amelie female 38.0 0 0 113572 80.0 B28 S 1
829 830 1 1 Stone, Mrs. George Nelson (Martha Evelyn) female 62.0 0 0 113572 80.0 B28 S 1

다안: 좋아! 이제 두 승객의 Embarked 값이 NaN에서 ‘S’로 변경된 것을 확인할 수 있어. 이렇게 하면 train 데이터의 Embarked 결측치가 모두 처리된 거야.

프롬: 데이터 분석이 정말 탐정 일처럼 느껴져요! 작은 단서들을 모아서 숨겨진 이야기를 찾아내는 것 같아요. 너무 재미있어요!

다안: 맞아, 데이터 분석은 종종 탐정 일과 비슷해. 주어진, 때로는 불완전한 정보들로부터 의미 있는 패턴과 이야기를 찾아내는 과정이니까.

인코딩 방식 비교#

다안: 이제 Embarked 변수를 우리 머신러닝 모델이 이해할 수 있는 숫자 형태로 변환해야 해. 이것을 ‘인코딩(encoding)’이라고 해.

프롬: 아, 지난 번에 남성을 0, 여성을 1로 바꾼 것처럼요?

다안: 맞아! 그때는 레이블 인코딩(Label Encoding)이라는 방식을 사용했어. 하지만 Embarked는 세 개의 카테고리(S, C, Q)가 있고, 이들 사이에는 크기나 순서 관계가 없어. 이런 경우에는 원-핫 인코딩(One-Hot Encoding)이라는 방식이 더 적합해.

다안: 두 인코딩 방식의 차이를 설명해줄게. 레이블 인코딩은 각 카테고리에 숫자를 할당하는 방식인데, 이렇게 하면 모델이 숫자 크기에 의미가 있다고 오해할 수 있어.

예를 들어, Southampton=0, Cherbourg=1, Queenstown=2와 같이 변환하면, 모델은 Queenstown이 Southampton보다 “두 배 더 중요하다”고 잘못 해석할 수 있지. 항구 사이에는 그런 순서나 크기 관계가 없는데도 말이야.

반면 원-핫 인코딩은 각 카테고리마다 별도의 컬럼을 만들어서 해당되면 1, 아니면 0으로 표시하는 방식이야. 이렇게 하면 각 항구를 완전히 독립적인 특성으로 처리할 수 있어.

원-핫 인코딩을 사용하면 각 항구가 독립적인 변수로 처리돼. 이제 pandas의 get_dummies 함수를 사용해서 Embarked를 원-핫 인코딩 해보자.

프롬: 원-핫 인코딩으로 변환해볼게요!

# 프롬프트
train과 test의 Embarked 피쳐를 One-Hot Encoding으로 추가해줘

코더블: 저는 pandas의 get_dummies 함수를 사용해서 원-핫 인코딩을 구현해볼게요. 이 함수는 범주형 변수를 자동으로 이진(0과 1) 더미 변수로 변환해주는 편리한 기능이에요.

embarked_dummies_train = pd.get_dummies(train['Embarked'], prefix='Embarked')
train = pd.concat([train, embarked_dummies_train], axis=1)

embarked_dummies_test = pd.get_dummies(test['Embarked'], prefix='Embarked')
test = pd.concat([test, embarked_dummies_test], axis=1)

train[['PassengerId', 'Embarked', 'Embarked_C', 'Embarked_Q', 'Embarked_S']].head()
PassengerId Embarked Embarked_C Embarked_Q Embarked_S
0 1 S False False True
1 2 C True False False
2 3 S False False True
3 4 S False False True
4 5 S False False True

프롬: 오, 새로운 컬럼들이 생겼네요! ‘Embarked_S’, ‘Embarked_C’, ‘Embarked_Q’ 이렇게요. 그러니까 첫 번째 승객은 Southampton에서 탑승했으니 ‘Embarked_S’가 1이고 나머지는 0인 거군요?

다안: 정확해! 이렇게 원-핫 인코딩을 하면 각 항구를 별도의 컬럼으로 표현할 수 있어. 이 방식은 항구 간에 크기 관계가 생기는 것을 방지하고, 각 항구의 영향을 독립적으로 분석할 수 있게 해줘.

One-Hot Encoding의 장단점

장점:

  • 범주형 변수들 사이에 크기 관계가 생기는 것을 방지할 수 있습니다

  • 각 범주가 독립적으로 처리되어 모델이 더 정확한 패턴을 학습할 수 있습니다

단점:

  • 범주의 종류가 많으면 생성되는 피쳐의 수도 많아집니다

  • 메모리 사용량이 증가하고 학습 시간이 길어질 수 있습니다

  • 과적합의 위험이 증가할 수 있습니다

모델 학습과 예측#

다안: 이제 원-핫 인코딩으로 생성된 새로운 피쳐들을 우리 모델에 추가해보자. 이전에 사용했던 변수들과 함께 이 새로운 변수들을 사용하면 예측 정확도가 어떻게 달라질지 확인해볼 거야.

프롬: 프롬: 이제 승선 항구 변수들을 inc_fts 리스트에 추가해볼게요!

# 프롬프트
승선 항구 관련 피쳐들(Embarked_C, Embarked_Q, Embarked_S)을 inc_fts에 추가해줘

코더블: 저도 승선 항구 관련 변수들을 기존 변수 리스트에 추가해볼게요.

inc_fts = ['Pclass', 'Age', 'SibSp', 'Parch', 'Fare']
inc_fts += ['Gender']
inc_fts += ['Embarked_C', 'Embarked_Q', 'Embarked_S']

print(inc_fts)
['Pclass', 'Age', 'SibSp', 'Parch', 'Fare', 'Gender', 'Embarked_C', 'Embarked_Q', 'Embarked_S']

다안: 좋아! 이제 우리는 총 9개의 변수를 사용하게 됐어:

  • 처음에 사용했던 5개의 수치형 변수(Pclass, Age, SibSp, Parch, Fare)

  • 지난 시간에 추가한 Gender 변수

  • 오늘 추가한 3개의 항구 관련 변수(Embarked_C, Embarked_Q, Embarked_S)

다안: 이 변수들을 사용해서 모델을 학습시켜보자. 챕터 시작부분에 ‘준비 코드’로 미리 정의해둔 train_and_predict 함수를 사용하면 모델 학습부터 캐글 제출 파일 생성까지 한 번에 할 수 있어.

train_and_predict(train, test)
Validation Score: 0.82123

프롬: 와! 모델의 Validation Score가 0.82123으로 나왔네요. 그런데 이전에 Gender만 추가했을 때와 같은 점수인데요?

다안: 맞아, 검증 세트에서의 점수는 이전과 동일하네. 하지만 실제 test 데이터에 대한 예측 결과는 달라질 수 있어. 캐글에 제출해봐야 정확한 차이를 알 수 있을 거야.

프롬: 제가 방금 캐글에 제출해봤어요! 점수가 0.74880이 나왔어요. 이전에 Gender만 사용했을 때는 0.73444였으니, 승선 항구 정보를 추가했더니 약간 상승했네요. 418명 중에 313명을 맞췄다는 의미니까, 이전보다 약 6명 더 정확하게 예측한 셈이에요.

다안: 좋은 소식이네! 승선 항구 정보가 생존 예측에 도움이 된다는 걸 확인했어. 지금까지의 결과를 표로 정리하면 이렇게 돼:

버전

피쳐 개수

Val. Score

Public Score

맞은 사람 수

설명

1.3

5개

0.73184

0.63397

265명

5개의 numeric 피쳐 사용

2.1

6개

0.82123

0.73444

307명

Gender 피쳐 추가

2.2

9개

0.82123

0.74880

313명

승선 항구 정보 추가

다안: 이 결과에서 몇 가지 중요한 인사이트를 얻을 수 있어:

  1. Gender 변수의 추가로 42명의 승객에 대한 예측이 개선됐어 - 이는 성별이 생존에 매우 결정적인 요소였음을 보여줘

  2. Embarked 관련 변수들을 추가하니 6명의 예측이 더 개선됐어 - 승선 항구도 유의미한 영향을 미친 것이지

  3. 검증 점수는 동일하지만 실제 테스트 데이터에 대한 예측은 달라졌어 - 이는 변수 추가의 효과가 데이터 세트마다 다르게 나타날 수 있다는 점을 시사해

프롬: 성별이 정말 중요한 변수였네요. 그에 비하면 승선 항구는 상대적으로 영향이 작았지만, 그래도 6명이나 더 정확하게 예측할 수 있게 해줬어요!

다안: 맞아, 그리고 앞서 분석에서 보았듯이, Cherbourg에서 승선한 승객들의 생존율(55.4%)이 다른 항구보다 높았던 것이 이런 예측 정확도 향상에 기여했을 거야. 특히 Cherbourg에서 승선한 승객들 중 1등실 승객의 비율이 높았다는 점도 중요한 요인이었을 거야.

프롬: 진짜 재밌어요! 타이타닉 데이터에서 숨겨진 패턴을 하나씩 발견하는 것 같아요. 다음엔 또 어떤 변수를 살펴볼 건가요?

다안: 다음 시간에는 요금(Fare) 데이터를 자세히 분석해볼 거야. 요금은 객실 등급과 밀접한 관련이 있을 테니까, 그 관계가 생존율에 어떤 영향을 미쳤는지 살펴볼 거야. 특히 test 데이터에 요금 정보가 누락된 승객이 있어서, 이 결측치를 어떻게 처리하면 좋을지도 함께 고민해볼 거야.

프롬: 와, 기대돼요! 데이터 속에 숨겨진 이야기들을 계속 발견해가는 게 너무 재밌어요. 승객들의 객실 등급이나 승선 항구에 따라 생존 확률이 달랐다는 사실도 흥미로웠는데, 요금 정보는 또 어떤 패턴을 보여줄지 궁금해요!

코더블: 저는 특히 결측치 처리 방법을 배우는 게 기대돼요. 실제 데이터 분석에서는 결측치가 항상 있기 마련이니까요. 다양한 결측치 처리 전략을 실습해볼 수 있을 것 같아요.

다안: 그래, 다음 시간도 기대해! 오늘은 승선 항구 정보를 분석하고 모델에 추가함으로써 예측 정확도를 향상시키는 데 성공했어. 그리고 결측치 처리의 중요성도 배웠지. 다음 시간에도 이런 기법들을 활용해서 모델을 더욱 발전시켜 나가보자!

직접 해보기

결측치 처리와 인코딩에 대한 이해를 높이기 위해 다음 실습을 해보세요:

  1. 결측치 처리 실험

    # 결측치를 'C'로 채우기
    train['Embarked'] = train['Embarked'].fillna('C')
    
    # 또는 'Q'로 채우기
    train['Embarked'] = train['Embarked'].fillna('Q')
    

    각각의 경우 모델의 성능이 어떻게 달라지는지 비교해보세요. 왜 그런 차이가 났을까요?

  2. 다른 방식의 인코딩 시도

    • 레이블 인코딩으로 처리해보기

    train['Embarked_Label'] = train['Embarked'].map({'S': 0, 'C': 1, 'Q': 2})
    
    • 원-핫 인코딩과 비교했을 때 어떤 차이가 있나요?

  3. 승선 항구와 다른 변수의 관계 분석

    # 각 항구별 평균 요금 확인
    train.groupby('Embarked')['Fare'].mean()
    
    # 각 항구별 객실 등급 분포 확인
    pd.crosstab(train['Embarked'], train['Pclass'])
    

    이 결과들은 앞서 본 생존율 차이를 어떻게 설명할 수 있나요?

도전해보세요!

  1. 승선 항구와 성별을 함께 고려하면 어떤 패턴이 보일까요?

  2. 각 항구별로 나이 분포는 어떻게 다를까요?

  3. 요금과 승선 항구 사이에는 어떤 관계가 있을까요?

이러한 분석을 통해 데이터 속에 숨어있는 새로운 패턴을 발견할 수 있습니다.

프롬프트 실험하기

AI에게 분석을 요청할 때는 다양한 방식으로 프롬프트를 작성해볼 수 있습니다:

  1. 기본적인 분석 요청:

    Embarked 컬럼의 값들을 보여줘
    
  2. 구체적인 분석 요청:

     항구별로 1등석, 2등석, 3등석 승객의 수와 비율을 계산해줘
    
  3. 시각화 요청:

    항구별 승객 수와 생존율을 막대그래프로 보여줘
    
  4. 복합적인 분석 요청:

     항구별로 성별 분포와 평균 요금을 함께 보여주고, 
    이것이 생존율과 어떤 관계가 있는지 분석해줘
    

같은 데이터도 다양한 각도에서 분석할 수 있습니다. 여러분만의 프롬프트로 새로운 인사이트를 발견해보세요!

데이터 속 숨은 이야기

승선 항구 결측치 승객들의 실제 이야기

우리가 분석한 결측치 승객들의 실제 이야기를 찾아보았습니다!

Encyclopedia Titanica에서 이 두 승객에 대한 흥미로운 기록을 발견했습니다. 데이터에서 우리가 추측했던 것처럼, 이들은 실제로 함께 여행했던 동행이었습니다.

../_images/22-2.png

62세의 Stone 부인(사진)과 38세의 Miss Icard는 고용주와 가정부 관계였습니다. 둘은 Southampton에서 승선했고, 다행히 모두 생존했습니다. 특히 감동적인 것은 Stone 부인이 사망 시 Icard에게 상당한 금액의 유산과 개인 소지품을 남겼다는 기록입니다. 24년의 나이 차이를 넘어, 고용주와 가정부로 시작된 두 사람의 관계가 깊은 신뢰와 우정으로 발전했던 것 같네요.

이렇게 차가운 숫자로 이루어진 데이터 속에도 따뜻한 인간 드라마가 숨어있습니다. 우리가 분석한 결측치 처리가 단순한 데이터 작업이 아닌, 실제 존재했던 사람들의 이야기라는 점이 특별하게 다가옵니다.

더 자세한 내용이 궁금하시다면: