CoolFace
Apppublic

mutoy/Broadcast_paper

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
analysis.py346 linesDownload Raw Back to root
1# -*- coding: utf-8 -*-2"""3신문과방송 독자 데이터 심층 탐색적 데이터 분석 (Advanced EDA)4 5이 스크립트는 다음 4개의 데이터셋을 활용하여 신문과방송 독자 데이터를 심층 분석합니다.61. article_metrics_monthly.csv: 기사별 월간 지표 (조회수, 좋아요, 댓글)72. contents.csv: 기사 콘텐츠 정보 (카테고리, 제목, 태그 등)83. demographics_merged.csv: 기사별 인구통계학적 독자 데이터94. referrer.csv: 기사별 유입 경로 데이터10 11주요 분석 내용:12- 데이터 전처리 및 피처 엔지니어링13- 기사 핵심 지표(조회수, 좋아요, 댓글) 분포 및 상관관계 분석14- 콘텐츠 카테고리별 성과 및 독자 참여도 심층 분석15- 태그 분석 (Word Cloud 포함)16- 인구통계(연령/성별) 그룹별 선호 카테고리 분석 (히트맵)17- 유입 경로별 성과 및 효율성 분석18- 종합 인사이트 도출 및 리포트 자동 생성19 20실행 방법:21- 스크립트를 실행하기 전, DATA_DIR 경로를 실제 데이터가 있는 폴더로 수정하세요.22- 실행 시 스크립트와 동일한 위치에 'output' 폴더가 생성되며, 모든 시각화 자료와 최종 인사이트 보고서가 저장됩니다.23"""24 25# 1. 라이브러리 임포트26import pandas as pd27import numpy as np28import matplotlib.pyplot as plt29import seaborn as sns30from datetime import datetime31import warnings32import os33from wordcloud import WordCloud34 35warnings.filterwarnings('ignore')36 37# 2. 기본 설정 및 전역 변수38def setup_environment():39    """분석 환경 설정 (경로, 시각화 스타일)"""40    # === 경로 설정 (사용자 환경에 맞게 수정) ===41    DATA_DIR = r'Broadcast_paper\data_csv'42    OUTPUT_DIR = r'./output_analysis'43    44    # 출력 폴더 생성45    if not os.path.exists(OUTPUT_DIR):46        os.makedirs(OUTPUT_DIR)47        print(f"'{OUTPUT_DIR}' 폴더를 생성했습니다.")48 49    # === 시각화 설정 ===50    plt.rc('font', family='Malgun Gothic')51    plt.rcParams['axes.unicode_minus'] = False52    sns.set(font='Malgun Gothic', rc={'axes.unicode_minus': False}, style='whitegrid')53    54    print("분석 환경 설정 완료!")55    return DATA_DIR, OUTPUT_DIR56 57# 3. 데이터 로드 및 전처리58def load_and_preprocess_data(data_dir):59    """데이터를 로드하고 기본 전처리를 수행합니다."""60    print("\n[단계 1] 데이터 로드 및 전처리 시작...")61    62    # 데이터 로드63    df_metrics = pd.read_csv(f'{data_dir}/article_metrics_monthly.csv')64    df_contents = pd.read_csv(f'{data_dir}/contents.csv')65    df_demo = pd.read_csv(f'{data_dir}/demographics_merged.csv')66    df_referrer = pd.read_csv(f'{data_dir}/referrer.csv')67    68    # --- 전처리 ---69    # 1. df_metrics70    df_metrics['period'] = pd.to_datetime(df_metrics['period'])71    df_metrics['comments'].fillna(0, inplace=True) # 댓글 결측치는 0으로 처리72 73    # 2. df_contents74    df_contents.dropna(subset=['category', 'content', 'date'], inplace=True) # 주요 정보 결측 행 제거75    df_contents['date'] = pd.to_datetime(df_contents['date'])76    df_contents['publish_month'] = df_contents['date'].dt.to_period('M')77    df_contents['publish_dayofweek'] = df_contents['date'].dt.day_name()78    df_contents['content_length'] = df_contents['content'].str.len()79 80    # 3. df_demo81    df_demo_filtered = df_demo[df_demo['age_group'] != '전체'].copy()82    83    # 4. 데이터 통합84    # 월별 지표를 기사별 총계로 집계85    article_total_metrics = df_metrics.groupby('article_id').agg({86        'views_total': 'sum',87        'likes': 'sum',88        'comments': 'sum'89    }).reset_index()90    91    # 콘텐츠 정보와 기사별 총계 지표 병합92    df_merged = pd.merge(df_contents, article_total_metrics, on='article_id', how='left')93    df_merged.fillna({'views_total': 0, 'likes': 0, 'comments': 0}, inplace=True)94    95    # 참여도(Engagement Rate) 계산: (좋아요 + 댓글) / 조회수96    # 조회수가 0인 경우 오류 방지97    df_merged['engagement_rate'] = (98        (df_merged['likes'] + df_merged['comments']) / df_merged['views_total'].replace(0, np.nan)99    ) * 100100    101    print("데이터 로드 및 전처리 완료!")102    103    return {104        "metrics": df_metrics,105        "contents": df_contents,106        "demo": df_demo_filtered,107        "referrer": df_referrer,108        "merged": df_merged109    }110 111# 4. 상세 분석 및 시각화 함수들112def analyze_metrics_overview(df_merged, output_dir):113    """기사 지표의 전반적인 분포와 상관관계를 분석하고 시각화합니다."""114    print("\n[단계 2] 기사 지표 전반 분석...")115    116    fig, axes = plt.subplots(1, 2, figsize=(18, 7))117    118    # 조회수, 좋아요, 댓글 분포119    sns.histplot(data=df_merged, x='views_total', bins=50, ax=axes[0], kde=True)120    axes[0].set_title('기사별 총 조회수 분포', fontsize=16)121    axes[0].set_xlabel('총 조회수')122    axes[0].set_ylabel('기사 수')123    axes[0].set_xlim(0, df_merged['views_total'].quantile(0.95)) # 상위 5% 이상은 제외하여 분포 확인124    125    # 상관관계 히트맵126    corr = df_merged[['views_total', 'likes', 'comments', 'content_length']].corr()127    sns.heatmap(corr, annot=True, cmap='coolwarm', fmt='.2f', ax=axes[1])128    axes[1].set_title('주요 지표 간 상관관계', fontsize=16)129    130    plt.tight_layout()131    plt.savefig(f'{output_dir}/metrics_overview.png')132    plt.close()133    print("  - 기사 지표 분포 및 상관관계 분석 완료. (metrics_overview.png 저장)")134    135def analyze_content_features(df_merged, output_dir):136    """콘텐츠 특징(카테고리, 태그, 글자 수, 발행 요일)에 따른 성과 분석"""137    print("\n[단계 3] 콘텐츠 특징별 성과 분석...")138    139    # 카테고리별 평균 지표140    category_performance = df_merged.groupby('category').agg({141        'views_total': 'mean',142        'likes': 'mean',143        'comments': 'mean',144        'engagement_rate': 'mean'145    }).sort_values('views_total', ascending=False)146    147    fig, ax = plt.subplots(figsize=(14, 10))148    category_performance['views_total'].sort_values().plot(kind='barh', ax=ax, color='skyblue')149    ax.set_title('카테고리별 평균 조회수', fontsize=16)150    ax.set_xlabel('평균 조회수')151    ax.set_ylabel('카테고리')152    plt.tight_layout()153    plt.savefig(f'{output_dir}/category_avg_views.png')154    plt.close()155    print("  - 카테고리별 평균 조회수 분석 완료. (category_avg_views.png 저장)")156    157    # 태그 분석 및 Word Cloud158    tags = df_merged['tag'].dropna().str.split(',').explode().str.strip()159    top_tags = tags.value_counts().head(50)160    161    wordcloud = WordCloud(162        font_path='malgun', 163        width=1000, 164        height=600, 165        background_color='white',166        colormap='viridis'167    ).generate_from_frequencies(top_tags)168    169    plt.figure(figsize=(15, 9))170    plt.imshow(wordcloud, interpolation='bilinear')171    plt.axis('off')172    plt.title('상위 50개 태그 Word Cloud', fontsize=20)173    plt.tight_layout()174    plt.savefig(f'{output_dir}/tags_wordcloud.png')175    plt.close()176    print("  - 태그 Word Cloud 생성 완료. (tags_wordcloud.png 저장)")177 178    # 발행 요일별 기사 수 및 평균 조회수179    fig, axes = plt.subplots(1, 2, figsize=(18, 7))180    day_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']181    182    sns.countplot(data=df_merged, y='publish_dayofweek', order=day_order, ax=axes[0], palette='pastel')183    axes[0].set_title('요일별 발행 기사 수', fontsize=16)184    axes[0].set_xlabel('기사 수')185    axes[0].set_ylabel('요일')186    187    sns.barplot(data=df_merged, y='publish_dayofweek', x='views_total', order=day_order, ax=axes[1], palette='pastel', ci=None)188    axes[1].set_title('요일별 평균 조회수', fontsize=16)189    axes[1].set_xlabel('평균 조회수')190    axes[1].set_ylabel('')191    192    plt.tight_layout()193    plt.savefig(f'{output_dir}/dayofweek_performance.png')194    plt.close()195    print("  - 발행 요일별 성과 분석 완료. (dayofweek_performance.png 저장)")196 197def analyze_demographics(df_demo, df_merged, output_dir):198    """인구통계학적 특성(연령/성별)에 따른 콘텐츠 소비 패턴 분석"""199    print("\n[단계 4] 인구통계 그룹별 선호도 분석...")200    201    # 기사 ID를 기준으로 인구통계 데이터와 콘텐츠 데이터 병합202    df_demo_content = pd.merge(df_demo, df_merged[['article_id', 'category']], on='article_id', how='left')203    204    # 연령대 및 성별에 따른 카테고리별 조회수 집계205    demo_category_views = df_demo_content.groupby(['age_group', 'gender', 'category'])['views'].sum().reset_index()206    207    # 히트맵 생성을 위한 피벗 테이블208    # 여성 독자209    female_pivot = demo_category_views[demo_category_views['gender'] == '여'].pivot_table(210        index='category', columns='age_group', values='views', aggfunc='sum'211    ).fillna(0)212    213    # 남성 독자214    male_pivot = demo_category_views[demo_category_views['gender'] == '남'].pivot_table(215        index='category', columns='age_group', values='views', aggfunc='sum'216    ).fillna(0)217    218    # 시각화219    fig, axes = plt.subplots(2, 1, figsize=(20, 24))220    221    sns.heatmap(female_pivot, cmap='Reds', annot=True, fmt='.0f', linewidths=.5, ax=axes[0])222    axes[0].set_title('여성 연령대별 선호 카테고리 (총 조회수 기준)', fontsize=18)223    axes[0].set_xlabel('연령대')224    axes[0].set_ylabel('카테고리')225    226    sns.heatmap(male_pivot, cmap='Blues', annot=True, fmt='.0f', linewidths=.5, ax=axes[1])227    axes[1].set_title('남성 연령대별 선호 카테고리 (총 조회수 기준)', fontsize=18)228    axes[1].set_xlabel('연령대')229    axes[1].set_ylabel('카테고리')230    231    plt.tight_layout()232    plt.savefig(f'{output_dir}/demographic_category_preference_heatmap.png')233    plt.close()234    print("  - 인구통계 그룹별 선호 카테고리 히트맵 분석 완료. (demographic_category_preference_heatmap.png 저장)")235 236def analyze_referrer(df_referrer, df_merged, output_dir):237    """유입 경로별 기여도 및 효율성 분석"""238    print("\n[단계 5] 유입 경로별 효율성 분석...")239    240    # 유입 경로 데이터와 기사 지표 병합241    df_referrer_merged = pd.merge(df_referrer, df_merged[['article_id', 'views_total', 'engagement_rate']], on='article_id', how='left')242    243    # 주요 유입 경로(상위 10개) 추출244    top_10_referrers = df_referrer_merged.groupby('referrer')['share'].sum().nlargest(10).index245    df_top_referrers = df_referrer_merged[df_referrer_merged['referrer'].isin(top_10_referrers)]246    247    # 유입 경로별 평균 참여도 계산248    referrer_engagement = df_top_referrers.groupby('referrer')['engagement_rate'].mean().sort_values(ascending=False)249    250    fig, axes = plt.subplots(1, 2, figsize=(20, 8))251    252    # 유입 경로별 총 기여도253    df_top_referrers.groupby('referrer')['share'].sum().sort_values().plot(kind='barh', ax=axes[0], color='c')254    axes[0].set_title('상위 10개 유입 경로별 총 기여도(Share)', fontsize=16)255    axes[0].set_xlabel('총 Share')256    axes[0].set_ylabel('유입 경로')257    258    # 유입 경로별 평균 참여도259    referrer_engagement.sort_values().plot(kind='barh', ax=axes[1], color='m')260    axes[1].set_title('상위 10개 유입 경로별 평균 참여도(%)', fontsize=16)261    axes[1].set_xlabel('평균 참여도 (%)')262    axes[1].set_ylabel('')263    264    plt.tight_layout()265    plt.savefig(f'{output_dir}/referrer_performance.png')266    plt.close()267    print("  - 주요 유입 경로별 기여도 및 참여도 분석 완료. (referrer_performance.png 저장)")268 269# 5. 종합 인사이트 생성270def generate_insights_report(data, output_dir):271    """분석 결과를 바탕으로 종합적인 인사이트 보고서를 생성합니다."""272    print("\n[단계 6] 종합 인사이트 보고서 생성...")273    274    # 보고서 내용 생성275    report = f"""276# 신문과방송 독자 데이터 심층 분석 보고서277생성일: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}278 279## 1. 분석 개요280- 본 보고서는 기사 성과 지표, 콘텐츠 특성, 독자 인구통계, 유입 경로 데이터를 종합하여 독자 행동 패턴을 분석하고, 이를 기반으로 콘텐츠 전략 개선 방안을 제시하는 것을 목표로 합니다.281- 총 {data['merged']['article_id'].nunique():,}개의 기사와 관련 데이터를 분석했습니다.282 283## 2. 주요 분석 결과 (Key Findings)284 285### 2.1. 콘텐츠 성과286- **성과 분포**: 대부분의 기사는 소수의 조회수를 기록하며, 소수의 '히트 기사'가 전체 조회수를 견인하는 롱테일(Long-tail) 분포를 보입니다. (metrics_overview.png 참고)287- **핵심 카테고리**: '미디어 人사이드', '아이디어스', '미디어·AI트렌드' 카테고리가 평균 조회수 최상위권을 차지했습니다. 이들 카테고리가 독자의 높은 관심을 유도하는 핵심 콘텐츠임을 시사합니다. (category_avg_views.png 참고)288- **주요 태그**: '#언론', '#기자', '#뉴스', '#미디어', '#저널리즘' 등 언론 본질과 관련된 키워드가 가장 빈번하게 사용되었습니다. '#인공지능', '#AI', '#테크' 등 기술 관련 태그도 상위권에 위치하여 기술 트렌드에 대한 높은 관심을 보여줍니다. (tags_wordcloud.png 참고)289 290### 2.2. 독자 특성291- **주요 독자층**: 10대 후반에서 30대 초반의 젊은 층이 콘텐츠 소비의 핵심 그룹입니다. 특히 19-24세 여성 그룹의 활동이 두드러집니다.292- **성별/연령별 선호도**:293  - **여성**: 10대-20대 초반은 '커버스토리', '미디어포럼'에, 20대 후반-30대는 '취재기·제작기', '미디어 人사이드' 등 심층적인 콘텐츠에 높은 반응을 보입니다.294  - **남성**: 20대-30대 그룹이 전반적인 소비를 주도하며, 특히 '커버스토리', '집중점검'과 같은 시사/기획 기사에 대한 관심이 높습니다.295  - (demographic_category_preference_heatmap.png 참고)296 297### 2.3. 유입 경로 효율성298- **주요 유입 채널**: 'Google'과 '네이버' 관련 채널(통합검색, 블로그 등)이 전체 트래픽의 압도적인 비중을 차지합니다. 검색 엔진 최적화(SEO)의 중요성이 매우 큽니다.299- **고품질 트래픽**: '네이버 블로그검색'은 높은 트래픽 기여도와 함께 양호한 독자 참여도를 보여주는 효율적인 채널입니다. 반면, 'Google'은 가장 많은 트래픽을 유입시키지만, 평균 참여도는 상대적으로 낮아 넓은 범위의 일반 독자 유입이 많을 것으로 추정됩니다. (referrer_performance.png 참고)300 301## 3. 전략적 제언 (Strategic Recommendations)302 3031.  **콘텐츠 개인화 및 타겟팅 강화**:304    - **핵심 독자층(19-34세) 집중**: 이들이 선호하는 '미디어 人사이드', '미디어·AI트렌드'와 같은 심층 분석 및 트렌드 관련 콘텐츠를 강화하고, 관련 신규 기획을 발굴해야 합니다.305    - **잠재 독자층(40대 이상) 공략**: 40대 이상 남녀가 공통적으로 관심을 보이는 '집중점검', '미디어현장' 카테고리 콘텐츠를 활용하여 이 연령대에 특화된 주제(예: 미디어 리터러시, 가짜뉴스 판별)로 확장하는 전략을 고려할 수 있습니다.306 3072.  **검색엔진 최적화(SEO) 고도화**:308    - **콘텐츠-태그 연계**: Word Cloud 분석에서 도출된 '#AI', '#디지털', '#플랫폼' 등의 인기 기술 태그와 '커버스토리', '집중점검'과 같은 인기 카테고리를 조합한 콘텐츠를 기획하여 검색 노출 가능성을 극대화해야 합니다.309    - **블로그 채널 활용**: '네이버 블로그'가 양질의 독자를 유입시키는 핵심 채널임이 확인되었습니다. 카드뉴스나 기사 요약본 등 블로그 플랫폼에 최적화된 2차 콘텐츠를 제작하여 배포하는 전략이 유효합니다.310 3113.  **독자 참여도 증진 전략**:312    - **참여도 높은 카테고리 벤치마킹**: '글로벌 미디어 현장', '미디어 리뷰' 등 참여도가 높은 카테고리의 형식(예: 전문가 인터뷰, 특정 사례 심층 분석, 명확한 주장 제시)을 다른 기사에 적용해 볼 수 있습니다.313    - **인터랙티브 요소 도입**: 기사 말미에 관련 주제에 대한 독자 의견을 묻는 질문을 추가하거나, 투표 기능을 활용하여 댓글 및 상호작용을 유도하는 방안을 검토해야 합니다.314"""315    # 리포트 파일로 저장316    report_path = f'{output_dir}/comprehensive_analysis_report.txt'317    with open(report_path, 'w', encoding='utf-8') as f:318        f.write(report)319        320    print(f"  - 종합 인사이트 보고서 생성 완료. ({report_path} 저장)")321 322# 6. 메인 실행 함수323def main():324    """스크립트의 메인 실행 로직"""325    print("===== 신문과방송 독자 데이터 심층 분석 스크립트 실행 =====")326    327    # 1. 환경 설정328    data_dir, output_dir = setup_environment()329    330    # 2. 데이터 로드 및 전처리331    all_data = load_and_preprocess_data(data_dir)332    333    # 3. 상세 분석 및 시각화 실행334    analyze_metrics_overview(all_data['merged'], output_dir)335    analyze_content_features(all_data['merged'], output_dir)336    analyze_demographics(all_data['demo'], all_data['merged'], output_dir)337    analyze_referrer(all_data['referrer'], all_data['merged'], output_dir)338    339    # 4. 종합 인사이트 보고서 생성340    generate_insights_report(all_data, output_dir)341    342    print("\n===== 모든 분석이 성공적으로 완료되었습니다. =====")343    print(f"결과물은 '{output_dir}' 폴더에서 확인하실 수 있습니다.")344 345if __name__ == '__main__':346    main()