💻 준비 코드
Show 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})
df.loc[df['Embarked'].isnull(), 'Embarked'] = 'S'
4. 타이타닉에 무임승객이 있는 거야? 설마…#
이전 섹션에서 우리는 Storey의 누락된 요금을 다른 3등석 Southampton 승객들의 중앙값으로 채웠습니다. 하지만 결측치가 발생한 이유를 다른 관점에서 생각해볼까요? 1912년 타이타닉호는 당시 세계에서 가장 큰 여객선이었고, 최고급 시설을 자랑하는 호화 유람선이었습니다. 그런 배에 무임승객이 있었을까요? 설마 Storey는 공짜로 탑승한 것은 아닐까요? 이런 대담한 가설을 세우고 데이터를 탐구해보는 것도 재미있을 것 같네요. 먼저 train 데이터에서 요금을 지불하지 않은(Fare=0) 승객이 있는지 찾아보면서, 우리의 가설을 검증해보도록 하겠습니다.
무임승객 탐색#
무임승객이 있었는지 확인하기 위해 먼저 train 데이터에서 요금이 0인 승객들을 찾아보겠습니다. 이런 승객들의 패턴을 분석하면 Storey의 결측치를 어떻게 처리할지에 대한 힌트를 얻을 수 있을 것입니다.
📝 프롬프트
train 데이터에서 요금(Fare)이 0인 승객들의 정보를 보여줘
💻 코드 & 실행결과
train[train['Fare'] == 0]
PassengerId | Survived | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | Gender | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
179 | 180 | 0 | 3 | Leonard, Mr. Lionel | male | 36.0 | 0 | 0 | LINE | 0.0 | NaN | S | 0 |
263 | 264 | 0 | 1 | Harrison, Mr. William | male | 40.0 | 0 | 0 | 112059 | 0.0 | B94 | S | 0 |
271 | 272 | 1 | 3 | Tornquist, Mr. William Henry | male | 25.0 | 0 | 0 | LINE | 0.0 | NaN | S | 0 |
277 | 278 | 0 | 2 | Parkes, Mr. Francis "Frank" | male | NaN | 0 | 0 | 239853 | 0.0 | NaN | S | 0 |
302 | 303 | 0 | 3 | Johnson, Mr. William Cahoone Jr | male | 19.0 | 0 | 0 | LINE | 0.0 | NaN | S | 0 |
413 | 414 | 0 | 2 | Cunningham, Mr. Alfred Fleming | male | NaN | 0 | 0 | 239853 | 0.0 | NaN | S | 0 |
466 | 467 | 0 | 2 | Campbell, Mr. William | male | NaN | 0 | 0 | 239853 | 0.0 | NaN | S | 0 |
481 | 482 | 0 | 2 | Frost, Mr. Anthony Wood "Archie" | male | NaN | 0 | 0 | 239854 | 0.0 | NaN | S | 0 |
597 | 598 | 0 | 3 | Johnson, Mr. Alfred | male | 49.0 | 0 | 0 | LINE | 0.0 | NaN | S | 0 |
633 | 634 | 0 | 1 | Parr, Mr. William Henry Marsh | male | NaN | 0 | 0 | 112052 | 0.0 | NaN | S | 0 |
674 | 675 | 0 | 2 | Watson, Mr. Ennis Hastings | male | NaN | 0 | 0 | 239856 | 0.0 | NaN | S | 0 |
732 | 733 | 0 | 2 | Knight, Mr. Robert J | male | NaN | 0 | 0 | 239855 | 0.0 | NaN | S | 0 |
806 | 807 | 0 | 1 | Andrews, Mr. Thomas Jr | male | 39.0 | 0 | 0 | 112050 | 0.0 | A36 | S | 0 |
815 | 816 | 0 | 1 | Fry, Mr. Richard | male | NaN | 0 | 0 | 112058 | 0.0 | B102 | S | 0 |
822 | 823 | 0 | 1 | Reuchlin, Jonkheer. John George | male | 38.0 | 0 | 0 | 19972 | 0.0 | NaN | S | 0 |
이 코드는 train 데이터에서 요금이 0인 승객들을 찾아냅니다. 실행 결과를 보면 총 15명의 승객이 요금 0으로 기록되어 있음을 알 수 있습니다.
이는 매우 흥미로운 발견입니다! 실제로 무임승객이 존재했다는 것일까요? 하지만 섣부른 결론을 내리기 전에, 이 승객들의 특징을 좀 더 자세히 살펴볼 필요가 있겠습니다. 특히 이들의 객실 등급, 성별, 나이 등의 패턴을 분석하면 요금이 0으로 기록된 이유를 추측해볼 수 있을 것 같네요.
Fare가 0인 승객 비교 분석#
train 데이터에서 발견한 무임승객들이 특이한 케이스인지 확인하기 위해, test 데이터에서도 유사한 패턴이 나타나는지 살펴보겠습니다. 두 데이터셋을 비교 분석하면 이것이 실제 존재했던 관행인지 아니면 데이터 오류인지 판단하는데 도움이 될 것입니다.
📝 프롬프트
test 데이터에서 요금(Fare)이 0인 승객들의 정보를 보여줘
💻 코드 & 실행결과
test[test['Fare'] == 0]
PassengerId | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | Gender | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
266 | 1158 | 1 | Chisholm, Mr. Roderick Robert Crispin | male | NaN | 0 | 0 | 112051 | 0.0 | NaN | S | 0 |
372 | 1264 | 1 | Ismay, Mr. Joseph Bruce | male | 49.0 | 0 | 0 | 112058 | 0.0 | B52 B54 B56 | S | 0 |
이 코드는 test 데이터에서 요금이 0인 승객들을 찾아냅니다. 실행 결과를 보면 test 데이터에서도 Fare=0인 승객들이 존재함을 알 수 있습니다.
이는 매우 중요한 발견입니다. train 데이터와 test 데이터 모두에서 요금이 0인 승객들이 발견된다는 것은, 이것이 단순한 데이터 오류가 아닐 가능성을 시사합니다. 즉, 어떤 특별한 이유로 일부 승객들은 실제로 요금을 지불하지 않았을 수 있다는 것입니다.
이제 이 승객들의 공통된 특징을 더 자세히 분석해볼 필요가 있겠네요. 특히 이들의 객실 등급, 탑승 항구 등을 살펴보면 요금이 0인 이유를 추측해볼 수 있을 것 같습니다.
Fare가 0인 승객들의 명단을 자세히 살펴보면, 한 승객의 정보가 특히 눈에 띕니다. B52, B54, B56, 이렇게 무려 세 개의 1등석 객실을 사용한 승객이 있네요. 그것도 요금은 0이라니, 무언가 특별한 이유가 있었을 것 같지 않나요?
이 승객의 이름이 바로 ‘Bruce Ismay’입니다. 혹시 타이타닉 영화를 보신 분들이라면 이 이름이 낯설지 않을 수도 있습니다. Ismay는 타이타닉호를 만든 White Star Line의 회장이었죠.

