# -*- coding: utf-8 -*- """ 신문과방송 독자 데이터 심층 탐색적 데이터 분석 (Advanced EDA) 이 스크립트는 다음 4개의 데이터셋을 활용하여 신문과방송 독자 데이터를 심층 분석합니다. 1. article_metrics_monthly.csv: 기사별 월간 지표 (조회수, 좋아요, 댓글) 2. contents.csv: 기사 콘텐츠 정보 (카테고리, 제목, 태그 등) 3. demographics_merged.csv: 기사별 인구통계학적 독자 데이터 4. referrer.csv: 기사별 유입 경로 데이터 주요 분석 내용: - 데이터 전처리 및 피처 엔지니어링 - 기사 핵심 지표(조회수, 좋아요, 댓글) 분포 및 상관관계 분석 - 콘텐츠 카테고리별 성과 및 독자 참여도 심층 분석 - 태그 분석 (Word Cloud 포함) - 인구통계(연령/성별) 그룹별 선호 카테고리 분석 (히트맵) - 유입 경로별 성과 및 효율성 분석 - 종합 인사이트 도출 및 리포트 자동 생성 실행 방법: - 스크립트를 실행하기 전, DATA_DIR 경로를 실제 데이터가 있는 폴더로 수정하세요. - 실행 시 스크립트와 동일한 위치에 'output' 폴더가 생성되며, 모든 시각화 자료와 최종 인사이트 보고서가 저장됩니다. """ # 1. 라이브러리 임포트 import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from datetime import datetime import warnings import os from wordcloud import WordCloud warnings.filterwarnings('ignore') # 2. 기본 설정 및 전역 변수 def setup_environment(): """분석 환경 설정 (경로, 시각화 스타일)""" # === 경로 설정 (사용자 환경에 맞게 수정) === DATA_DIR = r'Broadcast_paper\data_csv' OUTPUT_DIR = r'./output_analysis' # 출력 폴더 생성 if not os.path.exists(OUTPUT_DIR): os.makedirs(OUTPUT_DIR) print(f"'{OUTPUT_DIR}' 폴더를 생성했습니다.") # === 시각화 설정 === plt.rc('font', family='Malgun Gothic') plt.rcParams['axes.unicode_minus'] = False sns.set(font='Malgun Gothic', rc={'axes.unicode_minus': False}, style='whitegrid') print("분석 환경 설정 완료!") return DATA_DIR, OUTPUT_DIR # 3. 데이터 로드 및 전처리 def load_and_preprocess_data(data_dir): """데이터를 로드하고 기본 전처리를 수행합니다.""" print("\n[단계 1] 데이터 로드 및 전처리 시작...") # 데이터 로드 df_metrics = pd.read_csv(f'{data_dir}/article_metrics_monthly.csv') df_contents = pd.read_csv(f'{data_dir}/contents.csv') df_demo = pd.read_csv(f'{data_dir}/demographics_merged.csv') df_referrer = pd.read_csv(f'{data_dir}/referrer.csv') # --- 전처리 --- # 1. df_metrics df_metrics['period'] = pd.to_datetime(df_metrics['period']) df_metrics['comments'].fillna(0, inplace=True) # 댓글 결측치는 0으로 처리 # 2. df_contents df_contents.dropna(subset=['category', 'content', 'date'], inplace=True) # 주요 정보 결측 행 제거 df_contents['date'] = pd.to_datetime(df_contents['date']) df_contents['publish_month'] = df_contents['date'].dt.to_period('M') df_contents['publish_dayofweek'] = df_contents['date'].dt.day_name() df_contents['content_length'] = df_contents['content'].str.len() # 3. df_demo df_demo_filtered = df_demo[df_demo['age_group'] != '전체'].copy() # 4. 데이터 통합 # 월별 지표를 기사별 총계로 집계 article_total_metrics = df_metrics.groupby('article_id').agg({ 'views_total': 'sum', 'likes': 'sum', 'comments': 'sum' }).reset_index() # 콘텐츠 정보와 기사별 총계 지표 병합 df_merged = pd.merge(df_contents, article_total_metrics, on='article_id', how='left') df_merged.fillna({'views_total': 0, 'likes': 0, 'comments': 0}, inplace=True) # 참여도(Engagement Rate) 계산: (좋아요 + 댓글) / 조회수 # 조회수가 0인 경우 오류 방지 df_merged['engagement_rate'] = ( (df_merged['likes'] + df_merged['comments']) / df_merged['views_total'].replace(0, np.nan) ) * 100 print("데이터 로드 및 전처리 완료!") return { "metrics": df_metrics, "contents": df_contents, "demo": df_demo_filtered, "referrer": df_referrer, "merged": df_merged } # 4. 상세 분석 및 시각화 함수들 def analyze_metrics_overview(df_merged, output_dir): """기사 지표의 전반적인 분포와 상관관계를 분석하고 시각화합니다.""" print("\n[단계 2] 기사 지표 전반 분석...") fig, axes = plt.subplots(1, 2, figsize=(18, 7)) # 조회수, 좋아요, 댓글 분포 sns.histplot(data=df_merged, x='views_total', bins=50, ax=axes[0], kde=True) axes[0].set_title('기사별 총 조회수 분포', fontsize=16) axes[0].set_xlabel('총 조회수') axes[0].set_ylabel('기사 수') axes[0].set_xlim(0, df_merged['views_total'].quantile(0.95)) # 상위 5% 이상은 제외하여 분포 확인 # 상관관계 히트맵 corr = df_merged[['views_total', 'likes', 'comments', 'content_length']].corr() sns.heatmap(corr, annot=True, cmap='coolwarm', fmt='.2f', ax=axes[1]) axes[1].set_title('주요 지표 간 상관관계', fontsize=16) plt.tight_layout() plt.savefig(f'{output_dir}/metrics_overview.png') plt.close() print(" - 기사 지표 분포 및 상관관계 분석 완료. (metrics_overview.png 저장)") def analyze_content_features(df_merged, output_dir): """콘텐츠 특징(카테고리, 태그, 글자 수, 발행 요일)에 따른 성과 분석""" print("\n[단계 3] 콘텐츠 특징별 성과 분석...") # 카테고리별 평균 지표 category_performance = df_merged.groupby('category').agg({ 'views_total': 'mean', 'likes': 'mean', 'comments': 'mean', 'engagement_rate': 'mean' }).sort_values('views_total', ascending=False) fig, ax = plt.subplots(figsize=(14, 10)) category_performance['views_total'].sort_values().plot(kind='barh', ax=ax, color='skyblue') ax.set_title('카테고리별 평균 조회수', fontsize=16) ax.set_xlabel('평균 조회수') ax.set_ylabel('카테고리') plt.tight_layout() plt.savefig(f'{output_dir}/category_avg_views.png') plt.close() print(" - 카테고리별 평균 조회수 분석 완료. (category_avg_views.png 저장)") # 태그 분석 및 Word Cloud tags = df_merged['tag'].dropna().str.split(',').explode().str.strip() top_tags = tags.value_counts().head(50) wordcloud = WordCloud( font_path='malgun', width=1000, height=600, background_color='white', colormap='viridis' ).generate_from_frequencies(top_tags) plt.figure(figsize=(15, 9)) plt.imshow(wordcloud, interpolation='bilinear') plt.axis('off') plt.title('상위 50개 태그 Word Cloud', fontsize=20) plt.tight_layout() plt.savefig(f'{output_dir}/tags_wordcloud.png') plt.close() print(" - 태그 Word Cloud 생성 완료. (tags_wordcloud.png 저장)") # 발행 요일별 기사 수 및 평균 조회수 fig, axes = plt.subplots(1, 2, figsize=(18, 7)) day_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] sns.countplot(data=df_merged, y='publish_dayofweek', order=day_order, ax=axes[0], palette='pastel') axes[0].set_title('요일별 발행 기사 수', fontsize=16) axes[0].set_xlabel('기사 수') axes[0].set_ylabel('요일') sns.barplot(data=df_merged, y='publish_dayofweek', x='views_total', order=day_order, ax=axes[1], palette='pastel', ci=None) axes[1].set_title('요일별 평균 조회수', fontsize=16) axes[1].set_xlabel('평균 조회수') axes[1].set_ylabel('') plt.tight_layout() plt.savefig(f'{output_dir}/dayofweek_performance.png') plt.close() print(" - 발행 요일별 성과 분석 완료. (dayofweek_performance.png 저장)") def analyze_demographics(df_demo, df_merged, output_dir): """인구통계학적 특성(연령/성별)에 따른 콘텐츠 소비 패턴 분석""" print("\n[단계 4] 인구통계 그룹별 선호도 분석...") # 기사 ID를 기준으로 인구통계 데이터와 콘텐츠 데이터 병합 df_demo_content = pd.merge(df_demo, df_merged[['article_id', 'category']], on='article_id', how='left') # 연령대 및 성별에 따른 카테고리별 조회수 집계 demo_category_views = df_demo_content.groupby(['age_group', 'gender', 'category'])['views'].sum().reset_index() # 히트맵 생성을 위한 피벗 테이블 # 여성 독자 female_pivot = demo_category_views[demo_category_views['gender'] == '여'].pivot_table( index='category', columns='age_group', values='views', aggfunc='sum' ).fillna(0) # 남성 독자 male_pivot = demo_category_views[demo_category_views['gender'] == '남'].pivot_table( index='category', columns='age_group', values='views', aggfunc='sum' ).fillna(0) # 시각화 fig, axes = plt.subplots(2, 1, figsize=(20, 24)) sns.heatmap(female_pivot, cmap='Reds', annot=True, fmt='.0f', linewidths=.5, ax=axes[0]) axes[0].set_title('여성 연령대별 선호 카테고리 (총 조회수 기준)', fontsize=18) axes[0].set_xlabel('연령대') axes[0].set_ylabel('카테고리') sns.heatmap(male_pivot, cmap='Blues', annot=True, fmt='.0f', linewidths=.5, ax=axes[1]) axes[1].set_title('남성 연령대별 선호 카테고리 (총 조회수 기준)', fontsize=18) axes[1].set_xlabel('연령대') axes[1].set_ylabel('카테고리') plt.tight_layout() plt.savefig(f'{output_dir}/demographic_category_preference_heatmap.png') plt.close() print(" - 인구통계 그룹별 선호 카테고리 히트맵 분석 완료. (demographic_category_preference_heatmap.png 저장)") def analyze_referrer(df_referrer, df_merged, output_dir): """유입 경로별 기여도 및 효율성 분석""" print("\n[단계 5] 유입 경로별 효율성 분석...") # 유입 경로 데이터와 기사 지표 병합 df_referrer_merged = pd.merge(df_referrer, df_merged[['article_id', 'views_total', 'engagement_rate']], on='article_id', how='left') # 주요 유입 경로(상위 10개) 추출 top_10_referrers = df_referrer_merged.groupby('referrer')['share'].sum().nlargest(10).index df_top_referrers = df_referrer_merged[df_referrer_merged['referrer'].isin(top_10_referrers)] # 유입 경로별 평균 참여도 계산 referrer_engagement = df_top_referrers.groupby('referrer')['engagement_rate'].mean().sort_values(ascending=False) fig, axes = plt.subplots(1, 2, figsize=(20, 8)) # 유입 경로별 총 기여도 df_top_referrers.groupby('referrer')['share'].sum().sort_values().plot(kind='barh', ax=axes[0], color='c') axes[0].set_title('상위 10개 유입 경로별 총 기여도(Share)', fontsize=16) axes[0].set_xlabel('총 Share') axes[0].set_ylabel('유입 경로') # 유입 경로별 평균 참여도 referrer_engagement.sort_values().plot(kind='barh', ax=axes[1], color='m') axes[1].set_title('상위 10개 유입 경로별 평균 참여도(%)', fontsize=16) axes[1].set_xlabel('평균 참여도 (%)') axes[1].set_ylabel('') plt.tight_layout() plt.savefig(f'{output_dir}/referrer_performance.png') plt.close() print(" - 주요 유입 경로별 기여도 및 참여도 분석 완료. (referrer_performance.png 저장)") # 5. 종합 인사이트 생성 def generate_insights_report(data, output_dir): """분석 결과를 바탕으로 종합적인 인사이트 보고서를 생성합니다.""" print("\n[단계 6] 종합 인사이트 보고서 생성...") # 보고서 내용 생성 report = f""" # 신문과방송 독자 데이터 심층 분석 보고서 생성일: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} ## 1. 분석 개요 - 본 보고서는 기사 성과 지표, 콘텐츠 특성, 독자 인구통계, 유입 경로 데이터를 종합하여 독자 행동 패턴을 분석하고, 이를 기반으로 콘텐츠 전략 개선 방안을 제시하는 것을 목표로 합니다. - 총 {data['merged']['article_id'].nunique():,}개의 기사와 관련 데이터를 분석했습니다. ## 2. 주요 분석 결과 (Key Findings) ### 2.1. 콘텐츠 성과 - **성과 분포**: 대부분의 기사는 소수의 조회수를 기록하며, 소수의 '히트 기사'가 전체 조회수를 견인하는 롱테일(Long-tail) 분포를 보입니다. (metrics_overview.png 참고) - **핵심 카테고리**: '미디어 人사이드', '아이디어스', '미디어·AI트렌드' 카테고리가 평균 조회수 최상위권을 차지했습니다. 이들 카테고리가 독자의 높은 관심을 유도하는 핵심 콘텐츠임을 시사합니다. (category_avg_views.png 참고) - **주요 태그**: '#언론', '#기자', '#뉴스', '#미디어', '#저널리즘' 등 언론 본질과 관련된 키워드가 가장 빈번하게 사용되었습니다. '#인공지능', '#AI', '#테크' 등 기술 관련 태그도 상위권에 위치하여 기술 트렌드에 대한 높은 관심을 보여줍니다. (tags_wordcloud.png 참고) ### 2.2. 독자 특성 - **주요 독자층**: 10대 후반에서 30대 초반의 젊은 층이 콘텐츠 소비의 핵심 그룹입니다. 특히 19-24세 여성 그룹의 활동이 두드러집니다. - **성별/연령별 선호도**: - **여성**: 10대-20대 초반은 '커버스토리', '미디어포럼'에, 20대 후반-30대는 '취재기·제작기', '미디어 人사이드' 등 심층적인 콘텐츠에 높은 반응을 보입니다. - **남성**: 20대-30대 그룹이 전반적인 소비를 주도하며, 특히 '커버스토리', '집중점검'과 같은 시사/기획 기사에 대한 관심이 높습니다. - (demographic_category_preference_heatmap.png 참고) ### 2.3. 유입 경로 효율성 - **주요 유입 채널**: 'Google'과 '네이버' 관련 채널(통합검색, 블로그 등)이 전체 트래픽의 압도적인 비중을 차지합니다. 검색 엔진 최적화(SEO)의 중요성이 매우 큽니다. - **고품질 트래픽**: '네이버 블로그검색'은 높은 트래픽 기여도와 함께 양호한 독자 참여도를 보여주는 효율적인 채널입니다. 반면, 'Google'은 가장 많은 트래픽을 유입시키지만, 평균 참여도는 상대적으로 낮아 넓은 범위의 일반 독자 유입이 많을 것으로 추정됩니다. (referrer_performance.png 참고) ## 3. 전략적 제언 (Strategic Recommendations) 1. **콘텐츠 개인화 및 타겟팅 강화**: - **핵심 독자층(19-34세) 집중**: 이들이 선호하는 '미디어 人사이드', '미디어·AI트렌드'와 같은 심층 분석 및 트렌드 관련 콘텐츠를 강화하고, 관련 신규 기획을 발굴해야 합니다. - **잠재 독자층(40대 이상) 공략**: 40대 이상 남녀가 공통적으로 관심을 보이는 '집중점검', '미디어현장' 카테고리 콘텐츠를 활용하여 이 연령대에 특화된 주제(예: 미디어 리터러시, 가짜뉴스 판별)로 확장하는 전략을 고려할 수 있습니다. 2. **검색엔진 최적화(SEO) 고도화**: - **콘텐츠-태그 연계**: Word Cloud 분석에서 도출된 '#AI', '#디지털', '#플랫폼' 등의 인기 기술 태그와 '커버스토리', '집중점검'과 같은 인기 카테고리를 조합한 콘텐츠를 기획하여 검색 노출 가능성을 극대화해야 합니다. - **블로그 채널 활용**: '네이버 블로그'가 양질의 독자를 유입시키는 핵심 채널임이 확인되었습니다. 카드뉴스나 기사 요약본 등 블로그 플랫폼에 최적화된 2차 콘텐츠를 제작하여 배포하는 전략이 유효합니다. 3. **독자 참여도 증진 전략**: - **참여도 높은 카테고리 벤치마킹**: '글로벌 미디어 현장', '미디어 리뷰' 등 참여도가 높은 카테고리의 형식(예: 전문가 인터뷰, 특정 사례 심층 분석, 명확한 주장 제시)을 다른 기사에 적용해 볼 수 있습니다. - **인터랙티브 요소 도입**: 기사 말미에 관련 주제에 대한 독자 의견을 묻는 질문을 추가하거나, 투표 기능을 활용하여 댓글 및 상호작용을 유도하는 방안을 검토해야 합니다. """ # 리포트 파일로 저장 report_path = f'{output_dir}/comprehensive_analysis_report.txt' with open(report_path, 'w', encoding='utf-8') as f: f.write(report) print(f" - 종합 인사이트 보고서 생성 완료. ({report_path} 저장)") # 6. 메인 실행 함수 def main(): """스크립트의 메인 실행 로직""" print("===== 신문과방송 독자 데이터 심층 분석 스크립트 실행 =====") # 1. 환경 설정 data_dir, output_dir = setup_environment() # 2. 데이터 로드 및 전처리 all_data = load_and_preprocess_data(data_dir) # 3. 상세 분석 및 시각화 실행 analyze_metrics_overview(all_data['merged'], output_dir) analyze_content_features(all_data['merged'], output_dir) analyze_demographics(all_data['demo'], all_data['merged'], output_dir) analyze_referrer(all_data['referrer'], all_data['merged'], output_dir) # 4. 종합 인사이트 보고서 생성 generate_insights_report(all_data, output_dir) print("\n===== 모든 분석이 성공적으로 완료되었습니다. =====") print(f"결과물은 '{output_dir}' 폴더에서 확인하실 수 있습니다.") if __name__ == '__main__': main()