230405 / BSA05. 데이터 전처리

BSA05-Data-Transform.ipynb

 

필요한 패키지

import pandas as pd
import numpy as np

from pandas.api.types import CategoricalDtype
import datetime

 

수치자료 → 범주형 자료

# 난수 값은 seed에 영향을 받음 (seed값을 지정해서 같은 결과가 나오도록)
np.random.seed(316)
정규난수 = np.random.normal(size=1000)  # 정규분포를 따르는 난수 1000개를 발생시킴
정규난수[0:10]  # 기본적으로 array 형태로 저장됨

# 구간 5개로 나눔 (구간의 길이가 동일하도록)
# (-3.118, -1.818], (-1.818, -0.525], (-0.525, 0.768], (0.768, 2.062], (2.062, 3.355]

동일구간 = pd.cut(정규난수,bins=5)
동일구간.describe()  # 도수분포표 (구간 안에 몇 개의 데이터가 있는지)
# 정규분포를 따르는 난수를 발생했으므로 중간 구간에 해당하는 데이터가 많고, 양끝에는 적음

# (-3.5, -1.5] = A, (-1.5, -0.5] = B, ..., (1.5, 3.5] = E로 구간 설정
구간지정 = pd.cut(정규난수, bins=[-3.5, -1.5, -0.5, 0.5, 1.5, 3.5], labels=["A","B","C","D","E"])
구간지정.describe()  # 빈도 확인 

# 해당되는 객체의 개수가 동일하도록 구간 지정 (구간의 길이가 달라짐)
# 구간을 20%, 40%, 60%, 80%, 100% 비율로 구간 지정
동일개수 = pd.qcut(정규난수, q=5) 
동일개수.describe()

# 25%씩 4개의 구간으로 나눔
비율지정 = pd.qcut(정규난수, q=[0, 0.25, 0.5, 0.75, 1.])
비율지정.describe()

# 명목자료로 표시할 때에는 labels를 설정해야 함
# ordered=False : 순서를 가지지 않는 nominal(명목형 자료)임 
구간지정 = pd.cut(정규난수, bins=[-3.5, -1.5, -0.5, 0.5, 1.5, 3.5], labels=["A","B","C","D","E"], ordered=False)
# 범주값을 활용하고 싶은 경우
구간지정.categories

 

범주형 자료 → 수치자료

주택 = pd.read_csv("housing.csv")
주택.head()

# 내가 임의로 만든 문자열을 double quotation으로 
# 파이썬에서 제공하는 것은 single quotation으로 
주택["해변근접성"] = 주택['ocean_proximity'].astype('category')
주택["해변근접성"] # 크기 순서가 없는 명목형 자료임

# 명목형 자료는 가변수로
# 5개의 더미변수로 데이터프레임이 생성됨 (인덱스 자동 지정)
주택위치 = pd.get_dummies(주택["해변근접성"])

# 컬럼명은 가능한 공백이 없도록
주택위치.columns=["Ocean", "Inland", "Island", "NearBay", "NearOcean"]

 

순서자료 → 동일 간격의 수치 자료

# 명목자료를 순서자료로
순서지정 = ["A","B","C","D","E"]  # A < B < C < D < E의 순서
순서구간 = 구간지정.astype(CategoricalDtype(categories=순서지정, ordered=True))

순서변환 = {"A":1, "B":2, "C":3, "D":4, "E":5}  # mapping시키는 작업 
순서구간 = 순서구간.map(순서변환).astype('uint8')  # 순서자료(범주형) -> 숫자(수치자료)
# 그냥 변환하면 문자로 인식할 수도 있음
# uint8 : unsigned integer : 음수값 필요없으니까 (8bit의 integer)
# 순서자료가 아닌 일반 범주형 자료도 숫자(수치자료)로 변환 가능

 

변수의 type 확인

변수명 = 주택.columns.tolist()  # 전체 변수명을 리스트의 형태로
print("전체변수명:", 변수명)

주택._get_numeric_data()  # 문자형 데이터는 빠져있음

수치변수명 = 주택._get_numeric_data().columns.tolist()
범주변수명 = list(set(변수명)-set(수치변수명))  # 같은 것을 제외하고 다시 리스트로
print("수치변수명:", 수치변수명)
print("범주변수명:", 범주변수명)

주택.select_dtypes(exclude=np.number)

