본문 바로가기

개발 공부/데이터 분석

[Pandas] Iris와 Titanic 데이터로 데이터 분석 연습

Iris와 Titanic 데이터로 데이터 확인, 가공, 그룹별 집계 방법을 정리한다.

① Iris 데이터 탐색

② Titanic 데이터 가공과 집계

 

1. Iris 데이터로 DataFrame 탐색하기

먼저 Iris 데이터를 불러와 DataFrame의 구조·범주·결측값·기초 통계를 순서대로 확인한다.


1-1. Iris 데이터셋 이해하기

Iris는 붓꽃의 꽃받침과 꽃잎 크기를 측정한 데이터다. setosa·versicolor·virginica 세 품종이 각각 50개씩 들어 있어 데이터 탐색과 분류 연습에 자주 사용한다.

행 150개: 붓꽃 표본 150개를 뜻한다.

수치형 컬럼 4개: sepal_length, sepal_width, petal_length, petal_width

범주형 컬럼 1개: 붓꽃 품종을 나타내는 species

import seaborn as sns
iris = sns.load_dataset('iris')
iris.shape
iris.columns

실행 결과
(150, 5)
['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species']


1-2. 데이터를 받으면 먼저 구조를 확인한다

함수·메서드 무엇을 할 때 쓰는지 사용 예시
head() 앞부분 데이터를 빠르게 확인할 때 iris.head()
tail() 마지막 데이터를 빠르게 확인할 때 iris.tail()
info() 행 수·컬럼·자료형·결측값을 볼 때 iris.info()
describe() 수치형 컬럼의 기초 통계를 볼 때 iris.describe()

iris.head(3)

실행 결과
sepal_length sepal_width petal_length petal_width species
0 5.1 3.5 1.4 0.2 setosa
1 4.9 3.0 1.4 0.2 setosa
2 4.7 3.2 1.3 0.2 setosa


head()와 tail()은 값이 정상적으로 들어왔는지 확인하는 미리보기다. info()에서는 150개 행과 5개 컬럼, 각 자료형, non-null 개수를 확인한다. Iris는 모든 컬럼이 150 non-null이므로 결측값이 없다.

iris.info()
iris.isna().sum()

실행 결과
150 entries, 5 columns
float64 4개, object 1개
모든 컬럼의 결측값 개수 0


1-3. 범주형 데이터의 값과 개수 확인하기

함수·메서드 무엇을 할 때 쓰는지 사용 예시
mode() 가장 자주 등장한 최빈값을 구할 때 iris['species'].mode()
unique() 중복을 뺀 실제 값 목록을 볼 때 iris['species'].unique()
nunique() 고유한 값의 개수를 셀 때 iris['species'].nunique()
value_counts() 값별 등장 횟수를 셀 때 iris['species'].value_counts()
describe() 통계값을 한 번에 확인할 때 iris.describe()

iris['species'].mode()
iris['species'].unique()
iris['species'].nunique()
iris['species'].value_counts()

실행 결과
mode: 세 품종이 모두 50개라 세 값이 함께 최빈값
unique: ['setosa' 'versicolor' 'virginica']
nunique: 3
value_counts: 각 품종 50개

unique()는 실제 값의 종류를, nunique()는 그 개수를, value_counts()는 각 값이 몇 번 나왔는지를 알려준다. 분류 데이터를 받으면 클래스가 빠지지 않았는지와 수량이 한쪽으로 치우쳤는지 확인할 때 유용하다.


1-4. describe 결과 해석하기

iris.describe()

실행 결과
sepal_length 평균 5.843, 최솟값 4.3, 최댓값 7.9
각 수치형 컬럼의 count·mean·std·min·25%·50%·75%·max 출력


count가 전체 행 수와 같은지 먼저 보고, mean과 50%(중앙값)의 차이로 값의 치우침을 살펴본다. min·max는 범위를 벗어난 이상값 후보를 찾는 데 활용할 수 있다.

한 줄 정리 head·tail → shape·columns → info·isna → unique·value_counts → describe 순서로 보면 데이터의 모양과 품질을 빠르게 파악할 수 있다.

 

2. Titanic 데이터 가공과 그룹 집계

마지막으로 Titanic 데이터의 구조와 결측값을 확인하고, 필요한 값을 가공한 뒤 연령대별 생존률을 계산한다.


2-1. Titanic 데이터셋과 주요 컬럼 이해하기

Seaborn의 Titanic 데이터는 승객 891명의 생존 여부와 성별·나이·객실 등급·가족 관계 등을 담고 있다. survived가 1이면 생존, 0이면 사망을 뜻한다.

함수·메서드 무엇을 할 때 쓰는지 사용 예시
survived 생존 여부를 확인할 때 0: 사망, 1: 생존
pclass 객실 등급을 구분할 때 1·2·3등급
age 승객 나이를 분석할 때 연속형 수치
sibsp 형제·자매·배우자 수 가족 수 계산
parch 부모·자녀 수 가족 수 계산

