티스토리 뷰
[에이블스쿨] 3일차 - CRISP-DM : 데이터 분석의 순환 / Numpy와 Pandas(groupby를 제대로 알자)
sikaro 2024. 2. 23. 16:45CRISP-DM
Cross-Industry Standard Process for Data Mining
데이터마이닝을 위한 산업을 넘나드는 표준 프로세스
데이터 분석 방법론
- business understanding <-> data understanding -> data preparation <-> modeliing -> evaluation -> deployment
- 비즈니스 이해 <-> 데이터 분석 -> 데이터 준비(전처리) <-> 모델링(ML/DL) -> 평가 -> 배포(운영 시스템)
data preparation은 modeling과 상호작용하며, business understanding은 data understanding과 상호작용한다.
evaluation이 끝나면 다시 business understanding으로 가서 피드백한다.
회사에서 기대하는 건 비즈니스 문제를 해결하는 AI 개발자.
어떤 문제를 해결해야 하는지 정의하는 과정이다.
두 가지의 질문이 필요하다.
시작 질문 : 무엇이 문제인가?
마지막 질문: 그래서 문제가 해결되었는가?
분석을 하려면 데이터를 어떻게 준비해야 하는가?
데이터 수집, 웹 크롤링
모델링 : 전처리, 머신러닝,딥러닝,언어지능,시각지능
웹 서비스 구축 + MLOps
django, MLflow
이 전체 과정을 미니프로젝트로 간접 실습을 하고, 빅 프로젝트로 실무와 같은 경험을 하게 된다.
가장 첫번째 단계가 분석을 위한 data set, numpy와 pandas.
분석할 수 있는 정보는 딱 2가지 밖에 없다.
범주형과 수치형
범주형 | 수치형 |
명목형 | 이산형 |
순서형 | 연속형 |
범주형 - 명목(이름),순서형(연령대)
수치형 - 연속형, 이산형(값이 정수, 셀 수 있다)
범주형은 그룹으로 묶을 수 있다. 묶음 안에 공통된 특징이 있다. (질적 및 정성적)
수치형은 양적이다(양적 및 정략적)
범주형은 수치형에서 만들어진다 (나이 ->연령대)
시계열 데이터의 경우 대체로 범주이다. (1~12월)
그러나 개월로 취급하면 수치형이다.
사칙연산이 가능하면 수치형이다.
12월은 1월의 12배라는 계산은 성립하지 않는다.
이런 걸 결정하는 데 있어서 비즈니스 관점이 굉장히 중요하다.
비즈니스 관점의 지식을 도메인 지식이라고 한다.
데이터 실무에서는?
우리는 장의 유형을 A~E까지 정의하고 F,G라는 유형이 나오면 그걸 추가하지 않는다.
수치형이냐 범주형이나에 따라서 분석하는 방법과 모델링이 달라진다.
데이터
분석할 수 있는 데이터는 기본이 2차원이다.
1. Column = 열,정보,변수, 요인
대채로 변수가 열을 의미한다.
이 변수에는 요인에 해당되는 변수(독립변수)가 있고, 예측해야 하는 변수(타깃값)이 있다.
종속변수 = 타깃값(Target,y,output,Label)
이탈여부 = 범주형
독립변수 = Features, X, input, (독립변수)
해당 타깃값을 예측하는 데 있어서 필요한 정보들(가입 기간, 요금제, 결합할인 등)
2. row = 분석단위, 샘플, 관측치, 데이터 건
일 단위로 분석할거라면 일 단위로 행이 쌓여 있어야 하고,
분 단위로 분석할거라면 분 단위로 행이 쌓여 있어야 한다.
행이 어떤 단위로 분석할 것인지 분석하고, 그 단위를 결정해서 행을 만든다.
Numpy 와 Pandas
Numpy는 수치 연산
pandas는 비즈니스 데이터 표현
딥러닝 가면 Numpy를 더 많이 쓴다.
요약
1.분석할 수 있는 정보의 종류 2가지 : 숫자, 범주
2. 두 가지 종류의 정보가 특별한 구조를 가져야 한다 : 기본 구조는 2차원이다.
행은 분석 단위, 데이터 건마다. 열은 변수, 요인
데이터의 Size라고 하면, 몇 건이냐를 의미하고, 그건 행을 의미한다.
리스트의 한계
리스트는 대량의 데이터를 처리하는 데 있어서 속도가 느리다.
그래서 행렬과 벡터 연산을 편리하게 하기 위한 라이브러리를 만들었다.
numpy의 기초적인 것만 다루고 넘어간다.
아래부터는 모르는 것만 상기시키기 위해 작성했습니다.
Numpy
import numpy as np
df.shape #차원의 형태 알려준다
주요 용어
Axis : 배열의 각축
Rank : 축의 개수 (차원)
Shape : 축의 길이(개수)
데이터 분석 및 모델링에서 Axis 0의 의미는
데이터의 건수다. 그리고 차원의 맨 끝은 개수이다.
2차원 데이터의 shape이 (1000,10)이면, 1000건이 10개 있다는 뜻이다.
3차원 데이터의 shape이 (1000,500,300)이면, 1000*500인 이미지라는 데이터가 300개 있다는 뜻이다.
print(a.ndim) #차원의 개수
print(a.shape) #형태
print(a.dtype) # array의 타입
3차원 모델링은 시계열 모델링을 할 때 사용한다.
yolo 모델링을 할 때(이미지, 동영상 등)
Reshape은 요소 개수가 바뀌지 않는 이상 똑같이 사용할 수 있다.
6 *2 => 3*4 => 12 *1 등등
df.reshape(2,3) #2,3 배열로 변환
df.reshape(6,) #1차원 배열로 변환
df.reshape(6) #1차원 배열로 변환
reshape에서 -1을 사용하면 행이나 열 한쪽만 지정해서 자동으로 계산해준다.
df.reshape(1,-1) #1행 배열로 변환
df.reshape(2,-1) #2행 배열로 변환
df.reshape(3,-1) #3행 배열로 변환
단, 나눠지지 않으면 오류가 난다.
numpy의 인덱스과 슬라이싱
print(a[0, 1]) #요소는 행,열로 표기
print(a[행]) #열 인덱스 생략시 전체 열을 불러온다.
print(a[행,열])
print(a[행,[0,2]) #로 특정 열이나 행 표기할 떄는 리스트로 감싸준다.
print(a[행, 0:2) # :로 슬라이싱 가능
print(a[ [0,1] ]) #행을 0과 1을 가져온다. => 2차원
차원을 2차원으로 만들고 싶다면, 행이나 열에 []를 둘러서 [행], [열]으로 넣을 수 있다.
a[:,1] # 1차원
a[:,[1]] #2차원
a[:,1:2] #2차원
범위로 줘도 2차원으로 만들 수 있다.
조건에 맞는 요소를 실행하려면 [ ] 안에 조건을 넣어주면 된다.
print(sc[sc >= 90]) #결과는 1차원 배열
con = sc>=90
print(sc[con])
con = sc%2==0 #조건이 array로 나온다.
array([[ True, False, True, False, False, False],
[ True, False, True, False, False, False]])
print(sc[con])
조건을 변수로 선언해서 사용도 가능하다. 여러 조건을 &(and)과 |(or)로 연결해서 사용 가능하다.
행렬의 더하기 빼기는 쉽다.
그러나 행렬곱이나 행렬 나누기는 각각 x,y의 같은 행과 열에 있는 값을 곱한 결과가 나온다.
x 배열(2,2)
y 배열(2,2)
즉, x배열과 y배열이 같을 때
print(x + y)
print(x - y)
print(x * y)
print(x / y)
print(np.add(x, y))
print(np.subtract(x, y))
print(np.multiply(x, y))
print(np.divide(x, y))
지수연산은 **또는 .power()를 사용한다.
print(x ** y)
print(np.power(x, y))
도트 연산은 따로 만들어야 한다.
배열 내 집계 함수(np.sum(),np.mean(),np.std())
np.sum(), 혹은 array.sum()을 사용하는데, axis=0은 열 기준으로 더하고, axis =1은 행 기준으로 더한다.
axis=0은 위에서 아래로 더하고, axis=1은 왼쪽에서 오른쪽으로 더한다.
print(np.sum(a))
print(np.sum(a, axis = 0))
print(np.sum(a, axis = 1))
똑같이 mean이나 std도 가능하다.
자주 사용되는 함수
np.argmax(a) = 전체 중에서 가장 큰 값의 인덱스를 구한다.
여기서 인덱스 값은 당연하게도 왼쪽에서 오른쪽으로 증가한다.
만약 axis=0이면 열 별로, axis=1이면 행별로 나뉘어진다. 자매품 argmin
print(np.argmax(a))
print(np.argmax(a, axis = 0))
print(np.argmax(a, axis = 1))
여기서는 파라미터 = 입력 매개변수
np.where(조건, True 때의 값, False 때의 값)
np에 있는 조건이 True일 때
조건문의 결과는 0,1,0,1과 같이 array로 나온다.
np.where(a > 2, 1, 0)
array([0, 1, 0, 1])
np.where(a > 2, a, 0)
true일 때 기존값을 유지하려면 a를 쓰면 된다.
Pandas
데이터프레임은 관계형 데이터베이스에서의 테이블 또는 엑셀 시트와 같은 형태(2차원 구조)
변수들의 집합 -> 각 열을 변수라고 부른다.
구조 : 행 / 의미 : 분석 단위 / 다른 말로 관측치, 샘플
구조 : 열 / 의미 : 정보 / 다른 말로 변수(feature, target)
열 하나를 떼어낸 것이 시리즈 - 시리즈는 어떤 정보의 값들을 가지고 있다.
pandas의 사용 목적이 사실상 데이터프레임을 사용하기 위한 것.
데이터를 처리, 조회, 분석하는 가장 효율적인 방법.
보통은 csv 파일, 엑셀 파일 또는 DB에서 읽어온다.
데이터 프레임은 인덱스와 열이름이 있고 없고에 따라 3가지 상태로 나뉜다.
딕셔너리로부터 데이터 프레임 만들기
df = pd.DataFrame(dict1)
print(df.head(10)) #10행으로 출력하려면 값을 넣어주면 된다
pd.read_csv(./) 현재 디렉토리 불러오기
read_csv로 불러오기
csv는 사실 텍스트 파일이다.
- head(): 상위 데이터 5개
- tail(): 하위 데이터 5개
- shape: 데이터프레임 크기
- values: 값 정보 확인(저장하면 2차원 numpy 배열이 됨)
- value_counts(ascending=True) : 값별로 데이터의 수 확인,안에 넣으면 오름차순
- columns: 열 정보 확인(피처의 이름)
- dtypes: 열 자료형 확인(각 피처의 자료형)
- info(): 열에 대한 상세한 정보 확인
- describe(): 기초통계정보 확인
- unique(): 고유값 이름 확인
- nunique(): 고유값 개수 확인
- sort_values() : 데이터 순서대로 정렬
참고로 끝에 메소드를 추가로 붙이려면 그 데이터 타입에 맞는 코드를 만들어야 한다.
print(data.columns.values) # np array 형태
데이터 프레임을 리스트에 넣으면 열 이름이 반환된다.
list(data)
['Attrition',
'Age',
'DistanceFromHome',
'EmployeeNumber',
'Gender',
'JobSatisfaction',
'MaritalStatus',
'MonthlyIncome',
'OverTime',
'PercentSalaryHike',
'TotalWorkingYears']
열 자료형은 컬럼인 시리즈 형태로 출력된다. object type은 문자열이다.
# 열 자료형 확인
data.dtypes
Attrition int64
Age int64
DistanceFromHome int64
EmployeeNumber int64
Gender object
JobSatisfaction int64
MaritalStatus object
MonthlyIncome int64
OverTime object
PercentSalaryHike int64
TotalWorkingYears int64
dtype: object
(11,)
사실 이것보다 .info를 더 많이 쓰게 된다.
dtypes은 피처 요약표를 쓸 때 사용한다.
중요한 건 dtype's'다. 오류나면 s를 빠뜨리지 않았는지 조심하자.
data.sort_values(by='MonthlyIncome', ascending=False) #단일 기준으로 열을 정렬
data.sort_values(by=['JobSatisfaction', 'MonthlyIncome'], ascending=[True, False])
#복합 기준으로 열을 정렬
temp = data.sort_values(by=['JobSatisfaction', 'MonthlyIncome'], ascending=[True, False])
temp.reset_index(drop = True)
ascending이 False면 내림차순 정렬이다.
여러 개의 값을 받을 떄는 리스트를 쓴다.
복합 기준으로 정렬하고, index를 reset해준다. drop은 index가 column에 포함되지 않게 해준다.
고유값의 개수 확인은 value_counts()로 한다.
nunique()는 unique()로 구한 리스트의 len과 똑같다. -> 컬럼의 개
print(data['MaritalStatus'].value_counts())
MaritalStatus
Married 548
Single 384
Divorced 264
Name: count, dtype: int64
기본 집계 메소드도 알아둬야 한다.
print(data['MonthlyIncome'].sum())
print(data['MonthlyIncome'].max())
print(data[['Age', 'MonthlyIncome']].mean())
Age 36.943980
MonthlyIncome 6520.104515
dtype: float64
print(data[['Age', 'MonthlyIncome']].median())
Age 36.0
MonthlyIncome 4973.5
열이 여러개면 여러개를 표시해준다.
Pandas 기초의 두번쨰 : 데이터 프레임 조회 및 집계
판다스는 인덱스를 거의 안쓴다.
df.컬럼명으로도 불러올 수 있으나, 권장하지는 않는다.
대괄호 안에 리스트로 넣으면 시리즈를 붙여서 읽을 수 있다.
.loc은 [행 넘버, '컬럼명']
.iloc은 [행 넘버, 컬럼 넘버]
.loc 또한 조건 지정이 가능하다.
data.loc[data['DistanceFromHome'] > 10]
여러 조건을 지정할 떄는 &과 |을 사용한다.
원본을 덮어 쓸 것인지 말 것인지가 inplace이다.
display(data[data['DistanceFromHome'] > 10])
이런 방식도 가능하지만, 결측치가 있을 수 있다면 loc를 써야 한다.
isin()과 between()
isin([리스트] ) : 리스트 안에 존재하는 값만 조회한다.
무조건 isin 안에 리스트 형태로 입력해야 한다.
data.loc[data['JobSatisfaction'].isin([1,4])]
data.loc[(data['JobSatisfaction'] == 1) | (data['JobSatisfaction'] == 4)] #똑같은 의미
data.loc[data['JobSatisfaction'].isin(['컬럼1','컬럼2'])]
컬럼 1과 컬럼 2에 있는 값으로도 구할 수 있다.
isin의 값은 True False로 반환된다.
between(a,b)은 a부터 b 값 사이만.
참고로 a와 b도 포함이다.
data.loc[data['Age'].between(25, 30)]
data.loc[(data['Age'] >= 25) & (data['Age'] <= 30)] #같은 의미
행의 조건을 넣고, 후에 열의 이름을 적어주면 해당하는 열만 추출할 수 있다.
data.loc[data['MonthlyIncome'] >= 10000, ['Age', 'MaritalStatus', 'TotalWorkingYears']]
집계 함수
sum,mean,max,min,count,midean
groupby()
문법 = df.groupby('집계기준변수', as_index= ) ['집계대상변수']. 집계함수
성별 별로, 나이의, 평균의 계산해라
df.groupby('성별',as_index=)['나이'].mean
한마디로 '집계기준 변수'의 고유 값들로 나뉘어진, 나이 시리즈의 평균을 계산하는 것이다.
print(data['MaritalStatus'].unique())
data.groupby('MaritalStatus', as_index=True)['Age'].mean()
['Married' 'Single' 'Divorced']
MaritalStatus
Divorced 37.522727
Married 37.704380
Single 35.460938
Name: Age, dtype: float64
as_index는 대부분 False로 처리하는 경우가 많다.
디폴트가 True기 떄문에 False로 바꿔준다.
#as_index=False를 설정하면 행 번호를 기반으로 한 정수 값이 인덱스로 설정됩니다.
# MaritalStatus 별 Age 평균 --> 데이터프레임
data.groupby('MaritalStatus', as_index=False)[['Age']].mean()
MaritalStatus Age
0 Divorced 37.522727
1 Married 37.704380
2 Single 35.460938
하나의 그룹으로 두고, 여러 열을 집계할 수도 있다.
또 여러 개의 그룹을 만들어서 키 값으로 쓸 수도 있다.
이때 총 분류는 고유값1 개수 * 고유값 2 개수이다.
data.groupby('MaritalStatus', as_index=False)[['Age','MonthlyIncome']].mean()
# 'MaritalStatus', 'Gender'별 나머지 열들 평균 조회
data_sum = data.groupby(['MaritalStatus', 'Gender'], as_index=False)[['Age','MonthlyIncome']].mean()
# 확인
data_sum
집계할 컬럼을 표시하지 않으면, 모든 열에 대해서 다 집계한다.
이때 범주형도 다 계산 해버리므로 조심
df2 = df.groupby('Month', as_index=False)[['Ozone','Wind','Temp']].mean()
df2
Month Ozone Wind Temp
0 5 23.615385 11.622581 65.548387
1 6 29.444444 10.266667 79.100000
2 7 59.115385 8.941935 83.903226
3 8 59.961538 8.793548 83.967742
4 9 31.448276 10.180000 76.90000
아직도 헷갈린다면 이렇게 이해하면 직관적이다.
달별(1~12) 별로 각 피처들의 통계값을 구할 수 있다.
.agg
.agg를 쓰면, ['sum','mean','max,'min']의 값을 전부 보여준다.
.agg 안에 딕셔너리를 넣으면, {'total':sum, 'wind':mean}과 같이 쓸 수도 있다.
tmp= titanic.groupby(['Pclass','Survived'])[['Age','Fare']].agg(['max','min','mean','std'])
아주 편하다.
데이터의 결과로부터 유추할 수 있는 건 무엇인가?
운임의 차이로부터는 여자 승객의 운임이 비싼 곳들이 많다.
퀸즈타운은 여자 승객이 젊다. 왜 그럴까?
의문으로 끝나면 안되고 그 이유를 찾아야 한다.
결과를 어떻게 해석하는지를 다음 주 내내 찾는다.