Pandas로 배우는 타이타닉 데이터 분석 기초 정복하기

읽기 예상 시간: 9분

데이터 분석의 첫걸음인 Pandas 라이브러리와 타이타닉 데이터를 활용해 실제 분석 환경을 구축하는 방법을 알려드려요. 위치와 이름을 기반으로 데이터를 추출하는 ilocloc의 차이를 명확히 이해하고, 내가 원하는 특정 조건에 맞는 데이터만 쏙쏙 걸러내는 불리언 인덱싱 기법을 다뤄볼 거예요. 또한, 실무 데이터에서 무조건 마주치게 되는 결측값(NaN)을 확인하고 정제하는 방법부터, 계산 오류를 막기 위해 데이터 타입을 알맞게 변환하는 과정까지, 데이터를 자유자재로 다루기 위한 핵심 기술들을 모두 짚어드릴게요.

목차

실습 준비: 라이브러리 및 데이터 불러오기

책상 위 모니터에 데이터 분석 화면이 떠 있고 옆에 타이타닉호 모형이 놓인 모습

파이썬으로 본격적인 데이터를 만지기 전에, 코드를 직접 따라 칠 수 있는 실습 환경부터 세팅해야 해요. 우리가 목공을 하려면 망치와 톱이 필요하듯, 파이썬에서 데이터를 분석할 때 무조건 챙겨야 하는 도구가 있어요. 바로 Pandas라는 라이브러리예요. 엑셀에서 하던 표 정리 작업을 코드로 훨씬 빠르고 방대하게 처리할 수 있게 해 주죠. 그리고 숫자를 다루고 수학적인 계산을 빠르게 도와주는 NumPy도 짝꿍처럼 항상 같이 붙어 다녀요.

이번 실습에서는 데이터 분석의 ‘Hello World’라고 불리는 타이타닉 생존자 데이터를 사용할 거예요. 인터넷에 공개된 데이터를 내 파이썬 환경으로 바로 불러와서 뼈대를 잡는 작업을 해볼게요. 데이터를 불러오고 저장하는 전체적인 기본 과정은 Pandas 기초 공식 문서에도 아주 잘 설명되어 있으니 나중에 꼭 한 번 읽어보세요.

환경 설정과 데이터 불러오기 단계

1
필수 라이브러리 임포트

Pandas와 NumPy를 파이썬 환경으로 불러와요. 매번 길게 치기 귀찮으니까 보통 pdnp라는 짧은 별명으로 줄여서 부르는 게 전 세계 개발자들의 공통된 약속이에요.

2
타이타닉 CSV 파일 읽기

내 컴퓨터에 파일을 일일이 다운로드받지 않아도, 인터넷에 올라와 있는 데이터 주소(URL)만 알고 있으면 read_csv() 함수를 통해 순식간에 데이터프레임 형태의 표로 만들 수 있어요.

3
데이터 미리보기

불러온 데이터가 수백만 줄이 넘어갈 수도 있겠죠? head() 함수를 쓰면 가장 위에 있는 5줄만 빠르게 화면에 띄워줘요. 데이터가 깨지지 않고 열과 행이 맞게 들어왔는지 확인하는 아주 중요한 첫 단추예요.

python
# pandas는 보통 pd로, numpy는 np로 줄여서 부르는 것이 규칙이에요.
import pandas as pd
import numpy as np

# 인터넷에 있는 타이타닉 데이터 csv 파일을 읽어서 df라는 이름의 데이터프레임으로 저장해요.
url = 'https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv'
df = pd.read_csv(url)

# 데이터가 잘 들어왔는지 위에서부터 5줄만 화면에 출력해서 확인해 봐요.
print(df.head())

이 코드를 실행하면 PassengerId, Survived, Pclass, Name 같은 여러 열로 이루어진 엑셀 표와 같은 형태의 출력 결과를 파이썬 화면에서 바로 볼 수 있어요. 본격적인 분석 준비가 끝난 셈이죠.

📌 Note

데이터를 불러온 직후에 df.info() 명령어를 한 번 쳐보세요. 각 열에 빈칸이 얼마나 있는지, 데이터가 숫자인지 글자인지 한눈에 파악할 수 있어서 전체적인 구조를 잡는 데 아주 유리해요.