수치변수명 = 주택.select_dtypes(include=np.number).columns.tolist()  # 숫자 데이터를 가져오기
범주변수명 = 주택.select_dtypes(exclude=np.number).columns.tolist()  # 숫자 데이터를 버리기 
print("수치변수명:", 수치변수명)
print("범주변수명:", 범주변수명)

 

표준화

주택.info()

수치자료 = 주택.iloc[:,[2,3,4,5,6,7]]
수치자료.head()

수치자료.mean()  # axis=0 이 default값

# 표준화 (각 열의 평균을 계산해서 빼야 함)
수치자료-수치자료.mean(axis=0)  # 열 기준으로 계산

표준자료 = (수치자료-수치자료.mean(axis=0))/수치자료.std(axis=0)  # 표준편차로 나누기
표준자료.head()

 

정규화

# 정규화 (0~1 사이의 값으로 변환)
정규자료 = (수치자료-수치자료.min(axis=0))/(수치자료.max(axis=0)-수치자료.min(axis=0))
정규자료.tail()

 

시간자료 처리

# 시간 생성
pd.date_range("2023-3-2","2023-6-21")  # 시작, 끝
# freq='D' : 주기를 하루 단위로 데이터 생성

pd.date_range("2023-3-2",periods=100)  # 23/3/2일부터 하루 단위로 100일 간 날짜 생성
pd.date_range("2023-3-2", periods=5, freq='h')  # 23/3/2 00:00:00부터 1시간 단위로 5개 시간 생성

# 문자열 데이터 -> 시간 자료
# date time index 사용
시간자료 = pd.DataFrame({'날짜':['2023-01-01 01:10:00', '2023-02-25 03:20:30',
                           '2023-03-02 06:30:00', '2023-04-19 10:40:30',
                           '2023-05-16 12:50:00', '2023-06-29 15:00:30',
                           '2023-07-01 18:10:00', '2023-08-15 21:50:30']})
시간자료.head()  # '날짜'라는 하나의 변수에 8개의 데이터 입력
시간자료["날짜"]  # 안에 있는 값만 사용하는 경우

# 시간자료로 변환
# errors='raise' : 에러 발생 시 예외 형태로 표시함
시간자료['날짜'] = pd.to_datetime(시간자료['날짜'], format='%Y-%m-%d %H:%M:%S', errors='raise')
시간자료.info()

시간자료  # 연,월,일,시,분,초를 각각 가져올 수 있음

시간자료['date'] = 시간자료['날짜'].dt.date  # YYYY-MM-DD(문자)
시간자료['year'] = 시간자료['날짜'].dt.year # 연(4자리숫자)
시간자료['month'] = 시간자료['날짜'].dt.month  # 월(숫자)
시간자료['월이름'] = 시간자료['날짜'].dt.month_name()  # 월(문자)
시간자료['day'] = 시간자료['날짜'].dt.day  # 일(숫자)
시간자료['time'] = 시간자료['날짜'].dt.time  # HH:MM:SS(문자)
시간자료['hour'] = 시간자료['날짜'].dt.hour  # 시(숫자)
시간자료['minute'] = 시간자료['날짜'].dt.minute  # 분(숫자)
시간자료['second'] = 시간자료['날짜'].dt.second  # 초(숫자자)
시간자료.head()

시간자료.info()  # 숫자형(int)로 바뀌면서 연산이 가능해짐

 

날짜, 시간 조정

시간자료

시간자료['날짜']+datetime.timedelta(days=5)  # 5일씩 더하는 작업
시간자료['날짜']+datetime.timedelta(days=-5)  # 5일씩 빼는 작업

# 시간간격 조정 : resample
# np.random.randn(100) : 난수 100개 생성
# 난수를 series로 (dataframe의 한 줄(컬럼)처럼)
# 인덱스를 날짜로 (2023/1/1, 2023/1/2, ...)
시계열 = pd.Series(np.random.randn(100), index=pd.date_range("2023-1-1", periods=100, freq="D"))
시계열.head()

# 주 단위(week)로 데이터를 가져와서 7개의 평균을 계산하는 작업
시계열.resample('W').mean()

# freq="T" : 주기를 분 단위로
분당자료 = pd.Series(np.random.randn(100), index=pd.date_range("2023-1-1", periods=100, freq="T"))
분당자료.head()

# 10분간의 자료 중 open, high, low, close 값 출력
# 10분 단위로 잘라서 10개의 데이터의 o(시작점), h(제일 큰 값), l(제일 작은 값), c(마지막)
분당자료.resample('10T').ohlc()