본문 바로가기

[입문] 데이터 사이언스? 그게 뭔가요?

데이터 사이언스 입문자를 위한 [데이터 전처리]

타이타닉 승객 데이터셋을 이용한 데이터 핸들링 실습

여기서 다운받으시면 됩니다.(무료 회원가입 필요)

컬럼 설명

  • survival -> 살아남았는가에 대한 지표입니다. 1 이 생존, 0 이 사망 입니다.

  • pclass -> 사회경제적 지표입니다. 1 부터 3까지 Upper/Middle/Lower class 입니다.

  • Sex -> 성별 입니다. male = 남자, female = 여자 입니다.

  • Age -> 나이 입니다.

  • sibsp -> 본인을 제외하고 배에 탑승한 형제자매 및 배우자의 인원수 입니다.

  • parch -> 본인을 제외하고 배에 탑승한 부모/자식의 총 인원수 입니다.

  • ticket -> 이 사람이 보유한 탑승권의 식별자를 의미합니다.

  • fare -> 이 사람이 탑승하기 위해 지불한 금액을 의미합니다.

  • cabin -> 배정받은 숙소를 의미합니다.

  • embarked ->이 사람이 탑승한 항구를 의미합니다.

Load Dataset

모든 데이터 분석의 시작은 데이터를 읽어오는 것입니다.

파일의 경로를 지정하는 방법에 주의하셔야 합니다.

만일 read_csv를 실행할 때 (FileNotFoundError)라는 이름의 에러가 난다면 경로가 제대로 지정이 되지 않은 것입니다.

다음의 링크에서 경로를 지정하는 법을 다루고 있습니다.

# 판다스의 read_csv로 titanic.csv 파일을 읽어옵니다.
# 읽어온 데이터를 data 라는 이름의 변수에 할당합니다.
# index_col="PassengerId" => PassengerId 항목을 인덱스로 사용하겠다는 의미입니다.

import pandas as pd
data = pd.read_csv("data/titanic.csv", index_col="PassengerId")
# 새로 불러온 변수 data 의
# 가로, 세로 크기와
# 첫 5 개 행을 확인합니다.

print(data.shape)
data.head()
(891, 11)

Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
PassengerId
1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S
2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 PC 17599 71.2833 C85 C
3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 3101282 7.9250 NaN S
4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.1000 C123 S
5 0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.0500 NaN S

데이터 프레임 첫걸음

# 딕셔너리에서 키로 값을 조회함과 동일하게 다룹니다.
# column 이름이 키가 되고, row 들이 값이 됩니다.
# 값들이 한 화면에 표시하기에는 많기 때문에, .head()로 첫 5 행만 출력합니다.

data["Survived"].head()
PassengerId
1    0
2    1
3    1
4    1
5    0
Name: Survived, dtype: int64
# 보고자 하는 column 들을 리스트로 만들어서
# 따로 선별해 보고싶은 것만 조회할 수도 있습니다.

columns = ["Sex", "Pclass", "Survived"]
data[columns].head()

Sex Pclass Survived
PassengerId
1 male 3 0
2 female 1 1
3 female 3 1
4 female 1 1
5 male 3 0
# loc 은 locate 에서 앞글자를 따온 것입니다.
# 리스트에서의 인덱싱과 동일합니다.
# n 번째 인덱스를 가져온다는 의미입니다.

data.loc[1]
Survived                          0
Pclass                            3
Name        Braund, Mr. Owen Harris
Sex                            male
Age                              22
SibSp                             1
Parch                             0
Ticket                    A/5 21171
Fare                           7.25
Cabin                           NaN
Embarked                          S
Name: 1, dtype: object
# 리스트에서의 슬라이싱과 동일한 개념입니다.
# 어디 부터 어디 까지를 가져온다는 의미입니다. 

