▼오늘 배운 사항들
1. 군집 분석 - 결과 시각화
6) 결과 시각화 : pca
7) 클러스터 프로파일링 및 해석
2. 이커머스 고객 군집 분석
3. 시계열 데이터 분석
Part 1. 군집 분석 - 결과 시각화
6. 결과 시각화
- PCA : 핵심 패턴을 뽑아서 시각화 = 수많은 정보를 압축한 것
- n_componets= 차원을 지정해주기
# 6-1. PCA 2D 산점도
k=3
pca = PCA(n_components=2, random_state=42) # n_components=2 : 몇 차원으로 만들거야?
X_2d = pca.fit_transform(X_scaled) # X_scaled 값 넣어서 만들거야
colors = ['#E05C5C', '#4A90D9', '#52A97A'] # 클러스터 0, 1, 2 색상 : 클러스터 수에 맞게 색상 수도 수정
plt.figure(figsize=(9, 6))
for i in range(k):
mask = df['클러스터'] == i
plt.scatter(
X_2d[mask, 0], X_2d[mask, 1],
c=colors[i], label=f'클러스터 {i}',
alpha=0.8, s=100, edgecolors='white', linewidths=0.5 )
plt.title('리드 군집 분석 결과 (PCA 2D)', fontsize=14, fontweight='bold')
plt.xlabel('주성분 1 (가장 많은 정보를 담은 축)', fontsize=11)
plt.ylabel('주성분 2 (두 번째로 많은 정보를 담은 축)', fontsize=11)
plt.legend(fontsize=11)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
7. 클러스터 프로파일링 및 해석
- 숫자를 마케팅 전략으로 바꾸는 단계
7-1) 수치형 지표 클러스터별 평균
- 클러스터별 평균 행동 지표 df.groupby
- 여러 컬럼을 한 번에 볼 것이니 리스트[] 사용해서 묶어주기
- 클러스터 0 : 핫 리드 (적극적이고 전환의지도 좋음)
- 클러스터 1 : 콜드 리드 (적극적이지 않은 저관여 집단으로 예산 없애도 될 정도)
- 클러스터 2 : 워밍 리드( 관심은 있는데 행동은 미비한 고민하는 그룹 )
- 체류시간이 길다 = 꼼꼼히 보고 있다 제품에 관심있게 보고 있는 것
- 스크롤 깊이가 깊다 = 관심을 가지고 계속 정보를 습득하는 것 = 진지하게 콘텐츠 소비 중
- CTA 클릭 = 액션을 할만큼 관심이 크다 (행동할 의지가 있다)
- 폼제출 = 액션을 할만큼 관심이 크다 (전환할 의지가 있다)
- 재방문 = 지속적으로 관심이 있다
- 후속메세지 반응 = 영업응답 가능성을 확인할 수 있음
# 7-1. 수치형 지표 클러스터별 평균
print("클러스터별 평균 행동 지표")
profile = df.groupby('클러스터')[['체류시간_초', '스크롤깊이_%','CTA클릭', '폼제출', '재방문', '후속메시지반응']].mean().round(2)
profile.reset_index()

7-2) 카테고리형 특성 클러스터별 최빈값
- for 반복문 사용
- .mode() 사용해서 최빈값 추출
- [0] 사용해서 동률일 때 첫번째 값 출력 지정
▶ mode로 본 이유 : 수치가 아니라 문자형 컬럼이라 평균을 쓸 수 없기 때문에 가장 많이 나오는 값을 구하는 것( 7-1은 mean)
| 클러스터 구분 | 문자형 특징 | 후속 액션 |
| 클러스터 0 | ![]() |
적극적이고 바로 1개월 이내 도입하고 싶어하는 실무담당자 - 즉각적인 도입을 위한 정보 주기 - 경쟁사 대비 장점 어필 - 실제 무료 체험 열어주기 |
| 클러스터 1 | ![]() |
소극적이고 도입시기도 멀었음 - 당장 세일즈를 밀어붙이기 보단 조금 더 가벼운 교육 - 관심 유도 광고 |
| 클러스터 2 | ![]() |
검토를 고민중으로 결정을 할 수 있게 도와주기 - 무료 체험 제안 - 성공사례 제시 - 리텐션 관련 정보 제공 |
# 7-2. 카테고리형 특성 클러스터별 최빈값
print("카테고리형(문자형) 특성 : 클러스터별 가장 많이 나온 값")
k = 3
for cluster_id in range(k):
group = df[df['클러스터'] == cluster_id] # 각 클러스터별로 뽑기
print(f"<클러스터 : {cluster_id} | 총 합 : {len(group)}명>")
print(f"가장 많은 직무 : {group['직무'].mode()[0]}") # 클러스터 0에서 가장 많은 직무 알아보기 | 최빈값 : .mode()
print(f"가장 많은 회사규모 : {group['회사규모'].mode()[0]}") # [0]의 의미 : 여러개일 때 첫번째 값 사용하기 위함
print(f"가장 많은 관심주제 : {group['관심주제'].mode()[0]}")
print(f"가장 많은 도입시기 : {group['도입시기'].mode()[0]}")
print(f"가장 많은 현재상황 : {group['현재상황'].mode()[0]}")
print("-------------------------------------------")

