ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 6. 페이스북 광고 캠페인 전환 분석
    마케터 관점의 데이터분석/마케터의 파이썬 활용법 2025. 7. 22. 00:14

     

    최근에 데이터 분석 과정에서 

    광고 캠페인 전환율 분석 실습을 진행했다. 

     

    실제 마케팅 의사결정에 직결되는 인사이트를, 

    데이터 기반으로 도출해보는 과정이었다. 

     

    나중에 마케팅 실무 때 적용해보면 유용할 것 같아서 

    실습과정을 스텝별로 정리해 뒀다. 


    이건 여담인데,

    GA4, Amplitude 등 다양한 분석툴이 있기 때문에

    가끔 나는 파이썬을 써서 데이터 분석을 해야 하나? 생각이 들긴 했었다.

     

    하지만 갈수록 역시 배우길 잘했다고 생각한다.

     

    GA4나 Meta Ads는 마케팅 전략을 제시하지 않는다.

    단지 인사이트를 도출할 수 있을 뿐, 분석에는 분명한 한계가 있었다.

     

    빠른 지표 확인이나 사용자 행동 추적, 이벤트 분석, 실시간 대시보드나 시각화는 가능하지만 

    사용자 타겟팅을 해석하긴 어렵고, 왜 그랬는지, 어떻게 개선할지는 알 수 없기 때문이다. 

     

    대부분 BI툴들은 '무슨 일이 일어났는가'를 보여주지만

    '왜 일어났는지', '그래서 어떻게 해야하는 지'를 말해주진 않는다.

     

    그동안 나는 마케팅 업무하면서 이런 부족한 부분을 항상 채우길 원했다.

     

    예를들어, 

    광고 퍼포먼스 로그

    CRM과 광고 연결 데이터

    채널별 캠페인 리포트 병합

    어떤 타겟이 전환할 확률이 높은지(전환 예측 기반 추천)

    어떤 카피가 구매 전환을 더 이끌지(A/B 테스트 설계/검증)

    예산을 어디에 더 쓸 때 ROAS가 높아질지(예산 최적화 시뮬레이션)

    클릭은 있는데 전환이 안나는 이유 분석 등등

     

    결국 이런 판단을 하려면 파이썬이 필요하다는 걸 깨달았다.

     

    "CTR은 높은데 왜 전환이 없지?" CVR 낮은 타겟 추출 + 원인 파악
    "광고 성과가 불균형해" 고성과/저성과 광고 자동 분류
    "어떤 타겟이 전환이 잘 돼?" 전환 예측 모델 학습
    "이 카피, 진짜 효과 있나?" A/B 테스트 유의성 검정
    "예산 어디에 더 써야 하나?" 선형 계획법 기반 예산 최적화

     


    [Lab] Facebook 광고 캠페인 분석 실습 

    • 데이터 출처: Facebook 광고 캠페인 로그 
     

    Facebook Ad-Campaigns Analysis /Sales-Prediction

    Explore and run machine learning code with Kaggle Notebooks | Using data from Sales Conversion Optimization

    www.kaggle.com

     

    • 분석 대상: 총 1143개 광고 캠페인
    • 포함변수: 연령대, 성별, 관심사, 클릭수, 전환수, 광고비
    • 핵심 목표: KPI 분석 + 고/저성과 세그먼트 도출 + 전환 예측 모델 구축

    컬럼 설명

    • ad_id: 광고 ID (고유 식별자)
    • xyz_campaign_id: XYZ 캠페인 ID
    • fb_campaign_id: Facebook 캠페인 ID
    • age: 타겟 연령대 (예: 30-34, 35-39)
    • gender: 성별 (M: 남성, F: 여성)
    • interest: 관심사 카테고리 (숫자 코드)
    • Impressions: 노출 수 (광고가 화면에 표시된 횟수)
    • Clicks: 클릭 수 (광고를 클릭한 횟수)
    • Spent: 광고비 지출 (달러)
    • Total_Conversion: 총 전환 수
    • Approved_Conversion: 승인된 전환 수

    Step 1: 라이브러리 임포트 및 데이터 로딩

    # 필요한 라이브러리 임포트
    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    import seaborn as sns
    import warnings
    
    # 머신러닝 라이브러리
    from sklearn.model_selection import train_test_split
    from sklearn.preprocessing import LabelEncoder, StandardScaler
    from sklearn.linear_model import LogisticRegression
    from sklearn.ensemble import RandomForestClassifier
    from sklearn.svm import SVC
    from sklearn.metrics import classification_report, accuracy_score, roc_auc_score, confusion_matrix
    import xgboost as xgb
    
    # 경고 메시지 숨기기
    warnings.filterwarnings('ignore')
    
    # 시각화 설정 FONT 설정
    # plt.rcParams['font.family'] = 'Malgun Gothic'  # Windows용
    # plt.rcParams['font.family'] = 'AppleGothic'  # Mac용
    plt.rcParams['font.family'] = "DejaVuSans"
    import shutil
    import matplotlib.font_manager as fm
    import matplotlib as mpl
    
    # 폰트 입력
    # font_path = '/usr/share/fonts/D2Coding-Ver1.3.2-20180524-all.ttc'
    # cache_dir = mpl.get_cachedir()
    
    # 폰트 프로퍼티 생성
    # font_prop = fm.FontProperties(fname=font_path)
    # mpl.rcParams['font.family'] = font_prop.get_name()
    plt.rc ("font", family="DejaVuSans")
    
    
    plt.rcParams['axes.unicode_minus'] = False    # 마이너스 기호 정상 출력
    
    print("라이브러리 임포트 완료!")
    # 데이터 파일을 로딩
    df = pd.read_csv('KAG_conversion_data.csv')
    
    # 데이터의 기본 정보를 확인
    print("데이터 크기:", df.shape)
    print("\n컬럼명:")
    print(df.columns.tolist())
    
    print("\n데이터 타입:")
    print(df.dtypes)
    
    print("\n데이터프레임 정보:")
    print(df.info())

    # 데이터의 처음 5행 확인
    print("데이터 샘플:")
    print(df.head())
    
    print("\n마지막 5행:")
    print(df.tail())

    # 기본 통계 정보를 확인
    print("수치형 변수 기본 통계:")
    print(df.describe())
    
    print("\n범주형 변수 기본 정보:")
    print(df.describe(include=['object']))

    # 결측값 확인
    print("결측값 개수:")
    missing_values = df.isnull().sum()
    print(missing_values[missing_values > 0])
    
    if missing_values.sum() == 0:
        print("결측값이 없습니다!")
    else:
        print(f"\n총 결측값 개수: {missing_values.sum()}")
    
    # 중복값 확인
    print(f"\n중복된 행의 개수: {df.duplicated().sum()}")

    Step 2 : 핵심 성과 지표(KPI) 계산 

    # 핵심 성과 지표들을 계산
    
    # CTR (Click Through Rate) 계산
    # 0으로 나누는 경우를 방지하기 위해 np.where 사용
    df['CTR'] = np.where(df['Impressions'] > 0,
                         (df['Clicks'] / df['Impressions']) * 100,
                         0)
    
    # CVR (Conversion Rate) 계산
    # 클릭이 0인 경우를 처리
    df['CVR'] = np.where(df['Clicks'] > 0,
                         (df['Approved_Conversion'] / df['Clicks']) * 100,
                         0)
    
    # CPC (Cost Per Click) 계산
    df['CPC'] = np.where(df['Clicks'] > 0,
                         df['Spent'] / df['Clicks'],
                         0)
    
    # CPA (Cost Per Acquisition) 계산
    # 전환이 0인 경우를 처리 (무한대가 되지 않도록)
    df['CPA'] = np.where(df['Approved_Conversion'] > 0,
                         df['Spent'] / df['Approved_Conversion'],
                         np.inf)
    
    # 무한대 값을 NaN으로 변경 (분석 시 제외하기 위해)
    df['CPA'] = df['CPA'].replace(np.inf, np.nan)
    
    # 계산된 지표들의 기본 통계를 확인
    kpi_columns = ['CTR', 'CVR', 'CPC', 'CPA']
    print("핵심 성과 지표 통계:")
    print(df[kpi_columns].describe())
    
    print("\n각 지표별 0인 값의 개수:")
    for col in kpi_columns:
        zero_count = (df[col] == 0).sum()
        null_count = df[col].isnull().sum()
        print(f"{col}: 0인 값 {zero_count}개, 결측값 {null_count}개")


    Step 3 : 탐색적 데이터 분석(EDA)

    # 범주형 변수들의 분포를 확인
    
    # 연령대별 분포
    print("연령대별 광고 개수:")
    age_dist = df['age'].value_counts().sort_index()
    print(age_dist)
    print(f"연령대별 비율:\n{(age_dist / len(df) * 100).round(2)}%")
    
    print("\n성별 분포:")
    gender_dist = df['gender'].value_counts()
    print(gender_dist)
    print(f"성별 비율:\n{(gender_dist / len(df) * 100).round(2)}%")
    
    print("\n관심사 카테고리 분포 (상위 10개):")
    interest_dist = df['interest'].value_counts().head(10)
    print(interest_dist)
    print(f"\n총 관심사 카테고리 수: {df['interest'].nunique()}개")

     

    # 연령대별 성과 지표 비교
    age_performance = df.groupby('age')[['Impressions', 'Clicks', 'Spent', 'Total_Conversion',
                                         'Approved_Conversion', 'CTR', 'CVR', 'CPC', 'CPA']].mean()
    
    print("연령대별 평균 성과 지표:")
    print(age_performance.round(2))
    
    # 어떤 연령대에서 성과가 가장 좋은지 분석
    print("\n=== 연령대별 성과 분석 ===")
    ctr_best = age_performance['CTR'].idxmax()
    cvr_best = age_performance['CVR'].idxmax()
    cpa_best = age_performance['CPA'].idxmin()  # CPA는 낮을수록 좋음
    
    print(f"CTR이 가장 높은 연령대: {ctr_best} ({age_performance.loc[ctr_best, 'CTR']:.2f}%)")
    print(f"CVR이 가장 높은 연령대: {cvr_best} ({age_performance.loc[cvr_best, 'CVR']:.2f}%)")
    print(f"CPA가 가장 낮은 연령대: {cpa_best} (${age_performance.loc[cpa_best, 'CPA']:.2f})")
    
    # 추가 인사이트
    print("\n=== 추가 인사이트 ===")
    print("연령대별 광고 개수와 성과:")
    age_counts = df['age'].value_counts().sort_index()
    for age in age_counts.index:
        count = age_counts[age]
        ctr = age_performance.loc[age, 'CTR']
        cvr = age_performance.loc[age, 'CVR']
        print(f"{age}: {count}개 광고, CTR {ctr:.2f}%, CVR {cvr:.2f}%")
    
    
    #  CPA 가 낮다.
    # - 지출 비용/전환수 : 같은 비용으로 더 많은 전환을 획득할수 있다.
    # - 더 효율적인 광고 이다.
    # - ROI 가 높다

    # 성별 성과 지표 비교
    gender_performance = df.groupby('gender')[['Impressions', 'Clicks', 'Spent', 'Total_Conversion',
                                               'Approved_Conversion', 'CTR', 'CVR', 'CPC', 'CPA']].mean()
    
    print("성별 평균 성과 지표:")
    print(gender_performance.round(2))
    
    # 성별로 어떤 차이가 있는지 분석
    print("\n=== 성별 성과 분석 ===")
    print("주요 차이점:")
    
    # CTR 비교
    male_ctr = gender_performance.loc['M', 'CTR']
    female_ctr = gender_performance.loc['F', 'CTR']
    print(f"• CTR: 남성 {male_ctr:.2f}% vs 여성 {female_ctr:.2f}%")
    
    # CVR 비교
    male_cvr = gender_performance.loc['M', 'CVR']
    female_cvr = gender_performance.loc['F', 'CVR']
    print(f"• CVR: 남성 {male_cvr:.2f}% vs 여성 {female_cvr:.2f}%")
    
    # CPA 비교
    male_cpa = gender_performance.loc['M', 'CPA']
    female_cpa = gender_performance.loc['F', 'CPA']
    print(f"• CPA: 남성 ${male_cpa:.2f} vs 여성 ${female_cpa:.2f}")
    
    # 성별 광고 개수
    gender_counts = df['gender'].value_counts()
    print(f"\n광고 개수: 남성 {gender_counts['M']}개, 여성 {gender_counts['F']}개")
    
    # 성별 총 지출 및 전환
    male_data = df[df['gender'] == 'M']
    female_data = df[df['gender'] == 'F']
    print(f"총 지출: 남성 ${male_data['Spent'].sum():.2f}, 여성 ${female_data['Spent'].sum():.2f}")
    print(f"총 전환: 남성 {male_data['Approved_Conversion'].sum()}개, 여성 {female_data['Approved_Conversion'].sum()}개")


    Step 4 : 데이터 시각화

    # 주요 성과 지표들의 분포를 히스토그램으로 시각화
    fig, axes = plt.subplots(2, 2, figsize=(15, 10))
    fig.suptitle('주요 성과 지표 분포', fontsize=16)
    
    # CTR 분포
    axes[0,0].hist(df['CTR'].dropna(), bins=50, alpha=0.7, color='skyblue', edgecolor='black')
    axes[0,0].set_title('CTR 분포')
    axes[0,0].set_xlabel('CTR (%)')
    axes[0,0].set_ylabel('빈도')
    axes[0,0].axvline(df['CTR'].mean(), color='red', linestyle='--',
                      label=f'평균: {df["CTR"].mean():.2f}%')
    axes[0,0].legend()
    
    # CVR 분포
    axes[0,1].hist(df['CVR'].dropna(), bins=50, alpha=0.7, color='lightgreen', edgecolor='black')
    axes[0,1].set_title('CVR 분포')
    axes[0,1].set_xlabel('CVR (%)')
    axes[0,1].set_ylabel('빈도')
    axes[0,1].axvline(df['CVR'].mean(), color='red', linestyle='--',
                      label=f'평균: {df["CVR"].mean():.2f}%')
    axes[0,1].legend()
    
    # CPC 분포
    axes[1,0].hist(df['CPC'].dropna(), bins=50, alpha=0.7, color='orange', edgecolor='black')
    axes[1,0].set_title('CPC 분포')
    axes[1,0].set_xlabel('CPC ($)')
    axes[1,0].set_ylabel('빈도')
    axes[1,0].axvline(df['CPC'].mean(), color='red', linestyle='--',
                      label=f'평균: ${df["CPC"].mean():.2f}')
    axes[1,0].legend()
    
    # CPA 분포 (무한대 값 제외)
    cpa_clean = df['CPA'].replace([np.inf, -np.inf], np.nan).dropna()
    axes[1,1].hist(cpa_clean, bins=50, alpha=0.7, color='coral', edgecolor='black')
    axes[1,1].set_title('CPA 분포')
    axes[1,1].set_xlabel('CPA ($)')
    axes[1,1].set_ylabel('빈도')
    axes[1,1].axvline(cpa_clean.mean(), color='red', linestyle='--',
                      label=f'평균: ${cpa_clean.mean():.2f}')
    axes[1,1].legend()
    
    plt.tight_layout()
    plt.show()
    
    # 각 지표의 주요 통계
    print("주요 성과 지표 요약:")
    print(f"CTR: 평균 {df['CTR'].mean():.2f}%, 중간값 {df['CTR'].median():.2f}%")
    print(f"CVR: 평균 {df['CVR'].mean():.2f}%, 중간값 {df['CVR'].median():.2f}%")
    print(f"CPC: 평균 ${df['CPC'].mean():.2f}, 중간값 ${df['CPC'].median():.2f}")
    print(f"CPA: 평균 ${cpa_clean.mean():.2f}, 중간값 ${cpa_clean.median():.2f}")

    주요 성과 지표 요약:

    CTR: 평균 0.02%, 중간값 0.02%

    CVR: 평균 8.93%, 중간값 0.00%

    CPC: 평균 $1.23, 중간값 $1.45

    CPA: 평균 $40.55, 중간값 $22.85

    # 연령대별 성과 비교 막대 그래프
    fig, axes = plt.subplots(2, 2, figsize=(15, 10))
    fig.suptitle('연령대별 성과 지표 비교', fontsize=16)
    
    # CTR
    age_performance['CTR'].plot(kind='bar', ax=axes[0,0], title='연령대별 평균 CTR',
                               color='skyblue', rot=45)
    axes[0,0].set_ylabel('CTR (%)')
    axes[0,0].grid(True, alpha=0.3)
    
    # CVR
    age_performance['CVR'].plot(kind='bar', ax=axes[0,1], title='연령대별 평균 CVR',
                               color='lightgreen', rot=45)
    axes[0,1].set_ylabel('CVR (%)')
    axes[0,1].grid(True, alpha=0.3)
    
    # CPC
    age_performance['CPC'].plot(kind='bar', ax=axes[1,0], title='연령대별 평균 CPC',
                               color='orange', rot=45)
    axes[1,0].set_ylabel('CPC ($)')
    axes[1,0].grid(True, alpha=0.3)
    
    # CPA
    age_performance['CPA'].plot(kind='bar', ax=axes[1,1], title='연령대별 평균 CPA',
                               color='coral', rot=45)
    axes[1,1].set_ylabel('CPA ($)')
    axes[1,1].grid(True, alpha=0.3)
    
    plt.tight_layout()
    plt.show()
    
    # 연령대별 성과 순위
    print("=== 연령대별 성과 순위 ===")
    print("CTR 순위 (높은 순):")
    ctr_ranking = age_performance['CTR'].sort_values(ascending=False)
    for i, (age, value) in enumerate(ctr_ranking.items(), 1):
        print(f"{i}. {age}: {value:.2f}%")
    
    print("\nCVR 순위 (높은 순):")
    cvr_ranking = age_performance['CVR'].sort_values(ascending=False)
    for i, (age, value) in enumerate(cvr_ranking.items(), 1):
        print(f"{i}. {age}: {value:.2f}%")

    === 연령대별 성과 순위 ===

    CTR 순위 (높은 순):

    1. 45-49: 0.02%

    2. 40-44: 0.02%

    3. 35-39: 0.02%

    4. 30-34: 0.01%

     

    CVR 순위 (높은 순):

    1. 30-34: 11.13%

    2. 35-39: 10.39%

    3. 40-44: 6.59%

    4. 45-49: 5.80%

     

    # 성별 성과 비교 막대 그래프
    fig, axes = plt.subplots(1, 4, figsize=(20, 5))
    fig.suptitle('성별 성과 지표 비교', fontsize=16)
    
    # CTR 비교
    gender_performance['CTR'].plot(kind='bar', ax=axes[0], title='성별 평균 CTR',
                                  color=['lightblue', 'pink'])
    axes[0].set_ylabel('CTR (%)')
    axes[0].set_xlabel('성별')
    axes[0].grid(True, alpha=0.3)
    
    # CVR 비교
    gender_performance['CVR'].plot(kind='bar', ax=axes[1], title='성별 평균 CVR',
                                  color=['lightblue', 'pink'])
    axes[1].set_ylabel('CVR (%)')
    axes[1].set_xlabel('성별')
    axes[1].grid(True, alpha=0.3)
    
    # CPC 비교
    gender_performance['CPC'].plot(kind='bar', ax=axes[2], title='성별 평균 CPC',
                                  color=['lightblue', 'pink'])
    axes[2].set_ylabel('CPC ($)')
    axes[2].set_xlabel('성별')
    axes[2].grid(True, alpha=0.3)
    
    # CPA 비교
    gender_performance['CPA'].plot(kind='bar', ax=axes[3], title='성별 평균 CPA',
                                  color=['lightblue', 'pink'])
    axes[3].set_ylabel('CPA ($)')
    axes[3].set_xlabel('성별')
    axes[3].grid(True, alpha=0.3)
    
    plt.tight_layout()
    plt.show()
    
    # 성별 간 차이 분석
    print("=== 성별 간 성과 차이 분석 ===")
    for metric in ['CTR', 'CVR', 'CPC', 'CPA']:
        male_value = gender_performance.loc['M', metric]
        female_value = gender_performance.loc['F', metric]
    
        if metric in ['CTR', 'CVR']:
            diff = female_value - male_value
            better = 'F' if diff > 0 else 'M'
            print(f"{metric}: 남성 {male_value:.2f}%, 여성 {female_value:.2f}% (차이: {abs(diff):.2f}%p, {better}가 우수)")
        else:
            diff = male_value - female_value
            better = 'M' if diff > 0 else 'F'
            print(f"{metric}: 남성 ${male_value:.2f}, 여성 ${female_value:.2f} (차이: ${abs(diff):.2f}, {better}가 우수)")

    === 성별 간 성과 차이 분석 ===

    CTR: 남성 0.01%, 여성 0.02% (차이: 0.01%p, F가 우수)

    CVR: 남성 9.85%, 여성 7.94% (차이: 1.92%p, M가 우수)

    CPC: 남성 $1.25, 여성 $1.20 (차이: $0.05, M가 우수)

    CPA: 남성 $32.64, 여성 $49.75 (차이: $17.11, F가 우수)

     

    # 상관관계 히트맵 생성
    # 수치형 컬럼들 선택
    numeric_columns = ['Impressions', 'Clicks', 'Spent', 'Total_Conversion', 'Approved_Conversion', 'CTR', 'CVR', 'CPC', 'CPA']
    
    # 상관관계 계산 (CPA의 무한대 값 제외)
    df_corr = df[numeric_columns].copy()
    df_corr['CPA'] = df_corr['CPA'].replace([np.inf, -np.inf], np.nan)
    correlation_matrix = df_corr.corr()
    
    # 히트맵 그리기
    plt.figure(figsize=(12, 8))
    mask = np.triu(np.ones_like(correlation_matrix, dtype=bool))  # 상삼각 마스크
    sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', center=0,
                mask=mask, square=True, fmt='.2f', cbar_kws={'shrink': 0.8})
    plt.title('변수 간 상관관계 히트맵')
    plt.tight_layout()
    plt.show()
    
    # 강한 상관관계 분석
    print("=== 주요 상관관계 분석 ===")
    # 상관계수 절댓값 기준으로 정렬
    corr_pairs = []
    for i in range(len(correlation_matrix.columns)):
        for j in range(i+1, len(correlation_matrix.columns)):
            var1 = correlation_matrix.columns[i]
            var2 = correlation_matrix.columns[j]
            corr_value = correlation_matrix.iloc[i, j]
            if not np.isnan(corr_value):
                corr_pairs.append((var1, var2, corr_value))
    
    # 상관계수 절댓값 기준으로 정렬
    corr_pairs.sort(key=lambda x: abs(x[2]), reverse=True)
    
    print("강한 상관관계 (상위 10개):")
    for i, (var1, var2, corr) in enumerate(corr_pairs[:10]):
        direction = "양의" if corr > 0 else "음의"
        strength = "매우 강한" if abs(corr) > 0.7 else "강한" if abs(corr) > 0.5 else "중간"
        print(f"{i+1}. {var1} - {var2}: {corr:.3f} ({direction} {strength} 상관관계)")
    
    # 비즈니스 인사이트
    print("\n=== 비즈니스 인사이트 ===")
    print("• Impressions와 Clicks 간 강한 양의 상관관계: 노출이 많을수록 클릭도 많음")
    print("• Spent와 Clicks 간 양의 상관관계: 지출이 많을수록 클릭이 많음")
    print("• CVR과 다른 지표들과의 관계를 통해 전환 최적화 전략 수립 가능")

    === 주요 상관관계 분석 ===

    강한 상관관계 (상위 10개):

    1. Clicks - Spent: 0.993 (양의 매우 강한 상관관계)

    2. Impressions - Spent: 0.970 (양의 매우 강한 상관관계)

    3. Impressions - Clicks: 0.949 (양의 매우 강한 상관관계)

    4. Total_Conversion - Approved_Conversion: 0.864 (양의 매우 강한 상관관계)

    5. Impressions - Total_Conversion: 0.813 (양의 매우 강한 상관관계)

    6. Spent - Total_Conversion: 0.725 (양의 매우 강한 상관관계)

    7. Clicks - Total_Conversion: 0.695 (양의 강한 상관관계)

    8. Impressions - Approved_Conversion: 0.684 (양의 강한 상관관계)

    9. Clicks - CPA: 0.684 (양의 강한 상관관계)

    10. Spent - CPA: 0.672 (양의 강한 상관관계)

     

    === 비즈니스 인사이트 ===

    • Impressions와 Clicks 간 강한 양의 상관관계: 노출이 많을수록 클릭도 많음

    • Spent와 Clicks 간 양의 상관관계: 지출이 많을수록 클릭이 많음

    • CVR과 다른 지표들과의 관계를 통해 전환 최적화 전략 수립 가능


    Step 5 : 고성과/저성과 광고 분석

    # 고성과 광고 분석 (CVR 상위 10%)
    # CVR > 0인 데이터만 고려하여 상위 10% 기준점 계산
    cvr_positive = df[df['CVR'] > 0]['CVR']
    top_cvr_threshold = cvr_positive.quantile(0.9)
    print(f"CVR 상위 10% 기준: {top_cvr_threshold:.2f}%")
    
    # 고성과 광고 필터링
    high_performance_ads = df[df['CVR'] >= top_cvr_threshold]
    print(f"\n고성과 광고 개수: {len(high_performance_ads)}")
    print(f"전체 광고 대비 비율: {len(high_performance_ads) / len(df) * 100:.1f}%")
    
    # 고성과 광고의 특징 분석
    print("\n=== 고성과 광고의 특징 ===")
    print("연령대 분포:")
    high_age_dist = high_performance_ads['age'].value_counts(normalize=True) * 100
    print(high_age_dist.round(1))
    
    print("\n성별 분포:")
    high_gender_dist = high_performance_ads['gender'].value_counts(normalize=True) * 100
    print(high_gender_dist.round(1))
    
    print("\n주요 관심사 (상위 5개):")
    high_interest_dist = high_performance_ads['interest'].value_counts().head()
    print(high_interest_dist)
    
    print("\n평균 성과 지표:")
    high_performance_stats = high_performance_ads[['CTR', 'CVR', 'CPC', 'CPA']].mean()
    print(high_performance_stats.round(2))
    
    # 전체 평균과 비교
    print("\n=== 전체 평균 대비 고성과 광고 특징 ===")
    overall_stats = df[['CTR', 'CVR', 'CPC', 'CPA']].mean()
    for metric in ['CTR', 'CVR', 'CPC', 'CPA']:
        high_val = high_performance_stats[metric]
        overall_val = overall_stats[metric]
        if metric in ['CTR', 'CVR']:
            improvement = ((high_val - overall_val) / overall_val) * 100
            print(f"{metric}: 고성과 {high_val:.2f}% vs 전체 {overall_val:.2f}% ({improvement:+.1f}% 개선)")
        else:
            if not np.isnan(high_val) and not np.isnan(overall_val):
                improvement = ((overall_val - high_val) / overall_val) * 100
                print(f"{metric}: 고성과 ${high_val:.2f} vs 전체 ${overall_val:.2f} ({improvement:+.1f}% 개선)")

     

    # 저성과 광고 분석 (CVR 하위 10% 또는 CVR = 0)
    # CVR > 0인 경우의 하위 10% 기준점 계산
    cvr_positive = df[df['CVR'] > 0]['CVR']
    bottom_cvr_threshold = cvr_positive.quantile(0.1)
    print(f"CVR 하위 10% 기준: {bottom_cvr_threshold:.2f}%")
    
    # 저성과 광고 필터링 (CVR = 0 또는 하위 10%)
    low_performance_ads = df[(df['CVR'] == 0) | (df['CVR'] <= bottom_cvr_threshold)]
    print(f"\n저성과 광고 개수: {len(low_performance_ads)}")
    print(f"전체 광고 대비 비율: {len(low_performance_ads) / len(df) * 100:.1f}%")
    
    # CVR = 0인 광고와 CVR > 0이지만 낮은 광고 구분
    zero_cvr_ads = df[df['CVR'] == 0]
    low_but_positive_cvr = df[(df['CVR'] > 0) & (df['CVR'] <= bottom_cvr_threshold)]
    
    print(f"CVR = 0인 광고: {len(zero_cvr_ads)}개 ({len(zero_cvr_ads) / len(df) * 100:.1f}%)")
    print(f"CVR > 0이지만 낮은 광고: {len(low_but_positive_cvr)}개 ({len(low_but_positive_cvr) / len(df) * 100:.1f}%)")
    
    # 저성과 광고의 특징 분석
    print("\n=== 저성과 광고의 특징 ===")
    print("연령대 분포:")
    low_age_dist = low_performance_ads['age'].value_counts(normalize=True) * 100
    print(low_age_dist.round(1))
    
    print("\n성별 분포:")
    low_gender_dist = low_performance_ads['gender'].value_counts(normalize=True) * 100
    print(low_gender_dist.round(1))
    
    print("\n주요 관심사 (상위 5개):")
    low_interest_dist = low_performance_ads['interest'].value_counts().head()
    print(low_interest_dist)
    
    print("\n평균 성과 지표:")
    low_performance_stats = low_performance_ads[['CTR', 'CVR', 'CPC', 'CPA']].mean()
    print(low_performance_stats.round(2))
    
    # 저성과의 주요 원인 분석
    print("\n=== 저성과 원인 분석 ===")
    print("클릭은 있지만 전환이 없는 광고:")
    clicks_no_conversion = df[(df['Clicks'] > 0) & (df['Approved_Conversion'] == 0)]
    print(f"개수: {len(clicks_no_conversion)}개 ({len(clicks_no_conversion) / len(df) * 100:.1f}%)")
    print(f"평균 클릭 수: {clicks_no_conversion['Clicks'].mean():.1f}")
    print(f"평균 지출: ${clicks_no_conversion['Spent'].mean():.2f}")
    
    print("\n노출은 있지만 클릭이 없는 광고:")
    impressions_no_clicks = df[(df['Impressions'] > 0) & (df['Clicks'] == 0)]
    print(f"개수: {len(impressions_no_clicks)}개 ({len(impressions_no_clicks) / len(df) * 100:.1f}%)")
    print(f"평균 노출 수: {impressions_no_clicks['Impressions'].mean():.0f}")

    CVR 하위 10% 기준: 1.36%

     

    저성과 광고 개수: 682

    전체 광고 대비 비율: 59.7%

    CVR = 0인 광고: 630개 (55.1%)

    CVR > 0이지만 낮은 광고: 52개 (4.5%)

     

    === 저성과 광고의 특징 ===

    연령대 분포:

    age

    30-34 36.8

    45-49 25.5

    35-39 19.6

    40-44 18.0

    Name: proportion, dtype: float64

     

    성별 분포:

    gender

    F 50.6

    M 49.4

    Name: proportion, dtype: float64

     

    주요 관심사 (상위 5개):

    interest

    16 85

    10 50

    27 43

    29 39

    28 35

    Name: count, dtype: int64

     

    평균 성과 지표:

    CTR 0.01

    CVR 0.07

    CPC 1.02

    CPA 75.15

    dtype: float64

     

    === 저성과 원인 분석 ===

    클릭은 있지만 전환이 없는 광고:

    개수: 423개 (37.0%)

    평균 클릭 수: 23.2

    평균 지출: $34.88

     

    노출은 있지만 클릭이 없는 광고:

    개수: 207개 (18.1%)

    평균 노출 수: 2362

     

    # 관심사별 성과 분석
    # 관심사별 광고 개수가 일정 수준 이상인 것만 분석
    min_ads_per_interest = 10  # 최소 광고 개수
    
    # 관심사별 그룹핑 및 성과 계산
    interest_performance = df.groupby('interest').agg({
        'ad_id': 'count',  # 광고 개수
        'Impressions': 'mean',
        'Clicks': 'mean',
        'Spent': 'mean',
        'Approved_Conversion': 'mean',
        'CTR': 'mean',
        'CVR': 'mean',
        'CPC': 'mean',
        'CPA': 'mean'
    }).round(2)
    
    interest_performance.columns = ['광고개수', '평균노출', '평균클릭', '평균지출', '평균전환', 'CTR', 'CVR', 'CPC', 'CPA']
    
    # 충분한 데이터가 있는 관심사만 필터링
    significant_interests = interest_performance[interest_performance['광고개수'] >= min_ads_per_interest]
    
    print(f"분석 대상 관심사: {len(significant_interests)}개 (광고 {min_ads_per_interest}개 이상)")
    print(f"전체 관심사: {len(interest_performance)}개")
    
    print("\n주요 관심사별 성과 (CVR 기준 상위 10개):")
    top_cvr_interests = significant_interests.sort_values('CVR', ascending=False).head(10)
    print(top_cvr_interests[['광고개수', 'CTR', 'CVR', 'CPC', 'CPA']])
    
    print("\n주요 관심사별 성과 (CTR 기준 상위 10개):")
    top_ctr_interests = significant_interests.sort_values('CTR', ascending=False).head(10)
    print(top_ctr_interests[['광고개수', 'CTR', 'CVR', 'CPC', 'CPA']])
    
    # 관심사별 성과 시각화
    fig, axes = plt.subplots(2, 2, figsize=(15, 10))
    fig.suptitle('주요 관심사별 성과 분석 (상위 10개)', fontsize=14)
    
    # CVR 상위 10개 관심사
    top_cvr_interests['CVR'].plot(kind='bar', ax=axes[0,0], title='CVR 상위 관심사', color='lightgreen')
    axes[0,0].set_ylabel('CVR (%)')
    axes[0,0].tick_params(axis='x', rotation=45)
    
    # CTR 상위 10개 관심사
    top_ctr_interests['CTR'].plot(kind='bar', ax=axes[0,1], title='CTR 상위 관심사', color='skyblue')
    axes[0,1].set_ylabel('CTR (%)')
    axes[0,1].tick_params(axis='x', rotation=45)
    
    # 광고 개수가 많은 관심사 (상위 10개)
    top_volume_interests = significant_interests.sort_values('광고개수', ascending=False).head(10)
    top_volume_interests['광고개수'].plot(kind='bar', ax=axes[1,0], title='광고 개수 상위 관심사', color='orange')
    axes[1,0].set_ylabel('광고 개수')
    axes[1,0].tick_params(axis='x', rotation=45)
    
    # CPA 하위 10개 관심사 (CPA가 낮을수록 좋음)
    cpa_valid = significant_interests[significant_interests['CPA'].notna() & (significant_interests['CPA'] != np.inf)]
    if len(cpa_valid) > 0:
        top_cpa_interests = cpa_valid.sort_values('CPA', ascending=True).head(10)
        top_cpa_interests['CPA'].plot(kind='bar', ax=axes[1,1], title='CPA 우수 관심사', color='coral')
        axes[1,1].set_ylabel('CPA ($)')
        axes[1,1].tick_params(axis='x', rotation=45)
    
    plt.tight_layout()
    plt.show()
    
    # 관심사 분석 인사이트
    print("\n=== 관심사별 분석 인사이트 ===")
    best_cvr_interest = top_cvr_interests.index[0]
    best_cvr_value = top_cvr_interests.loc[best_cvr_interest, 'CVR']
    print(f"• 가장 높은 CVR을 보이는 관심사: {best_cvr_interest} ({best_cvr_value:.2f}%)")
    
    best_ctr_interest = top_ctr_interests.index[0]
    best_ctr_value = top_ctr_interests.loc[best_ctr_interest, 'CTR']
    print(f"• 가장 높은 CTR을 보이는 관심사: {best_ctr_interest} ({best_ctr_value:.2f}%)")
    
    most_volume_interest = top_volume_interests.index[0]
    most_volume_count = top_volume_interests.loc[most_volume_interest, '광고개수']
    print(f"• 가장 많은 광고가 집행된 관심사: {most_volume_interest} ({most_volume_count}개)")

    === 관심사별 분석 인사이트 ===

    • 가장 높은 CVR을 보이는 관심사: 31 (15.74%)

    • 가장 높은 CTR을 보이는 관심사: 2 (0.02%)

    • 가장 많은 광고가 집행된 관심사: 16 (140개)


    Step 6 : 머신러닝 모델링

    # 특성 엔지니어링
    # 예측할 타겟 변수 생성: 전환 여부 (Approved_Conversion > 0이면 1, 아니면 0)
    df['has_conversion'] = (df['Approved_Conversion'] > 0).astype(int)
    
    # 추가 특성 생성
    # 클릭 효율성 (노출 대비 클릭 비율)
    df['click_efficiency'] = np.where(df['Impressions'] > 0,
                                      df['Clicks'] / df['Impressions'],
                                      0)
    
    # 비용 효율성 (클릭 대비 지출)
    df['cost_efficiency'] = np.where(df['Clicks'] > 0,
                                     df['Spent'] / df['Clicks'],
                                     0)
    
    # 범주형 변수 인코딩
    # 복사본 생성
    df_ml = df.copy()
    
    # 연령대 인코딩
    age_encoder = LabelEncoder()
    df_ml['age_encoded'] = age_encoder.fit_transform(df_ml['age'])
    print("연령대 인코딩 매핑:")
    for i, age in enumerate(age_encoder.classes_):
        print(f"  {age} -> {i}")
    
    # 성별 인코딩
    gender_encoder = LabelEncoder()
    df_ml['gender_encoded'] = gender_encoder.fit_transform(df_ml['gender'])
    print("\n성별 인코딩 매핑:")
    for i, gender in enumerate(gender_encoder.classes_):
        print(f"  {gender} -> {i}")
    
    print("\n특성 엔지니어링 완료")
    print(f"타겟 변수 분포:")
    target_dist = df_ml['has_conversion'].value_counts()
    print(f"  전환 없음 (0): {target_dist[0]}개 ({target_dist[0]/len(df_ml)*100:.1f}%)")
    print(f"  전환 있음 (1): {target_dist[1]}개 ({target_dist[1]/len(df_ml)*100:.1f}%)")
    
    # 새로운 특성들의 분포 확인
    print(f"\n새로운 특성 통계:")
    print(f"클릭 효율성 평균: {df_ml['click_efficiency'].mean():.4f}")
    print(f"비용 효율성 평균: {df_ml['cost_efficiency'].mean():.2f}")

     

    연령대 인코딩 매핑:

    30-34 -> 0

    35-39 -> 1

    40-44 -> 2

    45-49 -> 3

     

    성별 인코딩 매핑:

    F -> 0

    M -> 1

     

    특성 엔지니어링 완료

    타겟 변수 분포:

    전환 없음 (0): 559개 (48.9%)

    전환 있음 (1): 584개 (51.1%)

     

    새로운 특성 통계:

    클릭 효율성 평균: 0.0002

    비용 효율성 평균: 1.23

    # 모델링을 위한 특성 선택
    # 예측에 사용할 특성들을 선택 (ID 컬럼, 타겟 변수 등은 제외)
    feature_columns = [
        'age_encoded', 'gender_encoded', 'interest',
        'Impressions', 'Clicks', 'Spent',
        'CTR', 'CPC', 'click_efficiency', 'cost_efficiency'
    ]
    
    print("선택된 특성들:")
    for i, col in enumerate(feature_columns, 1):
        print(f"{i}. {col}")
    
    # 결측값이 있는 행 제거 또는 처리
    print(f"\n원본 데이터 크기: {df_ml.shape}")
    
    # 무한대 값을 가진 컬럼 처리 (CPC에 무한대가 있을 수 있음)
    for col in feature_columns:
        if col in df_ml.columns:
            # 무한대 값을 NaN으로 변경
            df_ml[col] = df_ml[col].replace([np.inf, -np.inf], np.nan)
    
    # 결측값 확인
    missing_before = df_ml[feature_columns + ['has_conversion']].isnull().sum()
    print(f"\n결측값 확인:")
    for col, missing in missing_before.items():
        if missing > 0:
            print(f"  {col}: {missing}개")
    
    # 결측값이 있는 행 제거
    df_ml_clean = df_ml[feature_columns + ['has_conversion']].dropna()
    print(f"\n정제 후 데이터 크기: {df_ml_clean.shape}")
    print(f"제거된 행 수: {len(df_ml) - len(df_ml_clean)}")
    
    # 특성과 타겟 분리
    X = df_ml_clean[feature_columns]
    y = df_ml_clean['has_conversion']
    
    print(f"\n최종 데이터 크기: {X.shape}")
    print(f"특성 컬럼: {list(X.columns)}")
    print(f"타겟 분포: {y.value_counts().to_dict()}")
    
    # 각 특성의 기본 통계
    print(f"\n특성별 기본 통계:")
    print(X.describe().round(2))

    # 데이터 분할 및 스케일링
    # 훈련/테스트 데이터 분할
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.2, random_state=42, stratify=y
    )
    
    print(f"훈련 데이터 크기: {X_train.shape}")
    print(f"테스트 데이터 크기: {X_test.shape}")
    
    # 타겟 분포 확인
    train_target_dist = pd.Series(y_train).value_counts()
    test_target_dist = pd.Series(y_test).value_counts()
    
    print(f"\n훈련 데이터 타겟 분포:")
    print(f"  전환 없음 (0): {train_target_dist[0]}개 ({train_target_dist[0]/len(y_train)*100:.1f}%)")
    print(f"  전환 있음 (1): {train_target_dist[1]}개 ({train_target_dist[1]/len(y_train)*100:.1f}%)")
    
    print(f"\n테스트 데이터 타겟 분포:")
    print(f"  전환 없음 (0): {test_target_dist[0]}개 ({test_target_dist[0]/len(y_test)*100:.1f}%)")
    print(f"  전환 있음 (1): {test_target_dist[1]}개 ({test_target_dist[1]/len(y_test)*100:.1f}%)")
    
    # 특성 스케일링
    scaler = StandardScaler()
    X_train_scaled = scaler.fit_transform(X_train)
    X_test_scaled = scaler.transform(X_test)
    
    # DataFrame으로 변환 (컬럼명 유지)
    X_train_scaled = pd.DataFrame(X_train_scaled, columns=X_train.columns, index=X_train.index)
    X_test_scaled = pd.DataFrame(X_test_scaled, columns=X_test.columns, index=X_test.index)
    
    print(f"\n스케일링 완료!")
    print(f"스케일링 전 특성 평균 (훈련 데이터):")
    print(X_train.mean().round(2))
    print(f"\n스케일링 후 특성 평균 (훈련 데이터):")
    print(X_train_scaled.mean().round(2))

    # 여러 머신러닝 모델 학습 및 비교
    # 모델들 정의
    models = {
        'Logistic Regression': LogisticRegression(random_state=42, max_iter=1000),
        'Random Forest': RandomForestClassifier(random_state=42, n_estimators=100),
        'SVM': SVC(random_state=42, probability=True),
        'XGBoost': xgb.XGBClassifier(random_state=42, n_estimators=100, learning_rate=0.1, max_depth=6)
    }
    
    # 모델 학습 및 평가
    results = {}
    
    for name, model in models.items():
        print(f"\n{'='*50}")
        print(f"=== {name} 모델 학습 및 평가 ===")
        print(f"{'='*50}")
    
        # 모델 학습
        model.fit(X_train_scaled, y_train)
    
        # 예측
        y_pred = model.predict(X_test_scaled)
        y_pred_proba = model.predict_proba(X_test_scaled)[:, 1]  # 양성 클래스 확률
    
        # 성능 평가
        accuracy = accuracy_score(y_test, y_pred)
        auc_score = roc_auc_score(y_test, y_pred_proba)
    
        results[name] = {
            'accuracy': accuracy,
            'auc': auc_score,
            'model': model,
            'y_pred': y_pred,
            'y_pred_proba': y_pred_proba
        }
    
        print(f"정확도: {accuracy:.4f}")
        print(f"AUC: {auc_score:.4f}")
    
        # 혼동 행렬
        cm = confusion_matrix(y_test, y_pred)
        print(f"\n혼동 행렬:")
        print(cm)
    
        print(f"\n분류 보고서:")
        print(classification_report(y_test, y_pred))
    
    # 모델 성능 비교 요약
    print(f"\n{'='*60}")
    print(f"=== 모델 성능 비교 요약 ===")
    print(f"{'='*60}")
    
    comparison_df = pd.DataFrame({
        'Model': list(results.keys()),
        'Accuracy': [results[model]['accuracy'] for model in results.keys()],
        'AUC': [results[model]['auc'] for model in results.keys()]
    })
    comparison_df = comparison_df.sort_values('AUC', ascending=False)
    print(comparison_df.round(4))
    
    # 성능 비교 시각화
    fig, axes = plt.subplots(1, 2, figsize=(15, 5))
    
    # 정확도 비교
    comparison_df.plot(x='Model', y='Accuracy', kind='bar', ax=axes[0],
                       title='모델별 정확도 비교', color='skyblue', legend=False)
    axes[0].set_ylabel('Accuracy')
    axes[0].set_ylim(0, 1)
    axes[0].tick_params(axis='x', rotation=45)
    
    # AUC 비교
    comparison_df.plot(x='Model', y='AUC', kind='bar', ax=axes[1],
                       title='모델별 AUC 비교', color='lightgreen', legend=False)
    axes[1].set_ylabel('AUC')
    axes[1].set_ylim(0, 1)
    axes[1].tick_params(axis='x', rotation=45)
    
    plt.tight_layout()
    plt.show()

    012

    # 최고 성능 모델 선택 및 특성 중요도 분석
    # AUC 점수가 가장 높은 모델 선택
    best_model_name = max(results.keys(), key=lambda x: results[x]['auc'])
    best_model = results[best_model_name]['model']
    
    print(f"최고 성능 모델: {best_model_name}")
    print(f"정확도: {results[best_model_name]['accuracy']:.4f}")
    print(f"AUC: {results[best_model_name]['auc']:.4f}")
    
    # 특성 중요도 분석 (Random Forest 또는 XGBoost인 경우)
    if best_model_name in ['Random Forest', 'XGBoost']:
        feature_importance = pd.DataFrame({
            'feature': feature_columns,
            'importance': best_model.feature_importances_
        }).sort_values('importance', ascending=False)
    
        print(f"\n특성 중요도 분석:")
        print(feature_importance.round(4))
    
        # 특성 중요도 시각화
        plt.figure(figsize=(12, 6))
        bars = plt.bar(range(len(feature_importance)), feature_importance['importance'],
                       color='steelblue', alpha=0.7)
        plt.title(f'{best_model_name} 모델의 특성 중요도', fontsize=14, fontweight='bold')
        plt.xlabel('특성', fontsize=12)
        plt.ylabel('중요도', fontsize=12)
        plt.xticks(range(len(feature_importance)), feature_importance['feature'], rotation=45, ha='right')
        plt.grid(True, alpha=0.3)
    
        # 중요도 값을 막대 위에 표시
        for i, bar in enumerate(bars):
            height = bar.get_height()
            plt.text(bar.get_x() + bar.get_width()/2., height + 0.001,
                    f'{height:.3f}', ha='center', va='bottom', fontsize=10)
    
        plt.tight_layout()
        plt.show()
    
        # 상위 5개 중요 특성 분석
        print(f"\n상위 5개 중요 특성:")
        for i, (idx, row) in enumerate(feature_importance.head().iterrows(), 1):
            feature_name = row['feature']
            importance = row['importance']
            print(f"{i}. {feature_name}: {importance:.4f}")
    
            # 각 특성의 비즈니스 의미 설명
            if feature_name == 'CTR':
                print(f"   → 클릭률이 전환 예측에 가장 중요한 요소입니다.")
            elif feature_name == 'CVR':
                print(f"   → 기존 전환율 데이터가 예측에 중요합니다.")
            elif feature_name == 'Spent':
                print(f"   → 광고 지출 규모가 전환에 영향을 미칩니다.")
            elif feature_name == 'Clicks':
                print(f"   → 클릭 수가 전환 가능성을 나타냅니다.")
            elif feature_name == 'interest':
                print(f"   → 관심사 카테고리가 전환에 중요한 역할을 합니다.")
    
    # 다른 모델의 경우 (Logistic Regression 계수 분석)
    elif best_model_name == 'Logistic Regression':
        coefficients = pd.DataFrame({
            'feature': feature_columns,
            'coefficient': best_model.coef_[0]
        }).sort_values('coefficient', key=abs, ascending=False)
    
        print(f"\n로지스틱 회귀 계수 분석:")
        print(coefficients.round(4))
    
        # 계수 시각화
        plt.figure(figsize=(12, 6))
        colors = ['red' if x < 0 else 'blue' for x in coefficients['coefficient']]
        bars = plt.bar(range(len(coefficients)), coefficients['coefficient'], color=colors, alpha=0.7)
        plt.title('로지스틱 회귀 모델의 특성 계수', fontsize=14, fontweight='bold')
        plt.xlabel('특성', fontsize=12)
        plt.ylabel('계수', fontsize=12)
        plt.xticks(range(len(coefficients)), coefficients['feature'], rotation=45, ha='right')
        plt.axhline(y=0, color='black', linestyle='-', linewidth=0.5)
        plt.grid(True, alpha=0.3)
        plt.tight_layout()
        plt.show()
    
    # 모델 성능 해석
    print(f"\n모델 성능 해석:")
    print(f"• 정확도 {results[best_model_name]['accuracy']:.1%}는 전체 예측 중 정확한 비율입니다.")
    print(f"• AUC {results[best_model_name]['auc']:.3f}는 모델의 분류 성능을 나타냅니다 (1.0이 완벽).")
    
    if results[best_model_name]['auc'] >= 0.8:
        print(f"• 우수한 성능의 모델입니다. 실무에서 활용 가능합니다.")
    elif results[best_model_name]['auc'] >= 0.7:
        print(f"• 괜찮은 성능의 모델입니다. 추가 튜닝으로 개선 가능합니다.")
    else:
        print(f"• 성능 개선이 필요합니다. 특성 엔지니어링이나 다른 모델을 고려해보세요.")

    📋 모델 성능 해석:

    • 정확도 64.6%는 전체 예측 중 정확한 비율입니다.

    • AUC 0.675는 모델의 분류 성능을 나타냅니다 (1.0이 완벽).

    • 성능 개선이 필요합니다. 특성 엔지니어링이나 다른 모델을 고려해보세요.


    Step 7 : 결과 해석 및 요약 대시보드

    print("="*60)
    print("        광고 캠페인 분석 결과 요약")
    print("="*60)
    
    # 전체 성과 요약
    print("\n전체 캠페인 성과")
    print("-" * 30)
    total_ads = len(df)
    total_impressions = df['Impressions'].sum()
    total_clicks = df['Clicks'].sum()
    total_spent = df['Spent'].sum()
    total_conversions = df['Approved_Conversion'].sum()
    avg_ctr = df['CTR'].mean()
    avg_cvr = df['CVR'].mean()
    avg_cpc = df['CPC'].mean()
    avg_cpa = df['CPA'].mean()
    
    print(f"총 광고 수: {total_ads:,}개")
    print(f"총 노출 수: {total_impressions:,}회")
    print(f"총 클릭 수: {total_clicks:,}회")
    print(f"총 지출: ${total_spent:,.2f}")
    print(f"총 전환 수: {total_conversions:,}개")
    print(f"평균 CTR: {avg_ctr:.2f}%")
    print(f"평균 CVR: {avg_cvr:.2f}%")
    print(f"평균 CPC: ${avg_cpc:.2f}")
    print(f"평균 CPA: ${avg_cpa:.2f}")
    
    # ROI 계산
    assumed_revenue_per_conversion = 50
    total_revenue = total_conversions * assumed_revenue_per_conversion
    roi = ((total_revenue - total_spent) / total_spent) * 100
    
    print(f"추정 ROI: {roi:.1f}% (전환당 ${assumed_revenue_per_conversion} 가정)")
    
    # 최고 성과 세그먼트
    print("\n최고 성과 세그먼트")
    print("-" * 30)
    
    best_age_ctr = age_performance['CTR'].idxmax()
    best_age_cvr = age_performance['CVR'].idxmax()
    print(f"최고 CTR 연령대: {best_age_ctr} ({age_performance.loc[best_age_ctr, 'CTR']:.2f}%)")
    print(f"최고 CVR 연령대: {best_age_cvr} ({age_performance.loc[best_age_cvr, 'CVR']:.2f}%)")
    
    if gender_performance['CTR']['F'] > gender_performance['CTR']['M']:
        best_gender_ctr = 'Female'
    else:
        best_gender_ctr = 'Male'
    
    if gender_performance['CVR']['F'] > gender_performance['CVR']['M']:
        best_gender_cvr = 'Female'
    else:
        best_gender_cvr = 'Male'
    
    print(f"성과 우수 성별 (CTR): {best_gender_ctr}")
    print(f"성과 우수 성별 (CVR): {best_gender_cvr}")
    
    if 'significant_interests' in locals():
        top_interest = significant_interests['CVR'].idxmax()
        top_cvr_value = significant_interests.loc[top_interest, 'CVR']
        print(f"최고 성과 관심사: {top_interest} (CVR: {top_cvr_value:.2f}%)")
    
    # 머신러닝 모델 결과
    print("\n머신러닝 모델 성능")
    print("-" * 30)
    print(f"최고 성능 모델: {best_model_name}")
    print(f"정확도: {results[best_model_name]['accuracy']:.1%}")
    print(f"AUC 점수: {results[best_model_name]['auc']:.3f}")
    
    # 개선 권장사항
    print("\n주요 개선 권장사항")
    print("-" * 30)
    
    cvr_zero_ratio = (df['CVR'] == 0).sum() / len(df) * 100
    low_ctr_ratio = (df['CTR'] < 1.0).sum() / len(df) * 100
    
    print("1. 전환율 최적화")
    print(f"   - CVR이 0인 광고가 {cvr_zero_ratio:.1f}%입니다.")
    print("   - 랜딩 페이지 개선 및 타겟팅 정확도 향상이 필요합니다.")
    
    print("2. 클릭률 개선")
    print(f"   - CTR이 1% 미만인 광고가 {low_ctr_ratio:.1f}%입니다.")
    print("   - 광고 크리에이티브 및 타겟팅 재검토가 필요합니다.")
    
    print("3. 예산 재배분")
    print(f"   - {best_age_cvr} 연령대와 {best_gender_cvr.lower()} 타겟에 예산을 집중하세요.")
    print("   - 성과 저조 세그먼트의 예산 축소를 고려하세요.")
    
    print("4. 지속적 최적화")
    print("   - A/B 테스트를 통한 지속적인 개선을 추진하세요.")
    print("   - 머신러닝 모델을 활용한 전환 예측 및 타겟팅을 수행하세요.")
    
    # 다음 단계 액션 아이템
    print("\n다음 단계 액션 아이템")
    print("-" * 30)
    print("□ 성과 우수 세그먼트에 예산 20% 증액")
    print("□ CVR 0% 광고 캠페인 일시 정지 및 원인 분석")
    print("□ 상위 5개 관심사 기반 룩어라이크 오디언스 생성")
    print("□ 랜딩 페이지 전환율 개선 작업 수행")
    print("□ 주간 성과 모니터링 및 최적화 프로세스 구축")
    
    print("\n" + "="*60)
    print("        분석 완료")
    print("="*60)

    ============================================================

    광고 캠페인 분석 결과 요약

    ============================================================

     

    전체 캠페인 성과

    ------------------------------

    총 광고 수: 1,143개

    총 노출 수: 213,434,828회

    총 클릭 수: 38,165회

    총 지출: $58,705.23

    총 전환 수: 1,079개

    평균 CTR: 0.02%

    평균 CVR: 8.93%

    평균 CPC: $1.23

    평균 CPA: $40.55

    추정 ROI: -8.1% (전환당 $50 가정)

     

    최고 성과 세그먼트

    ------------------------------

    최고 CTR 연령대: 45-49 (0.02%)

    최고 CVR 연령대: 30-34 (11.13%)

    성과 우수 성별 (CTR): Female

    성과 우수 성별 (CVR): Male

    최고 성과 관심사: 31 (CVR: 15.74%)

     

    머신러닝 모델 성능

    ------------------------------

    최고 성능 모델: Logistic Regression

    정확도: 64.6%

    AUC 점수: 0.675

     

    주요 개선 권장사항

    ------------------------------

    1. 전환율 최적화

    - CVR이 0인 광고가 55.1%입니다.

    - 랜딩 페이지 개선 및 타겟팅 정확도 향상이 필요합니다.

    2. 클릭률 개선

    - CTR이 1% 미만인 광고가 100.0%입니다.

    - 광고 크리에이티브 및 타겟팅 재검토가 필요합니다.

    3. 예산 재배분

    - 30-34 연령대와 male 타겟에 예산을 집중하세요.

    - 성과 저조 세그먼트의 예산 축소를 고려하세요.

    4. 지속적 최적화

    - A/B 테스트를 통한 지속적인 개선을 추진하세요.

    - 머신러닝 모델을 활용한 전환 예측 및 타겟팅을 수행하세요.

     

    다음 단계 액션 아이템

    ------------------------------

    □ 성과 우수 세그먼트에 예산 20% 증액

    □ CVR 0% 광고 캠페인 일시 정지 및 원인 분석

    □ 상위 5개 관심사 기반 룩어라이크 오디언스 생성

    □ 랜딩 페이지 전환율 개선 작업 수행

    □ 주간 성과 모니터링 및 최적화 프로세스 구축

     

    ============================================================

    분석 완료

    ============================================================

     


     

Designed by Tistory.