data.loc[1:7]

Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
PassengerId
1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S
2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 PC 17599 71.2833 C85 C
3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 3101282 7.9250 NaN S
4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.1000 C123 S
5 0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.0500 NaN S
6 0 3 Moran, Mr. James male NaN 0 0 330877 8.4583 NaN Q
7 0 1 McCarthy, Mr. Timothy J male 54.0 0 0 17463 51.8625 E46 S
# 보고자 하는 인덱스(row)를 골라 리스트에 넣고
# 따로 선별해 보고자 하는 것들만 가져옵니다.

passenger_ids = [1, 3, 7, 13]
data.loc[passenger_ids]

Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
PassengerId
1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S
3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 3101282 7.9250 NaN S
7 0 1 McCarthy, Mr. Timothy J male 54.0 0 0 17463 51.8625 E46 S
13 0 3 Saundercock, Mr. William Henry male 20.0 0 0 A/5. 2151 8.0500 NaN S
# 이렇게 행과 열을 보고싶은 것만 리스트로 만들어서
# 따로 선별해 보고자 하는 것만 가져올 수도 있습니다.

data.loc[[1, 3, 7, 13], ["Pclass", "Sex", "Survived"]]

Pclass Sex Survived
PassengerId
1 3 male 0
3 3 female 1
7 1 male 0
13 3 male 0

색인

# 불리언 조건을 만들어서, 조건이 True 일 경우만 보려고 합니다.
# 대괄호 안에 인덱스 번호 대신, 불리언 조건을 만들어 넣어줍니다.

data[data["Sex"] == "male"].head()

Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
PassengerId
1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S
5 0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.0500 NaN S
6 0 3 Moran, Mr. James male NaN 0 0 330877 8.4583 NaN Q
7 0 1 McCarthy, Mr. Timothy J male 54.0 0 0 17463 51.8625 E46 S
8 0 3 Palsson, Master. Gosta Leonard male 2.0 3 1 349909 21.0750 NaN S
# 이 불리언 조건은 각 행에 대해
# True/False 정보를 담고 있습니다.
# 인덱스 번호 대신 이러한 참/거짓 정보가 들어가면
# 참인 경우만 골라서 보겠다는 의미입니다.

data["Sex"] == "male"
PassengerId
1       True
2      False
3      False
4      False
5       True
6       True
7       True
8       True
9      False
10     False
11     False
12     False
13      True
14      True
15     False
16     False
17      True
18      True
19     False
20     False
21      True
22      True
23     False
24      True
25     False
26     False
27      True
28      True
29     False
30      True
       ...  
862     True
863    False
864    False
865     True
866    False
867    False
868     True
869     True
870     True
871     True
872    False
873     True
874     True
875    False
876    False
877     True
878     True
879     True
880    False
881    False
882     True
883    False
884     True
885     True
886    False
887     True
888    False
889    False
890     True
891     True
Name: Sex, Length: 891, dtype: bool
# 이를 이용하여 탑승료를 20 보다 높게 낸
# 탑승객만 고르게 되면 코드는 아래와 같습니다.

data[data["Fare"] > 20].head()

Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
PassengerId
2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 PC 17599 71.2833 C85 C
4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.1000 C123 S
7 0 1 McCarthy, Mr. Timothy J male 54.0 0 0 17463 51.8625 E46 S
8 0 3 Palsson, Master. Gosta Leonard male 2.0 3 1 349909 21.0750 NaN S
10 1 2 Nasser, Mrs. Nicholas (Adele Achem) female 14.0 1 0 237736 30.0708 NaN C

데이터의 결측치에 대하여

데이터에는 소위 말하는 '결측치' 가 있습니다.

데이터가 없다는 뜻이고 이는 NA 또는 null 이라고 합니다.

NA 는 잘못 입력된 값, null은 정해지지 않는 값인데요,

파이썬에서는 이를 일괄 NaN(Not a Number) 라고 표현합니다.

