Growth Marketing/GM4

[TIL-260424] 멋쟁이사자처럼 그로스마케팅 4기 - 데이터분석 개론 day32 | 머신러닝 - 군집 분석 결과 시각화 & 시계열 데이터 분석

pamsyra 2026. 4. 24. 17:07
▼오늘 배운 사항들
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')

csv파일로 저장 완료

 

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()

long → wide

💡클러스터별 유입채널 비율을 통해...
- 검색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행 이후부터 끝까지

iloc 문법 및 사용 예시

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])

 

더보기

군집 분석 후에 클러스터를 다른 컬럼과 비교하면서 특징 잡아내는 거 재밌다

그 특성에 따라서 이런저런 마케팅 액션을 만들어볼 수도 있고

다음주 화요일까지가 머신러닝끝인데 이거 이거.. 주말동안 소화해낼 수 있을까?