import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import make_blobs
필요한것들을 먼저 임포트해줬습니다.
1. Clusters(클러스터)
- 유사한 특성을 가진 개체들의 집합
- 고객분류, 유전자 분석, 이미지 분할
X, y = make_blobs(n_samples=100, centers=3, random_state=2024)
X,y를 리턴받을거고 100개의 데이터를 값을 랜덤하게 만든다음에 센터스를 3으로 하면 정답(클래스)를 3가지로 만듭니다
랜덤스테이트는 2024로 줍니다.
X = pd.DataFrame(X)
X
sns.catterplot(x=X[0], y=X[1], hue=y)
hue=범례
정답에따라 색을 좀 다르게 준 것들입니다. 이러면 솔직히 군집나누기가 좀 쉽지않아보이죠?
그래서 살짝 랜덤스테이트를 변경해보겠습니다.
X, y = make_blobs(n_samples=100, centers=3, random_state=2023)
sns.scatterplot(x=X[0], y=X[1], hue=y)
from sklearn.cluster import KMeans
km = KMeans(n_clusters=3)
km.fit(X)
pred = km.predict(X)
비지도학습이고 레이블이없는데 데이터들중에서 가까운녀석들끼리 조합을 시켜서 최적으로 데이터를 나눌떄 어떻게하면 가장 좋을지 판단해주는 알고리즘입니다.
clusters=3은 3개로 나누라는 의미입니다. km.fit(X)은 정답을 주지않았음을 의미합니다.
예측을 시켜보고 그려보자면
sns.scatterplot(x=X[0], y=X[1], hue=pred)
잘 나뉘어지는것을 확인할 수 있습니다.
만약 k값을 몰랐다고 치면
km = KMeans(n_clusters=5)
km.fit(X)
pred = km.predict(X)
sns.scatterplot(x=X[0], y=X[1], hue=pred)
이런식으로 나오게 됩니다. 가까운값에 대한 차이가 있기때문에 이것은 100% 같게 나오진 않습니다.
즉 최적의 k를 찾는것이 kMeans에서의 목적이라고 할 수 있습니다.
최적의 k값을 얻는 방법도 알아보도록 하겠습니다.
몇등분으로 나뉘었을떄 가장 깔끔한지 알아보려면 어떻게 해야할지 알아보려고 합니다.
데이터가 막 나열되어있으면 몇 등분으로 나누는게 가장 깔끔한지 보려면 센터값을 알아야합니다
그리고 센터값은 평가값에 대해서 이야기가 됩니다.
평가값 | 하나의 클러스터안에 중심적으로부터 각각의 데이터 거리를 합한 값의 평균 |
km.inertia_
#114.2556884130779
하나의 군집에 이루어진 중심점으로부터 각각의 데이터들을 더한다음에 평균을 낸 값입니다.
이것을 가지고 k값을 얻어낼 수 있습니다.
inertia_list = []
for i in range(2,11):
km = KMeans(n_clusters=i)
km.fit(X)
inertia_list.append(km.inertia_)
k값을 돌려보는겁니다.
그럼 최적의 k값을 확인을 할 수 있겠죠
inertia_list
2부터10까지의 평가값이 들어가있습니다.
sns.lineplot(x=range(2,11), y=inertia_list) # 엘보우 메서드
군집 나눔에있어서 깔끔하게 나뉘는게 중요한건데 그러기 위에서는 훅 낮아지는 첫 지점을
보시면 됩니다. 그것이 바로 정답입니다.
위에서 이미 정답을 알고 있었지만요!
이것은 엘보우 메서드 라고합니다.
2. marketing 데이터셋
프로젝트 목표
종속변수가 없다고 생각하고 (비지도)
군집으로 묶어서 고객을 어떻게 분류하면 최적으로 분류할 수 있는지를 머신러닝으로 나눠보려고합니다.
나뉜 군집으로 마케팅을 어떻게 할지를 생각해보려고 합니다.
mkt_df = pd.read_csv('데이터경로')
mkt_df
mkt_df.info()
여기서 정말 필요없는 것들은 id라서 id를 drop해주기로하겠습니다.
mkt_df.drop('ID', axis=1, inplace=True)
mkt_df.describe()
소득을 보니 조금 눈에 띄지만 소득수준이 이상치라고 예상이 됩니다.
단 삭제한다고 생각했을때 이렇게하게되면 문제가 생깁니다.
# mkt_df = mkt_df[mkt_df['Income'] < 200000 ] #NaN도 함께 삭제됨
NaN도 함께 삭제되기때문에 적합한 방법은 아니라고 생각합니다.
그래서
mkt_df = mkt_df[mkt_df['Income'] != 666666] #NaN이 삭제되지 않음
로 != 로 적용시켜줍니다.
그리고,
다양한 기준으로 나눠보도록 하겠습니다.
mkt_df.sort_values('Year_Birth')
mkt_df.sort_values('Income', ascending=False)
각각 생년과 소득 내림차순으로 정렬해보았습니다.
그런데 생년을보면 min값이 1893년생도 있더라고요 이 부분을 확인해보니 값은 있는것으로 확인했습니다.
데이터가 조밀하게 있는것으로 보니 결측치라고 생각하지않고 넘어가도록 했습니다.
그리고 INCOME데이터중 삭제시켰는지 확인도 했습니다.
mkt_df.isna().mean()
mkt_df = mkt_df.dropna()
mkt_df.isna().mean()
NaN있는지 확인했더니 income만있는것으로 확인됩니다.그래서 삭제해줍니다.
다 날라간것을 확인할 수 있습니다.
mkt_df.info()
결측치 없고 잘 정리된것을 확인할 수 있습니다.
여기서 6번에 가입일이있는데 이 부분을보면 object입니다.
이 부분을 디타입으로 변경해주어야되는데 이유는 연산이 안되기때문이죠
mkt_df['Dt_Customer'] = pd.to_datetime(mtk_df['Dt_Customer'])
#error
에러가 발생하는 이유는 날짜 타입이 맞지 않기 때문입니다.
mkt_df['Dt_Customer'] = pd.to_datetime(mkt_df['Dt_Customer'], format='%d-%m-%Y')
mkt_df.info()
다시 format을 이용해서 d-m-y로 잡아주고 넣어준 다음 인포를 확인해보면
잘 변경된것을 확인할 수 있습니다.
# 마지막으로 가입된 사람을 기준으로 데이터의 가입 날짜(달)의 차를 구하기
# pass_month
mkt_df['pass_month'] = (mkt_df['Dt_Customer'].max().year * 12 + mkt_df['Dt_Customer'].max().month) - (mkt_df['Dt_Customer'].dt.year * 12 + mkt_df['Dt_Customer'].dt.month)
mkt_df.head()
일단 년도를 뽑고 *12를 하면 달로 뽑을 수 있고 그 다음 달로 뽑을 수 있으니 더할 수 있게 됩니다.
mkt_df.drop('Dt_Customer', axis=1, inplace=True)
mkt_df.head()
날짜는 오브젝트형으로 사용하는것은 좋지않기 때문에 날렸습니다.
잘 나오는것도 확인할 수 있습니다.
#와인 + 과일 + 육류 + 어류 + 단맛 + 골드
#Total_mnt
mkt_df['Total_mnt']=mkt_df[['MntWines','MntFruits','MntMeatProducts','MntFishProducts','MntSweetProducts','MntGoldProds']].sum(axis=1)
mkt_df.head()
전체 구입한것들을 또 파생변수로 담겠습니다.
mkt_df['Children'] = mkt_df[['Kidhome','Teenhome']].sum(axis=1)
mkt_df.head()
삭제하기는 좀 그런 데이터들을 묶어주도록 하겠습니다. (파생변수 생성)
mkt_df.drop(['Kidhome','Teenhome'], axis=1, inplace=True)
그런데 필요는없기때문에 children만 남겨두고 kidhome과 teenhome은 삭제를 해주었습니다.
mkt_df.info()
이제는 2,3번만 바꾸어주면됩닌다.
mkt_df['Education'].value_counts()
#Education
#Graduation 1115
#PhD 481
#Master 365
#2n Cycle 200
#Basic 54
#Name: count, dtype: int64
mkt_df['Marital_Status'].value_counts()
#Marital_Status
#Married 857
#Together 572
#Single 471
#Divorced 232
#Widow 76
#Alone 3
#Absurd 2
#YOLO 2
#Name: count, dtype: int64
Education은 원핫인코딩
Marital_Status는 두 분류로 나누어보려고 합니다.
mkt_df['Marital_Status'] = mkt_df['Marital_Status'].replace({
'Married':'Partner',
'Together':'Partner',
'Absurd':'Single',
'Widow':'Single',
'YOLO':'Single',
'Single':'Single',
'Divorced':'Single',
'Alone':'Single'
})
파트너와 싱글 두 분류로 다시 나누었습니다.
mkt_df['Marital_Status'].value_counts()
mkt_df = pd.get_dummies(mkt_df, columns=['Education','Marital_Status'])
mkt_df.head()
데이터 전처리하고 스케일링을 하면 좋겠지만 하지않아야할것들이있습니다.
1. 범주형변수(카테고리컬 한 것)가 만들어져있는것
2. 원핫인코딩 등
from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
mkt_df.info()
객체를ss로 만들었습니다.
ss.fit_transform(
mkt_df[['Year_Birth', 'Income', 'Recency', 'MntWines', 'MntFruits',
'MntMeatProducts', 'MntFishProducts', 'MntSweetProducts',
'MntGoldProds', 'NumDealsPurchases', 'NumWebPurchases',
'NumCatalogPurchases', 'NumStorePurchases', 'NumWebVisitsMonth',
'Complain', 'pass_month', 'Total_mnt', 'Children']]
)
모두 범위가 있는 컬럼들이라 총 18개를 넣어주었습니다.
스케일링된것을 확인할 수 있습니다.
만약 이해가 안가면 데이터 분석(스케일러를 확인해보기)
df에 적용해보기
mkt_df[['Year_Birth', 'Income', 'Recency', 'MntWines', 'MntFruits',
'MntMeatProducts', 'MntFishProducts', 'MntSweetProducts',
'MntGoldProds', 'NumDealsPurchases', 'NumWebPurchases',
'NumCatalogPurchases', 'NumStorePurchases', 'NumWebVisitsMonth',
'Complain', 'pass_month', 'Total_mnt', 'Children']] = ss.fit_transform(
mkt_df[['Year_Birth', 'Income', 'Recency', 'MntWines', 'MntFruits',
'MntMeatProducts', 'MntFishProducts', 'MntSweetProducts',
'MntGoldProds', 'NumDealsPurchases', 'NumWebPurchases',
'NumCatalogPurchases', 'NumStorePurchases', 'NumWebVisitsMonth',
'Complain', 'pass_month', 'Total_mnt', 'Children']]
)
mkt_df에 다시 fit_transform된 것을 넣고 각 필드에 맞게 저장이 됩니다.
이 부분이 이해가 잘 가지 않아서 다시설명을 적어놓자면
각각 필드를 스텐다드스케이러 형식으로 스케잉링시켰고 그것들을 df적용하려고 하는거고
각각 이렇게 스케일링된것이 덮어진다고 생각하면 됩니다.
mkt_df
3. KMeans
- k개의 중심점을 찍은 후에 이 중심점에서 각 점간의 거리의 합이 가장 최소가 되는 중심점 k의 위치를 찾고, 이 중심점에서 가까운 점들을 중심점을 기준으로 묶는 알고리즘입니다.(k개의 클러스터의 수는 정해주어야 하는것이 특징입니다)
- 군집의 개수(K)설정 -> 초기 중심점 설정 -> [데이터를 군집에 할당(배정) -> 중심점 재설정(갱신)] 반복
inertia_list = []
for i in range(2,11):
km = KMeans(n_cluster=i, random_state=2024)
km.fit(mkt_df)
inertia_list.append(km.inertia_)
2~11까지 돌아가고 학습시키고 그 결과값들이 이너시어 리트스에 들어가게 됩니다.
inertia_list
#[29919.188055126753,
# 26905.233635156495,
# 24780.99000529778,
# 23751.670604933766,
# 22709.4038715457,
# 22233.565555106637,
# 21141.751890886546,
# 20654.2152917408,
# 20236.488337531802]
이 값들로는 잘 모르니 시각화를 통해서 보려고 합니다.
sns.lineplot(x=range(2,11), y=inertia_list)
그런데 엘보매소드처럼 보이지가 않아서 원하는 k가 어떤건지 알 수 없을것같습니다.
즉 최적의 k를 알아내기 힘들다는것이죠.
찾자면 4,6,8정도..? 이렇게 보면 잘 모르기 클러스터링의 품징을 평가하는 지표인 실루엣 스코어를
사용해보려고 합니다.
4. 실루엣 스코어(Silhoutte Score)
- 클러스터링의 품질을 평가하는 지표로, 각 데이터 포인트가 자식이 속한 클러스터와 얼마나 유사하고 다른 클러스터와는 얼마나 다른지 측정하는 수치입니다.
- 수치는 -1~1 사이의 값을 가지고 값이 클수록 클러스터링의 품질이 높다고 할 수 있습니다.
from sklearn.metrics import silhouette_score
score =[]
for i in range(2,11):
km = KMeans(n_clusters=i, random_state=2024)
km.fit(mkt_df)
pred = km.predict(mkt_df) #학습뿐만아니라 예측까지 킴
score.append(silhouette_score(mkt_df, pred)) #군집이 잘 되었지 판단
사용하는 방법은 학습 뿐만 아니라 예측을 시키고 예측 결과를 score에 담고 실제 값, 예측값을 비교함으로서
군집이 잘 되었는지 판단합니다.
score
#[0.296789866551826,
# 0.20144426178353647,
# 0.20982124721599427,
# 0.1169123171804603,
# 0.10312058061546078,
# 0.0876739256263883,
# 0.10134194134091229,
# 0.10086273294875897,
# 0.10071335750020176]
sns.lineplot(x=range(2,11), y=score)
여기서 보면 3이라고 생각할 수 있는데 더 높은 구간으로 올라가니까 4가 가장 최적의 k라고 할 수 있습니다.
최적의 k를 찾았으니 클러스터링을 해보려고 합니다.
clusters에 4를 넣으면 됩니다. 여기서 random_state=2024로 고정시켜주어야합니다.
km = KMeans(n_clusters=4, random_state=2024)
km.fit(mkt_df)
학습을 완료 후 예측을 합니다.
pred = km.predict(mkt_df)
pred
#array([2, 0, 2, ..., 2, 1, 0], dtype=int32)
지금 결과값을 보면 1천째데이터는 2번 2번째는 0번 등등으로 예측을 됩니다.
이것을 레이블로 해줍니다.
mkt_df['label'] = pred
mkt_df
가장 우측에 레이블이 생겼고 이것으로 군집을 나누었습니다.
이제
레이블을 가지고 어떻게 나누어졌는지 확인해보도록 하겠습니다.
mkt_df['label'].value_counts()
이 결과로 보면 0번에 해당하는사람이 1019번 물건을 산 데이터로 해석할 수 있습니다.
나중에 마케팅을 할때 각각의 고객을 통해서 분석하고 소비패턴을 확인해볼 수 있습니다.
이어서 시간이없어서 따로 공부하면 좋을 알고리즘을 알려주셨습니다.
공부해보기
- K-최근접 이웃알고리즘(KNN) : 거리 기반 모델. 지도 학습 기반 알고리즘. 회기/분류 해결할 수 있지만 분류에서 많이 해결하는 알고리즘입니다.
- 나이브 베이즈(Naive Bayes): 베이즈 정리를 적용한 조건부 확률 기반 분류 모델 지도 학습 기반 알고리즘, 분류가능
- 앙상블모델 중 부스팅모델(성능을 극으로 끌어올림)중
- XG부스트 : 순차적으로 트리를 만들어 이전 트리로부터 더 나은 트리를 만들어내는 알고리즘 , 지도학습알고리즘, 회기/분류 가능
- lightGBM(LightGMB) : 최신 부스팅 모델. 지도 학습, 회귀/분류 가능
'AI > 머신러닝' 카테고리의 다른 글
[ML]로지스틱 회귀_승진 될지 안 될지 분류해보기 (2) | 2024.07.07 |
---|---|
[ML] 선형 회귀_Rent값 예측 모델 만들기(2) (0) | 2024.07.04 |
[ML] 선형 회귀_Rent값 예측 모델 만들기(1) (0) | 2024.07.02 |
[ML] 아이리스 데이터셋(Iris DataSet) (1) | 2024.07.01 |
[Python] 판다스(pandas) 데이터 프레임 합치기,산술연산, 원핫인코딩 등 (1) | 2024.06.18 |