# .isnull() 은 null인 경우 True, 아니면 False 인 불리언입니다.
# 인덱스 번호 대신 이렇게 참 / 거짓 정보가 들어가는 것이므로
# null 인 것만 보겠다는 의미입니다.

data[data["Age"].isnull()].head()

Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
PassengerId
6 0 3 Moran, Mr. James male NaN 0 0 330877 8.4583 NaN Q
18 1 2 Williams, Mr. Charles Eugene male NaN 0 0 244373 13.0000 NaN S
20 1 3 Masselmani, Mrs. Fatima female NaN 0 0 2649 7.2250 NaN C
27 0 3 Emir, Mr. Farred Chehab male NaN 0 0 2631 7.2250 NaN C
29 1 3 O'Dwyer, Miss. Ellen "Nellie" female NaN 0 0 330959 7.8792 NaN Q
# .notnull() 은 null인 경우 False, 아니면 True 인 불리언입니다.
# 인덱스 번호 대신 이렇게 참 / 거짓 정보가 들어가는 것이므로
# null 이 아닌 것만 보겠다는 의미입니다.

data[data["Age"].notnull()].head()

Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
PassengerId
1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S
2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 PC 17599 71.2833 C85 C
3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 3101282 7.9250 NaN S
4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.1000 C123 S
5 0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.0500 NaN S

기본 연산

# 결손값은 최빈값/평균 등으로 채워넣어서 처리하는 경우가 많습니다.
# 이를 위해 컬럼의 최대 / 최소값이나 평균 값 등이 궁금할 때
# 쉽게 알아보는 방법 두 가지를 소개합니다.

# 첫 번째 방법은, 컬럼 이름으로 값을 뽑아와 
# .mean(), .min(), .max() 등을 사용하는 것입니다.

print(data["Fare"].mean())
print(data["Age"].min())
print(data["Age"].max())
32.2042079685746
0.42
80.0
# 두 번째 방법은 기초통계량을 한번에 뽑아보는 것입니다.
# .describe() 인데요, describe가 설명한다는 뜻이 있으니 
# 데이터프레임 전체를 큰 틀에서 설명하는 기능입니다.

# 데이터의 개수, 평균, 표준편차, 최대/최소값, 
# 크기로 정렬했을 때 1/4, 2/4, 3/4 번째의 값을 알 수 있습니다.
# 통계를 하려면 숫자가 있어야 하기 때문에 숫자만 들어있는 컬럼만 볼 수 있습니다.

data.describe()

Survived Pclass Age SibSp Parch Fare
count 891.000000 891.000000 714.000000 891.000000 891.000000 891.000000
mean 0.383838 2.308642 29.699118 0.523008 0.381594 32.204208
std 0.486592 0.836071 14.526497 1.102743 0.806057 49.693429
min 0.000000 1.000000 0.420000 0.000000 0.000000 0.000000
25% 0.000000 2.000000 20.125000 0.000000 0.000000 7.910400
50% 0.000000 3.000000 28.000000 0.000000 0.000000 14.454200
75% 1.000000 3.000000 38.000000 1.000000 0.000000 31.000000
max 1.000000 3.000000 80.000000 8.000000 6.000000 512.329200

결손값 처리하기

# "Age" 컬럼을 지정하고, .mean() 을 이용해 평균을 구한 후
# = 을 이용해 mean_age 라는 이름의 변수에 넣어줍니다.
mean_age = data["Age"].mean()

# "Age" 컬럼을 지정하고, .fillna() 안에 mean_age를 넣게되면
# 결손값인 지점에 평균값을 넣어준다는 의미입니다.
# 이를 = 을 이용해 "Age" 컬럼 자리에 넣어줍니다.
data['Age'] = data['Age'].fillna(mean_age)

