mutoy/Broadcast_paper
0
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()