df = sns.load_dataset('titanic')
df.shape
df[['survived','pclass','sex','age','sibsp','parch']].head(3)

실행 결과
(891, 15)
앞부분 승객의 생존 여부, 등급, 성별, 나이, 가족 관계가 출력된다.


2-2. 결측값과 중복 의미 컬럼 확인하기

df.info()
df.isna().sum().sort_values(ascending=False).head()

실행 결과
deck 688
age 177
embarked 2
embark_town 2
그 외 주요 컬럼 0


age와 deck에는 결측값이 있으므로 분석 전에 확인해야 한다. alive는 survived를 yes/no로 표현한 컬럼이고, alone은 sibsp와 parch를 바탕으로 만든 값이라 같은 정보를 중복해서 담는다.

titanic = df.drop(columns=['alive', 'alone']).copy()
titanic.columns

실행 결과
alive와 alone을 제외한 새 DataFrame의 컬럼 목록

왜 copy()를 쓰나 drop()으로 새 DataFrame을 만들고 copy()까지 붙이면 원본 df를 보존하면서 이후 컬럼을 안전하게 추가할 수 있다. 슬라이스 결과를 직접 수정할 때 발생할 수 있는 SettingWithCopyWarning도 피하기 쉽다.


2-3. 불리언 인덱싱으로 필요한 연령만 추출하기

10대·20대·30대를 비교하려면 age가 10 이상 40 미만인 행만 선택한다. 여러 조건은 각각 괄호로 감싸고 and 대신 &를 사용한다.

age_df = titanic[(titanic['age'] >= 10) & (titanic['age'] < 40)].copy()
age_df['age'].min(), age_df['age'].max()

실행 결과
(10.0, 39.0)
age가 결측값인 행은 조건을 만족하지 않아 선택되지 않는다.


2-4. map과 apply로 값을 바꾸기

함수·메서드 무엇을 할 때 쓰는지 사용 예시
map() Series 값 하나씩 변환하거나 딕셔너리로 치환할 때 s.map({1:'A',2:'B'})
apply() 함수를 적용해 Series 또는 행·열을 가공할 때 s.apply(func)
groupby() 같은 값을 묶어 평균·합계 등을 구할 때 df.groupby('group').mean()

age_df['age_group'] = (age_df['age'] // 10).map({1:'10대', 2:'20대', 3:'30대'})
age_df[['age','age_group']].head()

실행 결과
age가 10~19면 10대, 20~29면 20대, 30~39면 30대로 저장


map()은 Series의 각 값을 딕셔너리로 간단히 치환할 때 편하다. 조건이 복잡하다면 변환 함수를 만든 뒤 apply()로 적용할 수 있다.

def to_age_group(age):
if age < 20: return '10대'
if age < 30: return '20대'
return '30대'

age_df['age_group_apply'] = age_df['age'].apply(to_age_group)

실행 결과
map으로 만든 age_group과 같은 연령대 값이 저장된다.


2-5. 가족 수 컬럼 만들고 검증하기

부모·자녀 수(parch)와 형제·배우자 수(sibsp)에 본인 1명을 더하면 함께 탑승한 가족 수를 만들 수 있다.

titanic['family_count'] = titanic['parch'] + titanic['sibsp'] + 1
titanic[['parch','sibsp','family_count']].head()

실행 결과
family_count가 parch + sibsp + 1과 일치하는지 앞부분에서 확인한다.


2-6. groupby로 연령대별 생존률 계산하기

age_df.groupby('age_group')['survived'].mean()

실행 결과
10대 0.401 | 20대 0.350 | 30대 0.437

survived는 0과 1로 이루어져 있으므로 평균을 구하면 해당 그룹에서 1이 차지하는 비율, 즉 생존률이 된다. 예제 결과에서는 30대 약 43.7%, 10대 약 40.1%, 20대 약 35.0% 순으로 나타났다.

결과 해석 주의 이 결과만으로 나이가 생존 여부의 원인이라고 단정할 수는 없다. 성별·객실 등급·운임 등 다른 조건의 영향을 함께 확인해야 한다. 이번 실습의 핵심은 연속형 나이를 범주로 바꾸고 groupby로 그룹별 평균을 구하는 과정이다.


2-7. 데이터 분석 기본 흐름 정리

  • 구조 확인: shape·columns·head·info로 데이터의 크기와 자료형을 본다.
  • 품질 확인: isna·value_counts·describe로 결측값과 분포를 확인한다.
  • 필요한 행 선택: 불리언 인덱싱으로 분석 범위를 좁힌다.
  • 값 가공: map·apply 또는 컬럼 연산으로 분석용 변수를 만든다.
  • 그룹 집계: groupby로 그룹별 평균·합계·개수를 계산한다.
  • 결과 해석: 수치 차이와 함께 분석 범위와 한계를 적는다.
반응형