# .isnull() 은 null인 경우 True, 아니면 False 인 불리언입니다.
# 인덱스 번호 대신 이렇게 참 / 거짓 정보가 들어가는 것이므로
# null 인 것만 보겠다는 의미이었습니다.
# 기존에는 여러 개 있었지만, 위 코드가 실행된 후 사라짐을 확인합니다.
data[data['Age'].isnull()]

Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
PassengerId

컬럼 추가 & 수정

# 데이터프레임이 딕셔너리와 아주 유사함을 보았는데요,
# 딕셔너리에서 새로운 키에 값을 할당하는 법을 다뤘고, = 을 통해 값을 넣어줄 수도 있다고 했습니다.
# 이 두 가지를 통해, 딕셔너리 때와 동일하게 값을 추가할 수 있습니다.
# DataType 이라는 키에, 'mydata' 라는 값을 추가하는 방법은 아래와 같습니다.

data["DataType"] = "mydata"

# .head() 로 잘 되었음을 확인합니다.
data.head()

Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked DataType
PassengerId
1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S mydata
2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 0 PC 17599 71.2833 C85 C mydata
3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 3101282 7.9250 NaN S mydata
4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.1000 C123 S mydata
5 0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.0500 NaN S mydata
# Sibsp 는 형제자매, Parch는 부모자식의 인원 수 입니다.
# 여기에서 본인까지 합하면, 이 배에 탑승한 내 가족의 총 인원수가 나오게 되겠네요!
# 다 더해서 FamilySize 라는 키에 넣어줍니다.

data["FamilySize"] = data["SibSp"] + data["Parch"] + 1

# SibSp, Parch, FamilySize 세 컬럼만 선택해 .head()로 상위 5 개만 확인합니다.
data[["SibSp", "Parch", "FamilySize"]].head()

SibSp Parch FamilySize
PassengerId
1 1 0 2
2 1 0 2
3 0 0 1
4 1 0 2
5 0 0 1
C = Cherbourg, - 프랑스 쉘부르
Q = Queenstown, - 뉴질랜드 퀸즈타운
S = Southampton - 영국 사우스햄턴
# 탑승한 항구를 기반으로 해당 인원의 국적을 유추합니다.
# C 는 프랑스, S 와 Q 는 UK 로 가정합니다.
# 코드 해설: data.loc[data["Embarked"] == "C", "Nationality"] = "FR"
# 'Embarked' 컬럼의 값이 C 인 경우, "Nationality" 라는 컬럼에 "FR" 이라는 값을 넣어줍니다. 

data.loc[data["Embarked"] == "C", "Nationality"] = "FR"
data.loc[data["Embarked"] == "S", "Nationality"] = "UK"
data.loc[data["Embarked"] == "Q", "Nationality"] = "UK"

# Embarked, Nationality 두 개의 키만 가져와 제대로 되었는지 확인합니다.
data[["Embarked", "Nationality"]].head()

Embarked Nationality
PassengerId
1 S UK
2 C FR
3 S UK
4 S UK
5 S UK

원-핫-인코딩 이라는 것이 있습니다.


한 컬럼이 여러가지 경우의 수를 나타내는 상황이 있습니다.
이를 '범주형' 자료라고 부르기도 합니다.
지금 경우에서는 국적 / 성별과 같은 경우가 범주형 자료에 속합니다.
우리는 향후 예측 알고리즘 등에 집어넣어서 생존 여부를 예측해 볼 건데요,
알고리즘에는 숫자만 들어갈 수 있기 때문에 미리 처리를 해주어야 합니다.
그 대표적인 방법인 원-핫-인코딩을 소개합니다.
# Nationality 는 FR, UK 두 가지 경우의 수만 있습니다.
# 새로운 컬럼을 만들어, Nationality_FR 에는 FR 인 경우만 넣고
# Nationality_UK 에는 UK 인 경우만 넣어주면 쉽게 분리가 될 것입니다.
# 컬럼을 선택해 == 으로 불리언 참/거짓 판별을 한 후, 참/거짓 값을 = 로 넣어줍니다.