행과 열 선택하기: iloc vs loc

투명한 데이터 보드 앞에서 특정 행과 열을 가리키고 있는 데이터 분석가

거대한 데이터프레임을 통째로 쓰는 일은 드물어요. 내가 분석하고 싶은 부분만 콕 집어서 꺼낼 줄 알아야 하죠. 엑셀에서 원하는 셀 범위를 마우스로 드래그하는 것처럼, Pandas에서는 특정 행이나 특정 열을 선택할 때 ilocloc이라는 두 가지 도구를 써요.

초보자분들이 이 둘의 생김새가 너무 비슷해서 정말 많이 헷갈려 하시는데, 개념을 확실히 잡아드릴게요. 쉽게 말해 iloc은 ‘인덱스 위치(Index Location)’, 즉 무조건 숫자 순서로 찾는 방식이에요. “위에서부터 3번째 줄 가져와!” 하는 식이죠. 반면에 loc은 ‘이름표(Label Location)’로 찾는 거예요. “이름표가 ‘나이’라고 적힌 열을 가져와!”라고 정확한 명칭을 부르는 방식이에요. 이 iloc loc 차이를 명확히 알아두는 것이 데이터 추출 에러를 줄이는 핵심이에요.

python
# 1. iloc: 파이썬의 기본 리스트처럼 0부터 시작하는 숫자 위치로 찾아요. 
# 주의! 0:3이라고 쓰면 0, 1, 2번째 행만 가져오고 3번째는 포함하지 않아요.
print("--- iloc 출력 ---")
print(df.iloc[0:3, 1:4]) 

# 2. loc: 눈에 보이는 이름표(인덱스 라벨과 열 이름)로 찾아요.
# 주의! 0:3이라고 쓰면 이름표가 '3'인 것까지 전부 포함해서 가져와요.
print("--- loc 출력 ---")
print(df.loc[0:3, ['Name', 'Age']])

# 3. 열 선택 시 대괄호 개수에 따른 차이
# 대괄호를 하나만 쓰면 데이터가 1차원 줄 형태(Series)로 나와요.
age_series = df['Age']
print("--- 단일 열 (Series) ---")
print(type(age_series))

# 대괄호를 두 번 쓰면 기존과 같은 2차원 표 형태(DataFrame)로 유지돼요.
age_name_df = df[['Age', 'Name']]
print("--- 다중 열 (DataFrame) ---")
print(type(age_name_df))

또한 대괄호의 개수에 따라 나오는 결과물의 형태가 완전히 달라진다는 것도 짚고 넘어가야 해요. 원하는 데이터를 1차원 리스트처럼 단순히 뽑아 쓰고 싶다면 대괄호 하나를 쓰고, 표 형태(2차원)를 그대로 유지해서 다른 데이터프레임 작업과 연결하고 싶다면 대괄호 두 개를 써야 한다는 점, 이거 실무에서 정말 뼈저리게 느끼게 될 거예요.

⚠️ Warning

iloc[0:3]은 3을 포함하지 않고 0, 1, 2까지만 가져오지만, loc[0:3]은 이름이 ‘3’인 행까지 가져와서 총 4개의 행이 출력될 수 있어요. 이 미묘한 범위 차이 때문에 나중에 전체 데이터 개수가 틀어지는 실수를 많이 하니 꼭 주의하세요.

조건 필터링: 원하는 데이터만 골라내기

깔때기를 통해 특정 색상의 구슬만 유리병으로 걸러지는 모습

“첫 번째부터 열 번째 줄까지 가져와”라는 요청보다는, 현업에서는 “나이가 25살 이상인 사람만 추려줘”, “1등석에 탄 여성 승객만 모아줘” 같이 의미 있는 조건으로 데이터를 걸러야 할 때가 훨씬 많아요. 이때 사용하는 마법 같은 방법이 바로 불리언 인덱싱(Boolean Indexing)이에요.

