info) BACS 1주차는 Titanic 데이터를 사용하여 Pandas 실습을 진행하였습니다. 

실습을 위한 Titanic csv 파일은 Kaggle의 'Titanic - Machine Learning from Disaster' 페이지에서 다운로드 가능하며 원본 파일은 train.csv이나 다른 파일과의 혼동을 방지하기 위해 Titanic으로 파일명을 변경하여 진행하였습니다. 

 

1. 데이터 불러오기

Titanic 파일은 콤마(,)로 구분된 csv파일입니다. Titanic 파일을 read_csv()를 통해 데이터프레임 형식으로 가져오도록 하겠습니다. 이때 경로 설정에 유의해주시기 바랍니다. 1) 모든 경로를 설정해주시거나 또는 2) 사전에 디렉토리 경로를 설정해주어 파일명만을 통해 가져오는 방법이 있습니다. 편하신 방법을 이용하시면 됩니다. 

import pandas as pd

df = pd.read_csv('./Titanic.csv') #다운로드한 csv파일을 데이터프레임 형식으로 불러오기
df

 

2. 데이터 개요 확인하기

데이터프레임은 행과 열로 구성된 2차원의 자료구조입니다. 따라서, 행과 열의 크기를 통해 데이터의 전체적인 사이즈를 확인할 수 있습니다.

train.shape

또한, 행의 수가 많은 경우 상위 n개와 하위 n개만을 가져와 약식으로 데이터의 모습을 확인할 수 있습니다.  head()와 tail()의 파라미터로 원하는 개수의 숫자를 넣어주면 되고 생략시 default값은 5입니다.

df.head() #df의 상위 5개 행을 확인

df.tail(10) #df의 하위 10개 행 확인

지금까지 Titanic의 개괄적인 부분을 살펴보았으니 각 feature들의 value 빈도를 확인해보겠습니다. 그래프를 통해 값의 분포를 확인하는게 좋으나 이번에는 pandas만을 사용하는 시간이니 value_counts를 통해 결과를 가져와 보겠습니다. 수치형 데이터는 분포가 너무 퍼져 있어 한눈에 보이지가 않고 출력물이 너무 길어지게 되어 간단히 범주형 데이터로 구성된 컬럼 몇 개만 확인해보겠습니다. 

df.columns #df의 column에 어떤 항목들이 있는지 확인
category_list = ['Survived', 'Pclass', 'Sex','Embarked'] #category 데이터로 이루어진 컬럼 중 일부만 가져오기

#반복문을 활용해 출력
for col in category_list:
    print("[{}]".format(col)) #해당 컬럼을 제목으로 입력
    print(df[col].value_counts()) #value_counts()를 사용해 값의 빈도 가져오기
    print("-" * 40, end='\n') #컬럼 간 구분

 

3. 결측치 확인 및 처리

isnull() 또는 isna()를 통해 결측치에 해당하는 부분을 boolean 값(True/False)로 반환할 수 있습니다. 파이썬에서는 True를 1로 인식하고 False를 0으로 인식하기 때문에 sum()을 통해 '합계'를 구하면 결국 '개수'와 동일한 결과를 얻을 수 있게 됩니다. 

df.isna().sum() #isna() 대신 isnull()도 가능

df.notnull().sum() #notnull()을 사용하면 null값이 아닌 것의 개수를 반환

결측치를 확인한 결과 'Age' 컬럼에 177개, 'Cabin' 컬럼에 687개, 'Embarked' 컬럼에 2개가 있습니다. 그러면 각 컬럼에서 결측치를 처리해보도록 하겠습니다. 각 case별로 하나씩 해보겠습니다. 

[Case1] 'Age'의 결측치 처리

결측치가 발견되는 경우 해당 결측치를 다른 값으로 '대체'를 하거나 결측치가 존재하는 컬럼이나 행을 '삭제'할 수 있습니다. 데이터의 성격에 따라 처리 방식은 달라질 수 있기 때문에 반드시 충분한 검토 후에 결측치 처리를 진행해야 합니다. 우선 'Age' 컬럼의 경우 결측치는 177개입니다. 꽤 많은 결측치가 있으나 우리가 이후 Titanic 데이터에 대한 가설을 설정할 때 연령 데이터는 중요한 요인이 될 수 있기 때문에 제거보다는 값을 대체하는 것이 더 좋은 선택이라 할 수 있습니다. 

'대체'의 경우에도 2가지 방식을 사용할 수 있습니다. 

1) 평균 연령으로 대체

mean()함수를 통해 'Age'의 평균을 구하고 이를 fillna()값에 넣어 대체할 수 있습니다. 

df.fillna(df['Age'].mean(), inplace = True) #'Age'의 평균값으로 결측치 대체

2) 성별 별 연령의 평균으로 대체

groupby()함수를 사용해 성별 별 평균 연령을 확인해보겠습니다. 

df.groupby('Sex')['Age'].mean()

각 평균값을 반올림하여 각각 28, 31로 하고 NaN값에 대체해보겠습니다. loc()함수에 2개의 조건을 주어 male이면서 나이가 null인 경우와 female이면서 나이가 null인 경우를 확인하고 각각의 평균값을 넣어주겠습니다. 

df.loc[(df['Sex'] == 'male') & (df['Age'].isnull()), 'Age'] = 31
df.loc[(df['Sex'] == 'female') & (df['Age'].isnull()), 'Age'] = 28

[Case2] 'Cabin'의 결측치 처리

'Cabin' 컬럼의 경우 결측치가 687개로 전체 데이터 개수인 891개에서 너무 많은 비중을 차지하고 있습니다. 따라서 값을 대체해도 무의미한 결과가 나올뿐만 아니라 'Cabin' 컬럼의 특성 상 그 값들이 특정 값으로 대체되기 어려운 데이터입니다. 상식적으로 cabin(선실)이라는 것은 대부분의 사람들이 각자가 예약한 방을 사용하기 때문에 가족이 아닌 이상 모두 다른 cabin을 이용해 하나의 값으로 대체하기가 어렵습니다. 따라서, 'Cabin' 컬럼의 경우에는 아예 컬럼을 삭제하는 선택을 하도록 하겠습니다. drop()에서는 index를 통해 행을 columns를 통해 열을 삭제할 수 있습니다.

df.drop(columns = 'Cabin', inplace = True)

한편, column을 drop해주는 방법도 있지만 결측치가 있는 데이터 row를 제거해주는 방법이 있습니다. 하지만 'Cabin'의 경우 결측치가 너무 많기 때문에 추천하지는 않습니다. 왜냐하면 너무 많은 데이터 row값이 삭제되어 데이터 샘플 수가 부족해지고 데이터에 왜곡이 생기는 문제가 생기기 때문입니다. 

df.dropna(subset=['Cabin'], inplace = True) #df에서 'Cabin'컬럼이 null인 행만 삭제

[Case3] 'Embarked'의 결측치 처리

마지막으로 'Embarked' 컬럼의 경우 결측치가 2개로 적은 편입니다. 'Embarked' 데이터의 분포를 보면 다음과 같이 'S'가 644개로 가장 많습니다. 따라서, 결측치 값을 'S'로 채워줄 수 있습니다. 

df['Embarked'].value_counts() #'Embarked'컬럼의 값 분포를 확인
df['Embarked'].fillna('S', inplace =True) #결측치를 'S'로 대체

 

+ Recent posts