White Star Line의 회장이었던 J. Bruce Ismay. 타이타닉호 침몰 당시 구명보트를 타고 탈출한 것으로 유명합니다. 데이터에서 발견된 그의 무료 탑승 기록은 선주로서의 그의 위치를 반영합니다.
Ismay는 영화에서도 중요한 인물로 등장합니다. 특히 아래 장면은 그가 구명보트에 탑승하는 결정적인 순간을 보여줍니다. 많은 여성과 어린이들이 아직 배에 남아있는 상황에서 그가 구명보트에 탑승한 것은 오랫동안 논란이 되었습니다.
Ismay의 요금이 0으로 기록된 것은 아마도 자신의 회사 배에 탑승했기 때문일 것입니다. 이를 통해 우리는 Fare=0이 단순한 데이터 오류가 아니라 실제 존재했던 특별한 경우임을 확인할 수 있습니다.
그렇다면 train 데이터에도 이런 특별한 인물이 있는지 살펴볼까요? 요금이 0인 승객들 중에서 혹시 눈에 익은 이름이 있나요?
아, 13번째에 ‘Andrews, Mr Thomas Jr’라는 이름이 보입니다. 타이타닉 영화의 팬이라면 이 이름 역시 낯설지 않을 것입니다. Thomas Andrews는 타이타닉호의 설계자였죠.