말이 조금 어렵게 들릴 수 있는데, 원리는 아주 단순해요. 데이터에 거름망을 하나 씌우는 거예요. 각 행마다 조건을 걸어보고 맞으면 참(True)으로 남겨두고, 틀리면 거짓(False)으로 판단해서 버리는 거죠. 결과적으로 참(True) 판정을 받은 행들만 우리 눈앞에 나타나게 됩니다.

python
# 나이가 25세 이상인 승객만 필터링해요.
# df['Age'] >= 25 조건을 데이터프레임 df의 대괄호 안에 넣어주면 돼요.
adults = df[df['Age'] >= 25]

# 여러 조건을 섞을 때는 and 대신 &, or 대신 | 기호를 써요.
# 주의! 다중 조건을 쓸 때는 반드시 각 조건을 괄호()로 묶어줘야 해요.
# 나이가 25세 이상이면서(&) 성별이 여성인 승객을 찾아요.
adult_females = df[(df['Age'] >= 25) & (df['Sex'] == 'female')]

# 나이가 25세 이상이거나(|) 1등석(Pclass == 1)에 탄 승객을 찾아요.
vip_or_adults = df[(df['Age'] >= 25) | (df['Pclass'] == 1)]

# 필터링이 잘 되었는지 확인해 봐요.
print(adult_females.head())

파이썬 기본 문법에서 쓰던 and, or 키워드 대신에 기호 &|를 쓴다는 점을 기억하세요. 특히 다중 조건을 적을 때 괄호를 빼먹으면 파이썬이 “어떤 계산부터 먼저 해야 하지?” 하고 연산 순서를 헷갈려서 에러를 뱉어내요. 그러니 습관적으로 조건 하나하나마다 괄호를 단단히 묶어주는 연습을 하시는 게 좋아요.

💡 Tip

조건이 너무 길어지면 대괄호 안이 지저분해져서 읽기 힘들어져요. 그럴 때는 condition = (df['Age'] >= 25) & (df['Sex'] == 'female') 처럼 조건을 먼저 변수에 담아두고, df[condition] 형태로 깔끔하게 집어넣는 방식을 추천해요.

정렬과 인덱스 관리하기

책상 위 파일함에 문서들이 번호 순서대로 깔끔하게 정리되어 있는 모습

조건 필터링으로 필요 없는 데이터를 막 쳐내다 보면 문제가 하나 생겨요. 순서가 뒤죽박죽이거나, 각 행의 고유 번호인 인덱스가 중간에 듬성듬성 이빨이 빠진 것처럼 비어버리거든요. 0, 1, 2로 가다가 갑자기 5, 8, 12 이렇게 튀는 식이죠. 나중에 반복문을 돌리거나 데이터를 합칠 때 이 엉망이 된 인덱스가 엄청난 골칫거리가 돼요.

그래서 데이터를 가공한 후에는 한 번씩 깔끔하게 정렬을 해주고 인덱스를 새롭게 0부터 시작하도록 리셋해 주는 정리 작업이 필수예요. 데이터에 고유한 회원번호가 있다면 아예 그 열을 인덱스 이름표로 승격시킬 수도 있어요.

python
# 나이(Age)를 기준으로 오름차순(작은 수부터) 정렬해요.
# 내림차순으로 하려면 ascending=False 옵션을 넣어주면 돼요.
df_sorted = df.sort_values(by='Age')

# 승객 고유 번호인 'PassengerId'를 행의 이름표(인덱스)로 설정해요.
# 변경된 결과를 원본에 적용하려면 반드시 변수(df)에 다시 덮어써야 해요.
df = df.set_index('PassengerId')

# 인덱스 이름표가 마음에 안 들면 rename()으로 콕 집어서 바꿀 수 있어요.
# 예: 이름표가 1인 행을 'First'로 변경
df = df.rename(index={1: 'First'})

# 바꾼 인덱스를 다시 일반 데이터 열로 돌려놓고, 0부터 시작하는 숫자로 초기화해요.
df = df.reset_index()

print(df.head())