7-3) 클러스터 이름 매핑
- 전단계에서 확인한 클러스터별 특징을 담아 이름을 지정하고 df에 ['클러스터']컬럼과 매핑해서 '리드등급' 컬럼 추가하기
- .map() 사용
# 7-3. 클러스터 이름 매핑
cluster_names = {
0 : "핫 리드 - 빠른 도입 고려, 즉각 팔로업 필요",
1 : "콜드 리드 - 탐색 초기, 장기적인 컨택 필요",
2 : "워밍 리드 - 검토 중, 신뢰 제공 자공 필요" }
df['리드등급'] = df['클러스터'].map(cluster_names) # 클러스터 값보고 뒤에있는 이름 매핑해줘

8. 저장하기
- df.to_csv("lead_clustering_data.csv", index=False, encoding='utf-8-sig')

9. 클러스터별 유입채널 비율 확인하기
- 클러스터 좋은 유입채널 강화하기 위해 활용 가능
- 클러스터와 유입채널 groupby로 상관관계 보기
- size() : 그룹된 데이터 개수 확인
- unstack() : long 데이터 → wide 데이터로 변환
# 9-1. 클러스터와 유입채널 groupby로 묶어서 상관관계 확인하기
channel_check = df.groupby(['클러스터', '유입채널']).size().unstack(fill_value=0)
# size() : 두 개 조합 몇 개나오는지
# unstack() : long -> wide로
# 9-2. 시각화
channel_check.plot(kind='bar', figsize=(11,5))
plt.xticks(rotation=0) # x축 라벨 가로표시
plt.show()



💡클러스터별 유입채널 비율을 통해...
- 검색seo로 고품질 클러스터0이 많이 유입되니 예산 늘리면 좋을 것 같음
- 유료광고는 저품질 리드가 많이 들어오는 걸 보니, 광고 타겟팅 재검토하거나 다른채널로 광고비 옮겨서 예산 분배가 필요해보임
Part 2. 이커머스 고객 군집 분석
- gemini로 열심히 돌렸는데, 다른 팀들 발표 들어보니 깊이가..!


Part 3. 시계열 데이터 분석
[시계열 데이터]
- 시계열 데이터의 3가지 구성 요소 : 트렌드 + 계절성 + 노이즈
- 선형회귀 : 여러 변수를 조합해 매출 예측
- ARIMA : 과거의 매출 흐름만으로 미래를 예측할 때
[ARIMA]
- ARIMA 다음에 파라미터 값 이해하기 위한 AR / I / MA 이해하기
- AR 이전값이 현재값에 미치는 영향 : 이전값 패턴 학습
- I 데이터를 안정적으로 만들기 위한 전처리 과정 : 데이터 전처리
- MA 오차 확인해서 다음 예측에 반영 : 데이터 오차 처리
- X값 필요없음. 본인 데이터 활용하면 되니까.
ARIMA(1, 1, 1) 의미:
p = 1 → 바로 전 달(1개) 값을 참고해서 예측
d = 1 → 데이터를 1번 차분해서 안정적으로 만듦
q = 1 → 바로 전 달의 예측 오차 1개를 반영
[ 시계열 데이터 분석 흐름 ]
1) 데이터 불러오기
- df = pd.DataFrame({ '날짜' : 날짜, '가입자' : 가입자}] : 리스트 데이터를 딕셔너리 만들어서 데이터프레임 만들기
2) 데이터 전처리
- pd.to_datetime : 날짜 object를 날짜유형으로 변경
3) 데이터 분리하기 (학습용 /테스트용)
- iloc[] 사용
- train = df.iloc[:30] # 처음부터 30행까지
- test = df.iloc[30:] # 30행 이후부터 끝까지