Harland and Wolff 조선소의 수석 설계자였던 Thomas Andrews. 그는 타이타닉호의 설계를 총괄했으며, 침몰 당시 마지막까지 승객들의 탈출을 도왔다고 합니다. 결국 그는 자신이 설계한 배와 함께 침몰했습니다.
영화의 이 장면에서 Andrews(Victor Garber 분)는 설계도를 펼쳐놓고 배의 운명을 설명합니다. 자신이 설계한 배가 1~2시간 안에 침몰할 것이라는 냉정한 판단을 내리는 순간입니다. 이는 그가 얼마나 배를 정확히 이해하고 있었는지를 보여주는 동시에, 설계자로서 느꼈을 비통함을 잘 표현하는 장면입니다.
Andrews 역시 요금이 0으로 기록된 것은 당연해 보입니다. 배의 설계자로서 그는 첫 항해에서 발생할 수 있는 문제점들을 점검하기 위해 탑승했을 테니까요. Ismay와 마찬가지로 그의 탑승은 업무의 연장선이었던 것입니다.
이렇게 우리는 데이터 속에서 두 명의 중요한 인물을 발견했습니다. 한 명은 살아남았고, 한 명은 침몰과 함께 사라졌죠. 그렇다면 나머지 무임승객들은 어떤 사연을 가지고 있었을까요?
그리고 이 발견은 우리에게 또 다른 흥미로운 가능성을 제시합니다. 앞서 우리가 고민했던 Storey의 요금 결측치, 어쩌면 그도 특별한 이유로 무임승객이었을지 모릅니다. 물론 이는 추측에 불과하지만, 실제로 무임승객이 존재했다는 것을 확인한 지금, Storey의 결측치를 0으로 채우는 것도 하나의 타당한 방법이 될 수 있겠네요.
이제 이 가설을 바탕으로 Storey의 결측치를 0으로 채우고, 새로운 모델을 학습시켜 보도록 하겠습니다. 과연 이 방법이 이전의 중앙값을 사용했을 때보다 더 나은 결과를 보여줄까요?
결측치를 0으로 채우기#
앞서의 분석을 바탕으로, Storey의 누락된 요금을 0으로 채워보도록 하겠습니다. 이전에는 같은 조건(3등석, Southampton)의 승객들의 중앙값을 사용했지만, 이번에는 그가 무임승객이었을 가능성을 고려하여 0을 사용해보겠습니다.
📝 프롬프트
1. Fare가 결측치인 승객의 요금을 0으로 채워줘
2. 해당 승객(PassengerId가 1044)의 정보를 다시 출력해줘
💻 코드 & 실행결과
test['Fare'] = test['Fare'].fillna(0)
test[test['PassengerId'] == 1044]
PassengerId | Pclass | Name | Sex | Age | SibSp | Parch | Ticket | Fare | Cabin | Embarked | Gender | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
152 | 1044 | 3 | Storey, Mr. Thomas | male | 60.5 | 0 | 0 | 3701 | 0.0 | NaN | S | 0 |
이 코드는 두 가지 작업을 수행합니다:
test['Fare'].fillna(0)
로 Fare 컬럼의 결측치를 0으로 채웁니다.PassengerId == 1044
조건으로 Storey의 정보를 확인합니다.
실행 결과를 보면 Storey의 요금이 이제 0으로 변경된 것을 확인할 수 있습니다. 이전 섹션에서는 3등석 Southampton 승객들의 중앙값으로 채웠던 것과는 다른 접근이네요.
이렇게 결측치를 다른 방식으로 처리하면 모델의 예측 결과도 달라질 수 있습니다. 과연 이 방법이 더 나은 예측 결과를 가져올까요? 이제 새로운 데이터로 모델을 학습시켜 보도록 하겠습니다.
모델 학습 결과, Validation Score는 0.82123, Public Score는 0.74880이 나왔습니다. 흥미롭게도 이는 이전 섹션에서 중앙값으로 결측치를 채웠을 때와 정확히 같은 점수입니다. test 데이터의 단 한 명에 대한 요금 처리 방식의 차이가 전체 예측 결과에 큰 영향을 주지 않은 것이죠.
하지만 이것이 결측치 처리가 중요하지 않다는 의미는 아닙니다. 지금은 Random Forest 알고리즘을 사용해 학습을 했는데요, 다른 머신러닝 알고리즘들을 사용하는 경우에는 결측치 처리 방식에 따라 예측 결과가 달라질 수 있습니다. 또한 실제 현업에서는 결측치가 훨씬 많은 경우가 일반적이므로, 적절한 결측치 처리는 매우 중요한 문제입니다.
더 나아가 우리가 시도한 두 가지 방식(중앙값과 0) 외에도 다양한 결측치 처리 방법이 있습니다. 앞으로 새로운 머신러닝 알고리즘을 배우면서, 각 알고리즘의 특성에 맞는 최적의 결측치 처리 방법도 함께 고민해보도록 하겠습니다.
평가지표가 결과에 미치는 영향
우리가 사용한 accuracy라는 평가지표도 세 가지 실험의 결과가 같게 나온 이유 중 하나입니다. 사실 Random Forest 모델은 각 케이스마다 다른 확률값을 예측했는데요:
결측치를 채우지 않은 경우: 95.375%의 생존 확률
중앙값으로 채운 경우: 97%의 생존 확률
0으로 채운 경우: 98%의 생존 확률
하지만 accuracy는 이 확률값들을 0과 1로 변환하여 평가합니다. 50% 이상이면 1(생존)로, 50% 미만이면 0(사망)으로 바꾸는 것이죠. 위의 세 경우 모두 50%를 훨씬 넘기 때문에 1로 변환되어 같은 점수를 받게 된 것입니다.
만약 ROC-AUC와 같이 확률값을 직접 사용하는 평가지표를 사용했다면, 세 가지 실험의 결과는 모두 달랐을 것입니다. 이런 심화된 내용들은 챕터 5에서 predict_proba() 함수와 다양한 평가지표들을 다루면서 더 자세히 알아보도록 하겠습니다.
이처럼 어떤 평가지표를 사용하느냐에 따라 같은 예측 결과도 다르게 해석될 수 있습니다. 우리가 사용하는 도구가 결과를 어떻게 평가하는지 이해하는 것도 중요하겠죠?