가장 중요한 포인트는 Pandas의 친절함이 오히려 독이 될 수 있다는 점이에요. sort_values()reset_index() 같은 기능은 기본적으로 원본 데이터를 건드리지 않고, 예쁘게 변경된 복사본만 화면에 스윽 보여주고 끝나요. 그래서 “어? 왜 원래 데이터는 안 변했지?” 하고 당황할 수 있어요. 변경 사항을 계속 유지하고 싶다면 반드시 df = 처럼 변수에 다시 할당해서 덮어써야 한다는 점을 잊지 마세요.

결측값(NaN) 처리하기: 비어있는 데이터 다루기

나무 책상 위에서 퍼즐의 빈칸에 조각을 맞춰 넣는 손의 모습

우리가 수업 시간에 다루는 데이터는 빈칸 하나 없이 완벽하게 정제되어 있지만, 실제 현업 데이터는 절대 그렇지 않아요. 고객이 입력을 안 했거나 시스템 오류로 구멍이 뻥뻥 뚫려 있는 칸이 무조건 존재하죠. Pandas에서는 이런 알 수 없는 빈칸을 ‘NaN(Not a Number)’이라는 결측값으로 표시해요.

이 빈칸들을 무시하고 평균을 내거나 통계를 돌려버리면 전체 결과가 이상하게 왜곡되거나 코드가 아예 멈춰버리는 끔찍한 상황이 발생해요. 그래서 이 결측값을 시원하게 삭제해 버릴지, 아니면 평균값이나 0 같은 다른 값으로 찰흙처럼 메울지 반드시 결정을 내려야 해요. 더 깊이 있는 전략은 Pandas 결측치 처리 문서를 참고해 보시면 아주 유익할 거예요.

python
# 1. 결측값 확인하기
# isna()는 빈칸이면 True를 반환하고, sum()을 붙이면 각 열마다 빈칸이 몇 개인지 세어줘요.
print("--- 열별 결측값 개수 ---")
print(df.isna().sum())

# 2. 결측값 제거하기 (dropna)
# subset 옵션을 쓰면 'Age'와 'Cabin' 열에 빈칸이 있는 행만 콕 집어서 지울 수 있어요.
df_dropped = df.dropna(subset=['Age', 'Cabin'])

# 3. 결측값 채우기 (fillna)
# 지우기 아깝다면 빈칸을 다른 값으로 채울 수 있어요. 여기서는 Age의 빈칸을 전체 나이 평균값으로 채워요.
mean_age = df['Age'].mean()
df['Age'] = df['Age'].fillna(mean_age)

# 다시 확인해보면 Age 열의 빈칸이 0개가 된 것을 볼 수 있어요.
print("--- 처리 후 Age 결측값 개수 ---")
print(df['Age'].isna().sum())

❗ 중요

결측값을 무작정 dropna()로 다 날려버리면 애써 모은 아까운 정상 데이터까지 같이 날아갈 수 있어요. 반대로 무작정 평균값으로 다 덮어버리면 데이터 본연의 뾰족한 특징이 뭉툭하게 왜곡되죠. 빈칸이 생기는 이유를 파악하고 데이터의 성격에 맞춰서 신중하게 대처해야 해요.

데이터 타입 변환하기: astype()

둥근 블록이 컨베이어 벨트를 지나며 사각형 블록으로 변환되는 모습

데이터를 겉으로만 보면 숫자인데, 파이썬이 내부적으로는 문자열(글자)로 인식하고 있을 때가 종종 있어요. 이 상태로 더하기나 빼기를 시도하면 당연히 연산이 안 되고 에러가 뿜어져 나오죠. 또한 소수점이 있는 실수 데이터를 딱 떨어지는 정수로 바꾸고 싶을 때도 있어요. 이럴 때 데이터의 성질을 우리가 원하는 대로 강제 변환해 주는 astype()이라는 형변환 기능을 사용하게 됩니다.

그런데 여기서 초보자분들이 피눈물을 흘리게 만드는 가장 흔한 덫이 하나 있어요. 바로 방금 전에 배운 ‘결측값(NaN)’이 섞여 있는 열을 정수형(int)으로 바꾸려고 할 때 발생하는 충돌 에러예요. 파이썬은 이 빈칸(NaN)을 내부적으로 실수형(float)으로 취급하기 때문에 정수와 섞일 수가 없거든요.