data["Nationality_FR"] = data["Nationality"] == "FR"
data["Nationality_UK"] = data["Nationality"] == "UK"

# 파이썬에서는 True / False 를 각각 1 / 0 으로 처리합니다.
# 향후 알고리즘에 집어넣으려면 숫자만 가능하기 때문에 이렇게 처리해줍니다. 
data[["Embarked", "Nationality_FR", "Nationality_UK"]].head()

Embarked Nationality_FR Nationality_UK
PassengerId
1 S False True
2 C True False
3 S False True
4 S False True
5 S False True
# 성별 또한 Nationality 와 동일하게 male, female 두 가지 경우의 수만 있습니다.
# 새로운 컬럼을 만들어, male 에는 male 인 경우만 넣고
# female 에는 female 인 경우만 넣어주면 쉽게 분리가 될 것입니다.
# 컬럼을 선택해 == 으로 불리언 참/거짓 판별을 한 후, 참/거짓 값을 = 로 넣어줍니다.

data["male"] = data["Sex"] == "male"
data["female"] = data["Sex"] == "female"

# 파이썬에서는 True / False 를 각각 1 / 0 으로 처리합니다.
# 향후 알고리즘에 집어넣으려면 숫자만 가능하기 때문에 이렇게 처리해줍니다. 
data[["Sex", "male", "female"]].head()

Sex male female
PassengerId
1 male True False
2 female False True
3 female False True
4 female False True
5 male True False
# Pclass 또한 Nationality 와 동일하게 1, 2, 3 세 가지 경우의 수만 있습니다.
# 위와 동일하게 1, 2, 3 을 각각 class_high, class_mid, class_low 로 넣어줍니다.
data["Class_High"] = data["Pclass"] == 1
data["Class_Mid"] = data["Pclass"] == 2
data["Class_Low"] = data["Pclass"] == 3

data[["Pclass", "Class_High", "Class_Mid", "Class_Low"]].head()

Pclass Class_High Class_Mid Class_Low
PassengerId
1 3 False False True
2 1 True False False
3 3 False False True
4 1 True False False
5 3 False False True

원-핫-인코딩 응용하기

# data.describe() 에서 기초 통계량을 알 수 있었습니다.
# 여기에서 Fare 컬럼의 값만 조회해보겠습니다.
# 최대 / 최소 / 평균 / 중앙값 / 분위값 을 확인하면
# 1. 돈을 안 내고(?) 탄 사람도 있습니다.
# 2. 중앙값과 평균값이 2배가량 차이가 납니다.

data.describe()['Fare']
count    891.000000
mean      32.204208
std       49.693429
min        0.000000
25%        7.910400
50%       14.454200
75%       31.000000
max      512.329200
Name: Fare, dtype: float64
# 중앙값(15)을 첫 번째 기준점으로 삼고
# 중앙값(15) + 표준편차(50)를 두 번째 기준으로 삼아
# Fare 를 3등분하여 각각 Fare_Low, Fare_Med, Fare_High 로 합니다.
# & 는 and 를 의미합니다.

data["Fare_Low"] = data["Fare"] <= 15
data["Fare_Med"] = (data["Fare"] > 15) & (data["Fare"] <= 65)
data["Fare_High"] = data["Fare"] > 65

data[["Fare", "Fare_Low", "Fare_Med", "Fare_High"]].head()

Fare Fare_Low Fare_Med Fare_High
PassengerId
1 7.2500 True False False
2 71.2833 False False True
3 7.9250 True False False
4 53.1000 False True False
5 8.0500 True False False
# 작업한 data 변수에 담긴 데이터프레임을 판다스의 to_csv로 저장합니다.
# "data/titanic_modified.csv" 의 의미는
# data 폴더에 titanic_modified.csv 이름으로 저장하겠다는 의미입니다.

data.to_csv("data/titanic_modified.csv")