1. Clusters(클러스터)
클러스터는 비슷한 속성을 가진 데이터 포인트들의 그룹(집합)입니다. 이는 데이터를 비슷한 패턴이나 특징을 공유하는 서로 다른 부분 집합으로 나누는 과정을 말합니다. 클러스터링은 데이터 내의 숨겨진 패턴이나 구조를 찾아 데이터를 유사한 특성을 가진 그룹으로 분류할 수 있습니다. (종속 변수가 없는 비지도 학습) 예를 들어 고객 분류, 유전자 분석, 이미지 분할 등으로 활용할 수 있습니다. K-평균 클러스터링, 계층적 클러스터링, DBSCAN (Density-Based Spatial Clustering of Applications with Noise) 등 다양한 클러스터링 알고리즘이 있습니다.
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
# 가상의 샘플 생성
X,y = make_blobs(n_samples=100, centers= 3, random_state=10)
# 100개의 샘플/ 3개의 클러스터
# random_state: 무작위성을 제어. 동일한 수를 사용하면 동일한 데이터 생성
- make_blobs 함수에서 생성된 X는 기본적으로 2차원 배열 (n_features로 다차원 배열 생성 가능)
X = pd.DataFrame(X)
y # 각 샘플이 속한 클러스터를 나타내는 레이블(클래스)
array([2, 2, 1, 0, 1, 1, 0, 2, 1, 0, 0, 1, 1, 2, 2, 1, 0, 1, 0, 1, 0, 2,
1, 2, 0, 1, 1, 1, 1, 0, 2, 1, 1, 0, 2, 2, 2, 1, 1, 1, 2, 0, 2, 2,
1, 0, 0, 0, 2, 0, 1, 2, 0, 0, 2, 0, 1, 2, 0, 0, 1, 1, 2, 2, 2, 0,
0, 2, 2, 2, 1, 0, 1, 1, 2, 1, 1, 2, 0, 0, 0, 1, 0, 1, 2, 1, 2, 0,
2, 2, 0, 0, 0, 2, 2, 2, 1, 0, 0, 0])
sns.scatterplot(x=X[0], y=X[1], hue=y)
from sklearn.cluster import KMeans
- 데이터를 K개의 클러스터로 그룹화하며, 각 클러스터의 중심(센터값)을 조정하여 클러스터를 형성합니다.
km = KMeans(n_clusters=3)
km.fit(X)
pred = km.predict(X)
# 원래는 예측에 넣는 값은 달라야하지만, 데이터를 하나만 만들었기때문에 일단 같은 값으로 해봄
sns.scatterplot(x=X[0], y= X[1], hue=pred)
km = KMeans(n_clusters=5) # 5개의 클러스터로 나눔
km.fit(X)
pred = km.predict(X)
sns.scatterplot(x=X[0], y= X[1], hue=pred)
- 평가값 : 하나의 클러스트 안에 중심점으로부터 각각의 데이터 거리를 합한 값 평균. 값이 작을수록 클러스터들이 모여있다고 볼 수 있다.(응집도가 높다)
km.inertia_
// 130.45207031101725
- n_clusters 를 몇개로 주는 것이(몇개의 클래스로 나누는 것이) 가장 효과적일지 찾아보기.
- 엘보우 메서드
inertia_list = []
for i in range(2, 11):
km = KMeans(n_clusters=i)
km.fit(X)
inertia_list.append(km.inertia_)
inertia_list
[976.8773336900748,
186.3658862010144,
154.5169216889871,
130.45207031101725,
114.3802551241855,
98.29150261690447,
85.77449714447347,
73.70078494670776,
64.53633931881369]
sns.lineplot(x=range(2,11), y=inertia_list)
- 군집의 수가 늘수록 평가값은 줄어들 수밖에 없음
- 다만 최적화가 됐을 때 값이 가장 큰 폭으로 줄어듬
2. marketing 데이터셋
mkt_df = pd.read_csv('/content/drive/MyDrive/KDT/머신러닝과 딥러닝/data/marketing.csv')
mkt_df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2240 entries, 0 to 2239
Data columns (total 21 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 ID 2240 non-null int64
1 Year_Birth 2240 non-null int64
2 Education 2240 non-null object
3 Marital_Status 2240 non-null object
4 Income 2216 non-null float64
5 Kidhome 2240 non-null int64
6 Teenhome 2240 non-null int64
7 Dt_Customer 2240 non-null object
8 Recency 2240 non-null int64
9 MntWines 2240 non-null int64
10 MntFruits 2240 non-null int64
11 MntMeatProducts 2240 non-null int64
12 MntFishProducts 2240 non-null int64
13 MntSweetProducts 2240 non-null int64
14 MntGoldProds 2240 non-null int64
15 NumDealsPurchases 2240 non-null int64
16 NumWebPurchases 2240 non-null int64
17 NumCatalogPurchases 2240 non-null int64
18 NumStorePurchases 2240 non-null int64
19 NumWebVisitsMonth 2240 non-null int64
20 Complain 2240 non-null int64
dtypes: float64(1), int64(17), object(3)
memory usage: 367.6+ KB
# 필요없는 열 삭제
mkt_df.drop('ID', axis=1, inplace=True)
# 이상치 확인
mkt_df.sort_values('Year_Birth')
mkt_df.sort_values('Income', ascending=False)
✔️ mkt_df = mkt_df[mkt_df['Income'] < 200000]
NaN이 있으면 NaN도 같이 날아감(저장되지 않음). 따라서 NaN 값이 있을 때는 함부로 위의 식으로 저장하면 안됨
mkt_df = mkt_df[mkt_df['Income'] != 666666]
# 결측치 삭제
mkt_df=mkt_df.dropna()
mkt_df.isna().mean()
Year_Birth 0.0
Education 0.0
Marital_Status 0.0
Income 0.0
Kidhome 0.0
Teenhome 0.0
Dt_Customer 0.0
Recency 0.0
MntWines 0.0
MntFruits 0.0
MntMeatProducts 0.0
MntFishProducts 0.0
MntSweetProducts 0.0
MntGoldProds 0.0
NumDealsPurchases 0.0
NumWebPurchases 0.0
NumCatalogPurchases 0.0
NumStorePurchases 0.0
NumWebVisitsMonth 0.0
Complain 0.0
dtype: float64
mkt_df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 2215 entries, 0 to 2239
Data columns (total 20 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Year_Birth 2215 non-null int64
1 Education 2215 non-null object
2 Marital_Status 2215 non-null object
3 Income 2215 non-null float64
4 Kidhome 2215 non-null int64
5 Teenhome 2215 non-null int64
6 Dt_Customer 2215 non-null object
7 Recency 2215 non-null int64
8 MntWines 2215 non-null int64
9 MntFruits 2215 non-null int64
10 MntMeatProducts 2215 non-null int64
11 MntFishProducts 2215 non-null int64
12 MntSweetProducts 2215 non-null int64
13 MntGoldProds 2215 non-null int64
14 NumDealsPurchases 2215 non-null int64
15 NumWebPurchases 2215 non-null int64
16 NumCatalogPurchases 2215 non-null int64
17 NumStorePurchases 2215 non-null int64
18 NumWebVisitsMonth 2215 non-null int64
19 Complain 2215 non-null int64
dtypes: float64(1), int64(16), object(3)
memory usage: 363.4+ KB
mkt_df['Dt_Customer'] = pd.to_datetime(mkt_df['Dt_Customer'])
# 마지막으로 가입된 사람을 기준으로 현재 데이터의 가입 날짜(달) 구하기
# pass_month
last_join_date = mkt_df['Dt_Customer'].max()
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.drop('Dt_Customer',axis=1, inplace=True)
# Total_mnt
# 와인+과일+육류+골드+어류+단맛제품의 합계 구하기
mkt_df['Total_mnt'] = mkt_df[['MntWines', 'MntFruits', 'MntMeatProducts', 'MntFishProducts', 'MntSweetProducts', 'MntGoldProds']].sum(axis=1)
mkt_df['Children'] = mkt_df[['Kidhome','Teenhome']].sum(axis=1)
mkt_df.drop(['Kidhome','Teenhome'],axis=1, inplace=True)
mkt_df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 2215 entries, 0 to 2239
Data columns (total 20 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Year_Birth 2215 non-null int64
1 Education 2215 non-null object
2 Marital_Status 2215 non-null object
3 Income 2215 non-null float64
4 Recency 2215 non-null int64
5 MntWines 2215 non-null int64
6 MntFruits 2215 non-null int64
7 MntMeatProducts 2215 non-null int64
8 MntFishProducts 2215 non-null int64
9 MntSweetProducts 2215 non-null int64
10 MntGoldProds 2215 non-null int64
11 NumDealsPurchases 2215 non-null int64
12 NumWebPurchases 2215 non-null int64
13 NumCatalogPurchases 2215 non-null int64
14 NumStorePurchases 2215 non-null int64
15 NumWebVisitsMonth 2215 non-null int64
16 Complain 2215 non-null int64
17 pass_month 2215 non-null int64
18 Total_mnt 2215 non-null int64
19 Children 2215 non-null int64
dtypes: float64(1), int64(17), object(2)
memory usage: 363.4+ KB
# object 컬럼 확인하기
mkt_df['Education'].value_counts()
Graduation 1115
PhD 481
Master 365
2n Cycle 200
Basic 54
Name: Education, dtype: int64
mkt_df['Marital_Status'].value_counts()
Married 857
Together 572
Single 471
Divorced 232
Widow 76
Alone 3
Absurd 2
YOLO 2
Name: Marital_Status, dtype: int64
mkt_df['Marital_Status']=mkt_df['Marital_Status'].replace({
'Married':'Partner',
'Together':'Partner',
'Single':'Single',
'Divorced':'Single',
'Widow':'Single',
'Alone':'Single',
'Absurd':'Single',
'YOLO':'Single'
})
mkt_df['Marital_Status'].value_counts()
Partner 1429
Single 786
Name: Marital_Status, dtype: int64
# 원 핫 인코딩
mkt_df =pd.get_dummies(mkt_df, columns=['Education', 'Marital_Status'])
# 정규화
from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
ss.fit_transform(mkt_df)
ss_df = pd.DataFrame(ss.fit_transform(mkt_df), columns=mkt_df.columns)
3. KMeans
클러스터링 알고리즘 중 하나인 K-Means는 데이터를 K개의 클러스터로 그룹화하는 비지도 학습 기법입니다. 이 알고리즘은 간단하면서도 효과적인 방법으로 데이터를 구분하는 데 사용됩니다.
K-Means 알고리즘의 주요 단계
1. 중심점 초기화
- 데이터에서 임의로 K개의 중심점을 선택하여 초기 클러스터 중심을 설정합니다.
( 사용자가 클러스터의 수인 K값을 정해줌.)
2. 클러스터 할당
- 각 데이터 포인트는 가장 가까운 중심점에 할당됩니다. 이때 거리 측정은 주로 유클리드 거리를 사용합니다.
- 할당된 클러스터는 중심점에 의해 정의되며, 같은 클러스터에 속한 데이터는 서로 유사한 특성을 가지고 있습니다.
3. 중심점 업데이트
- 각 클러스터에 속한 데이터들의 평균을 계산하여 새로운 중심점을 갱신합니다.
- 이 단계에서 중심점은 클러스터 내 데이터 포인트들의 중심으로 이동합니다.
4. 수렴 확인
- 중심점이 더 이상 크게 변하지 않을 때까지 클러스터 할당과 중심점 업데이트 단계를 반복합니다.
- 알고리즘이 수렴하면 최종 클러스터링이 완료됩니다.
K-Means의 특징 및 사용 사례
- K값의 선택: 사용자가 K값을 지정해주어야 합니다. 이를 위해 여러 실험적인 방법이나 엘보우 메서드를 사용하여 적절한 K값을 찾는 것이 일반적입니다.
- 클러스터의 모양: K-Means는 원형 클러스터를 가정하고 있기 때문에, 비교적 간단한 형태의 클러스터에서 잘 동작합니다. 다른 모양의 클러스터를 갖는 데이터에 대해서는 성능이 떨어질 수 있습니다.
이러한 특성들로 인해 K-Means는 간단하면서도 효과적인 클러스터링 알고리즘이며, 데이터를 손쉽게 그룹화하고 해석할 수 있는 장점을 가지고 있습니다. 주로 고객 세분화, 이미지 압축, 텍스트 문서 분류 등 다양한 분야에서 활용됩니다.
# ss_df에 엘보우 메소드를 사용하여 최적의 k값을 찾아보기
inertia_list = []
for i in range(2, 11):
km = KMeans(n_clusters=i, random_state=2024)
km.fit(ss_df)
inertia_list.append(km.inertia_)
print(inertia_list)
[42898.75475762481, 39825.85332911935, 37620.72546383063,
36296.39316484985, 34242.24326674706, 32502.627684050876,
30781.8055617468, 29990.604680739903, 28248.19018930947]
sns.lineplot(x=range(2,11), y=inertia_list)
4. 실루엣 스코어(Silhouette Score)
실루엣 분석(Silhouette Analysis)은 클러스터링 결과의 품질을 평가하기 위한 방법 중 하나로, 개별 데이터 포인트가 해당 군집 내에서 얼마나 잘 뭉쳐 있고, 다른 군집과는 얼마나 분리되어 있는지를 측정하는 지표인 실루엣 계수(Silhouette Coefficient)를 사용합니다. 실루엣 계수(스코어)는 -1에서 1 사이의 값을 가지며, 높을수록 클러스터링의 품질이 좋다고 해석됩니다. 이는 클러스터의 개수(K)를 결정하는 데 활용할 수 있습니. K값에 대해 실루엣 스코어를 계산하고, 높은 스코어를 갖는 K를 선택할 수 있습니다.
from sklearn.metrics import silhouette_score
score = []
for i in range(2,11):
km = KMeans(n_clusters=i, random_state=2024)
km.fit(ss_df)
pred = km.predict(ss_df)
score.append(silhouette_score(ss_df,pred))
score
[0.23022720420084983,
0.1422826246539863,
0.11964043510714399,
0.1281132041299626,
0.12161756074874215,
0.1252612275778885,
0.14543070406681055,
0.13864397879387974,
0.14911484181076592]
sns.lineplot(x=range(2,11), y=score)
km = KMeans(n_clusters=8, random_state=2024)
km.fit(ss_df)
pred=km.predict(ss_df)
pred // array([5, 3, 2, ..., 5, 2, 0], dtype=int32)
mkt_df['label'] = pred
mkt_df['label'].value_counts()
5 518
4 449
2 409
0 265
3 260
1 239
6 54
7 21
Name: label, dtype: int64