python
# 각 열이 어떤 데이터 타입(숫자, 글자 등)으로 되어 있는지 확인해요.
print(df.dtypes)

# 요금(Fare) 열은 소수점이 있는 실수(float)인데, 이를 정수(int)로 바꿔봐요.
# 빈칸이 없는 깔끔한 열이라면 에러 없이 바로 잘 바뀌어요.
df['Fare'] = df['Fare'].astype(int)

# 주의! 나이(Age) 열에 결측값(NaN)이 있다면 바로 int로 바꿀 수 없어서 IntCastingNaNError가 나요.
# 그래서 먼저 fillna()로 빈칸을 -1 같은 임시 숫자로 채운 다음에 정수로 바꿔줘야 해요.
df['Age'] = df['Age'].fillna(-1).astype(int)

# 만약 임시로 넣은 -1을 다시 원래의 빈칸(NaN) 상태로 돌려놓고 싶다면 replace를 써요.
df['Age'] = df['Age'].replace(-1, np.nan)

print(df['Age'].head())

타입 변환에서 에러가 난다면 열에 구멍이 없는지, 혹은 숫자가 아닌 이상한 특수문자가 섞여 있지는 않은지 먼저 점검하는 습관을 들이는 것이 좋습니다. 데이터를 올바른 형태로 다듬어 두면 이어지는 머신러닝이나 시각화 단계가 눈에 띄게 수월해지니까요.

자주 묻는 질문

Q. 본문의 실습 코드를 그대로 실행했는데 ‘NameError: name pd is not defined’ 에러가 납니다.

이 에러는 파이썬에게 “내가 Pandas를 pd라는 이름으로 쓸 거야!”라고 미리 알려주지 않아서 생기는 문제예요. 실습 준비 파트 가장 위에 있는 import pandas as pd, import numpy as np 코드를 누락하지 말고 가장 먼저 실행해 주시면 에러가 말끔히 사라질 거예요.

Q. 데이터를 선택할 때 Series와 DataFrame 중 어떤 것이 반환되는지 어떻게 확실히 구분하나요?

대괄호 []를 몇 번 썼는지를 기준으로 생각하면 아주 명확해요. df['Age']처럼 대괄호를 한 번만 쓰면 데이터가 1차원 형태인 Series로 뽑혀 나와요. 반면에 df[['Age', 'Name']]처럼 대괄호 안에 또 대괄호를 써서 리스트 형태로 넘겨주면, 표 형태인 DataFrame이 그대로 유지되죠. 헷갈리신다면 type() 함수로 직접 출력 결과를 확인해 보시는 걸 강력히 추천해요.

Q. 결측값을 채우거나 지웠는데, 나중에 다시 데이터프레임을 열어보면 빈칸이 그대로 남아있습니다. 왜 그런가요?

Pandas의 dropna(), fillna(), set_index() 같은 데이터 변형 기능들은 원본 데이터를 파괴하지 않기 위해 직접 뜯어고치지 않아요. 대신 변형이 완료된 새로운 임시 복사본을 화면에 잠시 보여주기만 하죠. 그래서 이 변경된 상태를 영구적으로 유지하려면 df = df.dropna()처럼 기존 변수에 그 결과를 다시 할당(덮어쓰기) 해줘야 한다는 점을 꼭 기억하셔야 해요.

Q. 나이(Age) 열을 정수(int)로 바꾸려고 df['Age'].astype(int)를 실행하니 에러가 납니다.

나이 열 중간에 비어있는 칸(NaN)이 끼어 있어서 파이썬이 당황한 거예요. 파이썬에서는 결측값 NaN을 기본적으로 실수(float)로 취급하기 때문에, 이걸 강제로 정수(int)로 바꾸려다 보니 충돌이 나는 거죠. 이 에러를 피해 가려면 fillna()를 사용해서 빈칸을 -1이나 평균값 같은 가짜 숫자로 먼저 꽉 채워준 다음에, 안전하게 형변환을 시도해 보세요.

이 글이 마음에 드세요?

RSS 피드를 구독하세요!

댓글 남기기