4) 데이터 탐색 - 시각화
- figure 사용해서 시계열 흐름 확인하기
- ▶ 전체적으로 우상향, 계절성이 있는 데이터면 ARIMA 안쓰는 게 좋음
5) 모델 학습
- ARIMA 사용
- model=ARIMA(train['가입자'], order=(1,1,1)) : 날짜는 airma가 순서대로인 걸 알고 있으니 알고싶은 가입자수 기준으로 모델링
- 학습된모델 = model.fit() : 모델 학습 진행
6) 모델 예측
- 예측결과 = 학습된모델 .get_forecast(steps=) 사용 : steps=6은 6개월치, 데이터가 일별이면 6일치 예측함
- 예측가입자수 = 예측결과.predicted_mean.values : 예측결과 평균값 (예측결과의 부산물)
- 예측범위 = 예측결과.con_int(alpha=0.05) : 신뢰구간 95%의 예측가입자수의 범위 추출
- 구한 예측범위 안의 예측하한과 예측상한을 분리해서 확인 가능
- 예측하한 = 예측범위.iloc[:,0].values : 모든행의 첫번째 열만 선택
- 예측상한 = 예측범위.iloc[:,1].values : 모든행의 두번째 열만 선택


※ 시계열 데이터 분석 코드
# 1. 데이터 불러오고 담기
df = pd.DataFrame({'날짜' : 날짜, '가입자' : 가입자}) # 위에 데이터가 리스트라서 {}딕셔너리 만들어서 넣어준 것
# 1-1. 숫자데이터인지 확인
df.info()
#2. 날짜 object를 날짜유형으로 변경
df['날짜'] = pd.to_datetime(df['날짜'])
# 3. 데이터 분리하기 학습용/테스트용 ( 30, 6 )
train = df.iloc[:30] # 처음부터 30행까지
test = df.iloc[30:] # 30행 이후부터 끝까지
# 다중회귀분석1에서 학습용/테스트용 데이터 분리한 함수와 비교
# test_size = 테스트 용 20% / random_state = 기준 잡고 랜덤돌리기 -> 42는 기준
# X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2 , random_state=42)
# 3-1. 데이터 분리 확인
# shape으로 행, 열 개수확인
train.shape
test.shape
# 4. 시각화 탐색
plt.figure(figsize = (13,5))
plt.plot(df['날짜'], df['가입자'])
plt.show()
▶ 전체적으로 우상향, 계절성이 있는 데이터면 ARIMA 안쓰는 게 좋음
# 5. 모델 학습하기
model = ARIMA(train['가입자'], order=(1,1,1)) # AIRMA는 날짜가 순서대로 되어있는 걸 알고 있으니까, 알고싶은 가입자를 넣는 것
학습된모델 = model.fit()
# 6. 모델 예측하기 (test 데이터와 지금 만든 모델로 예측한 6개월치 비교하기)
# 6개월치 예측하기
예측결과 = 학습된모델.get_forecast(steps=6) # steps=6 : 6개월치
# 예측값 (예측값 안에서 예측된 평균값 불러오기)
예측가입자수 = 예측결과.predicted_mean.values
▶ 예측가입자수
▶ array([2440.31192679, 2480.62379322, 2520.93559931, 2561.24734504,
2601.55903043, 2641.87065547])
▶ Q. 만약 데이터가 일별로 되어있으면 steps=6하면? 6일치 예측가입자수가 도출됨
# 6-1. 예측범위 생성 가능 - alpha =0.05가 95% 신뢰구간 추출
# = lower 가입자 2367명 ~ upper 가입자 2513명까지 예측된다
예측범위 = 예측결과.conf_int(alpha=0.05)
예측범위
# 6-2. 예측하한선과 예측상한선 따로 구할 수 있음
예측하한 = 예측범위.iloc[:,0].values # 모든행의 첫번째 열만 선택
예측상한 = 예측범위.iloc[:,1].values # 모든행의 두번째 열만 선택
▶ 예측하한
▶ array([2367.47282567, 2375.95338114, 2390.7391395 , 2408.63266618,
2428.42238513, 2449.50036675])
군집 분석 후에 클러스터를 다른 컬럼과 비교하면서 특징 잡아내는 거 재밌다
그 특성에 따라서 이런저런 마케팅 액션을 만들어볼 수도 있고
다음주 화요일까지가 머신러닝끝인데 이거 이거.. 주말동안 소화해낼 수 있을까?



