项目一:全球气候温度数据可视化与趋势分析工具

项目背景

全球气候变暖是当前重要环境议题,相关温度数据的分析能直观呈现气候变化规律。本项目基于公开的全球气温数据集,开发分析工具,实现温度数据的清洗、统计与可视化,为气候相关学习与研究提供支持,贴合信息与计算科学专业 “数据处理与可视化” 的实践需求。

核心功能要求

1.     数据获取与预处理:从公开数据源(如NASA地球观测站官网https://earthobservatory.nasa.gov/提供的 “全球年均温度异常数据集”、 Kaggle 平台 “Global Temperature Time Series” 数据集)下载 CSV/Excel 格式数据,包含 “年份、全球平均温度、北半球平均温度、南半球平均温度” 等字段。对数据进行清洗,剔除缺失值、修正格式异常数据(如日期格式统一),并提取 “2020-2025 年” 关键时间段数据进行分析。

2.     趋势统计分析:计算 2020-2025 年全球平均温度的年际变化率、每年平均温度均值;对比北半球与南半球的温度变化差异,找出温度上升最显著的年份。

3.     多维度可视化:使用 Matplotlib/Seaborn 绘制 “2020-2025 年全球平均温度变化折线图”(标注温度异常峰值年份)、“南北半球温度对比柱状图(每年)”、“全球温度异常分布热力图(按年份)”;通过可视化直观呈现温度上升趋势。

4.     结果导出与报告:将统计结果(如每年温度均值表、温度变化率表)导出为 Excel 文件,将所有可视化图表保存为高清 PNG 格式;给出生成《全球气候温度趋势分析报告》(Markdown 格式),包含数据来源说明、分析结论(如 “2020-2025 年全球平均温度上升约1.1℃”)及建议。

代码包含以下8个文件:

data_loader.py:负责加载 Excel 格式的数据文件

data_extractor.py:从原始数据中提取指定时间段(默认 2020-2025 年)的数据,处理数据缺失情况

data_analyzer.py:对提取的时间段数据进行基础分析,包括计算温度趋势(上升/下降/平稳)、总变化幅度、极值(最高/最低温度及对应年份)、平均值、标准差,以及检测异常值(基于±2σ范围),并输出分析结果。

data_trend_analyzer.py:进行更深入的趋势统计分析

data_visualizer.py:提供可视化功能

data_visualizer_extend.py:扩展可视化功能

report_generator.py:生成分析结果报告

main.py:主程序入口,串联整个分析流程

以下为每个代码块的具体代码:

data_loader.py

import pandas as pd

import os

from typing import Optional



def load_excel_data(file_path: str) -> Optional[pd.DataFrame]:

    """

    加载Excel数据

    :param file_path: Excel文件路径

    :return: 原始DataFrame或None(文件不存在时)

    """

    # 1. 校验文件是否存在

    if not os.path.exists(file_path):

        print(f"文件 '{file_path}' 不存在")

        return None



    # 2. 校验文件格式

    if not file_path.endswith(('.xlsx', '.xls')):

        print(f"文件 '{file_path}' 不是Excel格式")

        return None



    try:

        # 优先使用openpyxl引擎(支持.xlsx),兼容中文

        df = pd.read_excel(file_path, engine='openpyxl')

        print(f"成功加载数据:{file_path}")

        print(f"原始数据规模:{df.shape[0]}行 × {df.shape[1]}列")

        return df

    except Exception as e:

        print(f"读取文件失败:{str(e)}")

        return None

data_extractor.py

import pandas as pd

import os

from typing import Optional



def extract_time_period(df: pd.DataFrame, start_year: int = 2020, end_year: int = 2025) -> Optional[pd.DataFrame]:

    """

    提取指定时间段数据(默认2020-2025年)

    :param df: 清洗后的DataFrame

    :param start_year: 起始年份

    :param end_year: 结束年份

    :return: 时间段内的DataFrame

    """



    # 校验年份列是否存在

    if '年份' not in df.columns:

        print("未找到'年份'列,无法提取时间段数据")

        return None



    # 2. 筛选目标时间段

    df_extracted = df[(df['年份'] >= start_year) & (df['年份'] <= end_year)]

    extracted_years = sorted(df_extracted['年份'].unique())



    # 3. 处理数据不足的情况

    if len(df_extracted) == 0:

        print(f"无{start_year}-{end_year}年数据,将提取最近5年数据")

        recent_years = sorted(df['年份'].unique())[-5:]

        df_extracted = df[df['年份'].isin(recent_years)]

        extracted_years = recent_years

    return df_extracted



def save_extracted_data(df: pd.DataFrame, output_dir: str = 'output', filename: str = 'extracted_data.xlsx') -> bool:

    """

    保存提取的时间段数据

    :param df: 提取后的DataFrame

    :param output_dir: 输出文件夹

    :param filename: 输出文件名

    :return: 保存成功返回True,失败返回False

    """

    # 创建输出文件夹(不存在则创建)

    os.makedirs(output_dir, exist_ok=True)

    output_path = os.path.join(output_dir, filename)



    try:

        df.to_excel(output_path, index=False, engine='openpyxl')

        return True

    except Exception as e:

        print(f"保存提取数据失败:{str(e)}")

        return False

data_analyzer.py

import pandas as pd

# import numpy as np

from typing import Dict, Optional



def analyze_extracted_data(df: pd.DataFrame) -> Optional[Dict]:

    """

    分析提取的时间段数据(2020-2025年)- 修复空值导致的KeyError

    :param df: 提取后的DataFrame

    :return: 分析结果字典(含趋势、极值、异常值等)

    """



    # 基础校验:必要列存在性

    required_cols = ['年份', '全年平均']

    if not all(col in df.columns for col in required_cols):

        print(f"缺失必要列(需包含{required_cols}),无法分析")

        return None



    # 2. 空值过滤:仅保留核心字段无空值的行

    df_valid = df[['年份', '全年平均']].copy().dropna(subset=['年份', '全年平均'])

    total_raw_rows = len(df)  # 原始总行数

    valid_data_rows = len(df_valid)  # 有效数据行数



    if valid_data_rows == 0:

        print(f"有效数据为空:原始{total_raw_rows}行数据均含空值(年份或全年平均为空),无法计算分析指标")

        return None  # 直接返回,避免后续创建字典失败



    # 提取核心数据

    years = df_valid['年份'].values

    annual_avg = df_valid['全年平均'].values



    # 4. 提取季节数据

    seasonal_cols = ['冬季平均', '春季平均', '夏季平均', '秋季平均']

    if all(col in df.columns for col in seasonal_cols):

        df_season_valid = df_valid.join(df[seasonal_cols]).dropna(subset=seasonal_cols)



    # 5. 计算关键指标(仅在有效数据足够时执行)

    analysis_result = {}  # 初始化字典

    try:

        # 基础时间信息

        analysis_result['time_period'] = f"{int(years.min())}-{int(years.max())}年"

        analysis_result['total_raw_rows'] = total_raw_rows

        analysis_result['valid_data_rows'] = valid_data_rows



        # 趋势计算(至少2行有效数据才计算趋势)

        if valid_data_rows >= 2:

            trend = '上升' if annual_avg[-1] > annual_avg[0] else '下降' if annual_avg[-1] < annual_avg[0] else '平稳'

            total_change = round(annual_avg[-1] - annual_avg[0], 1)

            analysis_result['annual_trend'] = trend

            analysis_result['total_change'] = total_change

        else:

            analysis_result['annual_trend'] = '无法判断(有效数据仅1行)'

            analysis_result['total_change'] = 0.0



        # 极值与统计量计算

        analysis_result['max_temp'] = {

            'year': int(years[annual_avg.argmax()]),

            'value': round(annual_avg.max(), 1)

        }

        analysis_result['min_temp'] = {

            'year': int(years[annual_avg.argmin()]),

            'value': round(annual_avg.min(), 1)

        }

        analysis_result['avg_temp'] = round(annual_avg.mean(), 2)

        analysis_result['temp_std'] = round(annual_avg.std(), 2) if valid_data_rows >= 2 else 0.0



        # 异常值检测(至少3行有效数据才检测)

        analysis_result['outliers'] = []

        if valid_data_rows >= 3:

            mean_val = annual_avg.mean()

            std_val = annual_avg.std()

            outliers_mask = (annual_avg < mean_val - 2 * std_val) | (annual_avg > mean_val + 2 * std_val)

            if outliers_mask.any():

                analysis_result['outliers'] = [

                    {'year': int(years[i]), 'value': round(annual_avg[i], 1)}

                    for i in range(len(years)) if outliers_mask[i]

                ]



    except Exception as e:

        print(f"计算分析指标时出错:{str(e)}")

        return None



    # 输出分析结果(确保所有字典键存在)

    print(f"分析时间段:{analysis_result.get('time_period', '未知')}")

    print(f"原始数据行数:{analysis_result.get('total_raw_rows', 0)}行")

    print(f"有效数据行数:{analysis_result.get('valid_data_rows', 0)}行")



    print(f"\n温度趋势:")

    print(f"整体趋势:{analysis_result.get('annual_trend', '无法判断')}")

    if analysis_result.get('annual_trend') != '无法判断(有效数据仅1行)':

        print(f"变化幅度:{analysis_result.get('total_change', 0.0)}")

    print(f"平均温度:{analysis_result.get('avg_temp', 0.0)}")

    print(f"温度波动:{analysis_result.get('temp_std', 0.0)}(标准差)")



    print(f"\n极值统计:")

    max_temp = analysis_result.get('max_temp', {'year': '未知', 'value': 0.0})

    min_temp = analysis_result.get('min_temp', {'year': '未知', 'value': 0.0})

    print(f"最高温度:{max_temp['year']}年({max_temp['value']})")

    print(f"最低温度:{min_temp['year']}年({min_temp['value']})")



    outliers = analysis_result.get('outliers', [])

    if outliers:

        print(f"\n发现{len(outliers)}个异常值(超出±2σ范围)")

        for outlier in outliers:

            print(f"{outlier['year']}年:{outlier['value']}")

    else:

        print(f"\n未发现异常值(所有数据在±2σ范围内)")



    return analysis_result

data_trend_analyzer.py

import pandas as pd

import numpy as np

from typing import Dict, Tuple



def calculate_annual_change_rate(df: pd.DataFrame) -> Tuple[pd.DataFrame, Dict]:

    """

    计算2020-2025年全球平均温度的年际变化率和每年温度均值

    年际变化率 = (当年温度 - 前一年温度) / 前一年温度 * 100%

    """

    # 筛选有效数据

    df_valid = df[['年份', '全年平均']].dropna(subset=['年份', '全年平均']).sort_values('年份')

    years = df_valid['年份'].astype(int).values

    temps = df_valid['全年平均'].values



    # 计算每年温度均值

    annual_mean = pd.DataFrame({

        '年份': years,

        '全球平均温度': temps

    })



    # 计算年际变化率

    annual_change_rates = [np.nan]  # 第一年无变化率

    for i in range(1, len(temps)):

        rate = (temps[i] - temps[i - 1]) / temps[i - 1] * 100

        annual_change_rates.append(round(rate, 2))

    annual_mean['年际变化率(%)'] = annual_change_rates



    # 找出温度上升最显著的年份

    valid_rates = [(years[i], annual_change_rates[i]) for i in range(1, len(annual_change_rates)) if

                   annual_change_rates[i] > 0]

    if valid_rates:

        max_rise_year, max_rise_rate = max(valid_rates, key=lambda x: x[1])

    else:

        max_rise_year, max_rise_rate = None, 0



    trend_result = {

        'annual_mean_df': annual_mean,

        'max_rise_year': max_rise_year,

        'max_rise_rate': max_rise_rate,

        'total_rise': round(temps[-1] - temps[0], 2) if len(temps) >= 2 else 0

    }

    return annual_mean, trend_result



def compare_hemisphere_temp(df: pd.DataFrame) -> pd.DataFrame:

    """

    对比南北半球温度变化差异(模拟划分:冬季平均=北半球,夏季平均=南半球)

    计算每年南北半球温度及温差

    """

    required_cols = ['年份', '冬季平均', '夏季平均']

    df_hemisphere = df[required_cols].dropna(subset=required_cols).sort_values('年份')

    df_hemisphere['年份'] = df_hemisphere['年份'].astype(int)

    df_hemisphere.columns = ['年份', '北半球平均温度', '南半球平均温度']



    # 计算南北半球温差

    df_hemisphere['南北半球温差'] = df_hemisphere['北半球平均温度'] - df_hemisphere['南半球平均温度']

    df_hemisphere = df_hemisphere.round(2)

    return df_hemisphere



def get_temp_anomaly(df: pd.DataFrame) -> pd.DataFrame:

    """

    计算每年温度异常值(相对于2020-2025年整体均值),用于热力图绘制

    """

    df_valid = df[['年份', '全年平均']].dropna(subset=['年份', '全年平均'])

    overall_mean = df_valid['全年平均'].mean()

    df_valid['温度异常值'] = df_valid['全年平均'] - overall_mean

    df_valid['年份'] = df_valid['年份'].astype(int)

    return df_valid[['年份', '温度异常值']].sort_values('年份')

data_visualizer.py

import pandas as pd

import matplotlib

matplotlib.use('TkAgg')  # 使用TkAgg后端

import matplotlib.pyplot as plt

import os

import numpy as np



def set_plot_style():

    """设置图表样式(兼容所有Matplotlib版本,仅保留基础稳定参数)"""



    # 中文显示配置

    plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'sans-serif']

    plt.rcParams['axes.unicode_minus'] = False



    # 基础样式

    plt.rcParams['figure.figsize'] = (16, 8)  # 月度图加宽画布,减少标签重叠

    plt.rcParams['font.size'] = 10

    plt.rcParams['axes.linewidth'] = 1.0

    plt.rcParams['grid.alpha'] = 0.3



def plot_monthly_temperature_2020_2025(df: pd.DataFrame, output_dir: str = 'output',

                                       filename: str = 'monthly_temperature_202001_202510.png') -> bool:

    """绘制2020年1月-2025年10月月度温度图"""

    set_plot_style()



    # 数据预处理:年度转月度时间序列

    target_years = df[(df['年份'] >= 2020) & (df['年份'] <= 2025)].copy()

    if len(target_years) == 0:

        print(f"未找到2020-2025年数据")

        return False



    month_cols = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']

    monthly_dates = []  # 格式:2020-01

    monthly_temps = []

    missing_months = []



    for _, row in target_years.iterrows():

        year = int(row['年份'])

        # 2025年只取1-10月,其他年份1-12月

        max_month = 10 if year == 2025 else 12

        for month in range(1, max_month + 1):

            month_col = month_cols[month - 1]

            temp = row[month_col]

            date_str = f"{year}-{str(month).zfill(2)}"

            monthly_dates.append(date_str)



            if pd.notna(temp):

                monthly_temps.append(float(temp))

            else:

                monthly_temps.append(np.nan)

                missing_months.append(date_str)



    # 筛选2020-01至2025-10范围

    start_idx = monthly_dates.index('2020-01') if '2020-01' in monthly_dates else 0

    end_idx = monthly_dates.index('2025-10') + 1 if '2025-10' in monthly_dates else len(monthly_dates)

    final_dates = monthly_dates[start_idx:end_idx]

    final_temps = monthly_temps[start_idx:end_idx]



    if len(final_dates) == 0:

        print(f"未找到2020-01至2025-10的月度数据")

        return False



    # 2. 绘制图表

    fig, ax = plt.subplots(1, 1)



    # 绘制月度温度折线

    ax.plot(final_dates, final_temps,

            color='#2E86AB', linewidth=2.0,

            marker='o', markersize=4,

            markerfacecolor='#E74C3C', markeredgewidth=0.5,

            label='月度平均温度', alpha=0.8)



    # 标注缺失数据

    if missing_months:

        missing_in_range = [d for d in missing_months if d in final_dates]

        if missing_in_range:

            # 计算y轴位置(避免与有效数据重叠)

            valid_temps = [t for t in final_temps if pd.notna(t)]

            y_min = min(valid_temps) - 5 if valid_temps else 0

            # 绘制缺失值标记

            ax.scatter(missing_in_range, [y_min] * len(missing_in_range),

                       color='#E74C3C', s=50, marker='x', linewidth=2,

                       label=f'缺失数据({len(missing_in_range)}个月份)', zorder=5)



    # 3. X轴标签设置

    # 每6个月显示一个标签,减少密度

    ax.set_xticks(final_dates[::6])

    # 设置标签并旋转

    ax.set_xticklabels(final_dates[::6], rotation=45)

    ax.tick_params(axis='x', labelsize=9)



    # 坐标轴和标题

    ax.set_xlabel('年月', fontsize=12, fontweight='bold')

    ax.set_ylabel('月度平均温度', fontsize=12, fontweight='bold')

    ax.grid(True, axis='y', alpha=0.3)  # 仅显示y轴网格,不干扰x轴标签

    ax.set_title('2020年1月-2025年10月全球月度平均温度变化图',

                 fontsize=14, fontweight='bold', pad=20)

    # 图例位置优化

    ax.legend(loc='upper right', frameon=True, fancybox=True, shadow=True, fontsize=9)

    ax.set_facecolor('#F8F9FA')

    fig.patch.set_facecolor('white')



    # 5. 保存图表

    os.makedirs(output_dir, exist_ok=True)

    output_path = os.path.join(output_dir, filename)

    try:

        # 旧版Matplotlib tight_layout可能有问题,改用subplots_adjust

        plt.subplots_adjust(bottom=0.15)  # 预留底部空间,防止x轴标签被截断

        plt.savefig(output_path, dpi=300, bbox_inches='tight', facecolor='white')

        plt.close()

        # 统计信息

        valid_count = sum(1 for t in final_temps if pd.notna(t))

        print(f"统计:共{len(final_temps)}个月份(2020-01至2025-10),有效数据{valid_count}个,缺失{len(final_temps) - valid_count}个")

        return True

    except Exception as e:

        print(f"保存图表失败:{str(e)}")

        return False



def plot_temperature_trend(df: pd.DataFrame, output_dir: str = 'output', filename: str = 'analysis_chart.png') -> bool:

    """绘制年度温度图"""

    set_plot_style()

    years = df['年份'].values

    annual_avg = df['全年平均'].values



    # 1. 过滤无效值(解决分析时nan问题)

    valid_mask = pd.notna(annual_avg)

    valid_years = years[valid_mask]

    valid_avg = annual_avg[valid_mask]



    if len(valid_years) < 2:

        print("有效年度数据不足(需至少2年),无法绘制年度图")

        return False



    # 2. 计算统计指标

    mean_val = valid_avg.mean()

    std_val = valid_avg.std()

    outliers_mask = (valid_avg < mean_val - 2 * std_val) | (valid_avg > mean_val + 2 * std_val)

    outliers = valid_avg[outliers_mask]

    outlier_years = valid_years[outliers_mask]



    # 3. 绘制年度图(旧版兼容)

    fig, ax = plt.subplots(1, 1)

    ax.plot(valid_years, valid_avg,

            color='#2E86AB', linewidth=3.5,

            marker='o', markersize=10,

            markerfacecolor='#E74C3C', markeredgecolor='white', markeredgewidth=2,

            label='全球平均温度')



    # 标注异常值(旧版兼容)

    if len(outliers) > 0:

        for year, temp in zip(outlier_years, outliers):

            ax.scatter(year, temp, color='#F39C12', s=200, zorder=5, edgecolor='#D35400', linewidth=2)

            ax.annotate(f'异常值\n{int(year)}年:{temp:.1f}',

                        xy=(year, temp), xytext=(0, 30), textcoords='offset points',

                        ha='center', va='bottom', fontweight='bold', fontsize=9,

                        bbox=dict(boxstyle='round,pad=0.5', facecolor='#FDEBD0', edgecolor='#E67E22'))



    # 4. X轴设置(旧版兼容)

    ax.set_xticks(valid_years)

    ax.set_xticklabels([f'{int(y)}年' for y in valid_years])

    ax.tick_params(axis='x', labelsize=10)



    # 5. 参考线和配置

    ax.axhline(y=mean_val, color='#95A5A6', linestyle='-', linewidth=1.5, alpha=0.8, label=f'平均值:{mean_val:.1f}')

    ax.axhline(y=mean_val + 2 * std_val, color='#E74C3C', linestyle='--', linewidth=2, alpha=0.8,

               label=f'异常上限:{mean_val + 2 * std_val:.1f}')

    ax.set_xlabel('年份', fontsize=13, fontweight='bold')

    ax.set_ylabel('全球平均温度', fontsize=13, fontweight='bold')

    ax.set_title(f'{int(valid_years.min())}-{int(valid_years.max())}年全球平均温度变化趋势',

                 fontsize=16, fontweight='bold', pad=20)

    ax.grid(True, axis='y')

    ax.legend(loc='upper left', frameon=True, fancybox=True, shadow=True)

    ax.set_facecolor('#F8F9FA')

    fig.patch.set_facecolor('white')



    # 6. 保存(旧版兼容)

    os.makedirs(output_dir, exist_ok=True)

    output_path = os.path.join(output_dir, filename)

    try:

        plt.subplots_adjust(bottom=0.1)

        plt.savefig(output_path, dpi=300, bbox_inches='tight', facecolor='white')

        plt.close()

        return True

    except Exception as e:

        print(f"保存年度温度图失败:{str(e)}")

        return False

data_visualizer_extend.py

import pandas as pd

import matplotlib.pyplot as plt

import numpy as np

import os



# 基础样式配置

def set_base_style():

    plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'sans-serif']

    plt.rcParams['axes.unicode_minus'] = False

    plt.rcParams['figure.figsize'] = (12, 6)

    plt.rcParams['font.size'] = 10

    plt.rcParams['grid.alpha'] = 0.3



def plot_temp_change_line(annual_mean_df: pd.DataFrame, output_dir: str = 'output'):

    """绘制2020-2025年全球平均温度变化折线图(标注异常峰值年份)"""

    set_base_style()

    years = annual_mean_df['年份'].values

    temps = annual_mean_df['全球平均温度'].values



    # 识别异常峰值(高于均值+1个标准差)

    mean_temp = temps.mean()

    std_temp = temps.std()

    peak_mask = temps > mean_temp + std_temp

    peak_years = years[peak_mask]

    peak_temps = temps[peak_mask]



    fig, ax = plt.subplots(1, 1)

    # 绘制主折线

    ax.plot(years, temps, color='#2E86AB', linewidth=3, marker='o', markersize=8, label='全球平均温度')

    # 标注峰值

    if len(peak_years) > 0:

        for year, temp in zip(peak_years, peak_temps):

            ax.scatter(year, temp, color='#E74C3C', s=150, zorder=5)

            ax.annotate(f'峰值\n{year}年:{temp:.1f}', xy=(year, temp), xytext=(0, 10), textcoords='offset points',

                        ha='center')



    ax.set_title('2020-2025年全球平均温度变化折线图', fontsize=14, fontweight='bold', pad=20)

    ax.set_xlabel('年份', fontsize=12, fontweight='bold')

    ax.set_ylabel('全球平均温度', fontsize=12, fontweight='bold')

    ax.grid(True, axis='y')

    ax.legend()

    ax.set_facecolor('#F8F9FA')



    output_path = os.path.join(output_dir, '全球温度变化折线图.png')

    plt.tight_layout()

    plt.savefig(output_path, dpi=300, bbox_inches='tight', facecolor='white')

    plt.close()

    print(f"折线图已保存至:{output_path}")



def plot_hemisphere_bar(hemisphere_df: pd.DataFrame, output_dir: str = 'output'):

    """绘制南北半球温度对比柱状图(每年)"""

    set_base_style()

    years = hemisphere_df['年份'].values

    north_temps = hemisphere_df['北半球平均温度'].values

    south_temps = hemisphere_df['南半球平均温度'].values



    x = np.arange(len(years))

    width = 0.35



    fig, ax = plt.subplots(1, 1)

    bars1 = ax.bar(x - width / 2, north_temps, width, label='北半球', color='#3498DB', alpha=0.8)

    bars2 = ax.bar(x + width / 2, south_temps, width, label='南半球', color='#E67E22', alpha=0.8)



    ax.set_title('2020-2025年南北半球温度对比柱状图', fontsize=14, fontweight='bold', pad=20)

    ax.set_xlabel('年份', fontsize=12, fontweight='bold')

    ax.set_ylabel('平均温度', fontsize=12, fontweight='bold')

    ax.set_xticks(x)

    ax.set_xticklabels(years)

    ax.legend()

    ax.grid(True, axis='y')

    ax.set_facecolor('#F8F9FA')



    # 添加数值标签

    for bar in bars1:

        height = bar.get_height()

        ax.annotate(f'{height:.1f}', xy=(bar.get_x() + bar.get_width() / 2, height), xytext=(0, 3),

                    textcoords='offset points', ha='center')

    for bar in bars2:

        height = bar.get_height()

        ax.annotate(f'{height:.1f}', xy=(bar.get_x() + bar.get_width() / 2, height), xytext=(0, 3),

                    textcoords='offset points', ha='center')



    output_path = os.path.join(output_dir, '南北半球温度对比柱状图.png')

    plt.tight_layout()

    plt.savefig(output_path, dpi=300, bbox_inches='tight', facecolor='white')

    plt.close()

    print(f"柱状图已保存至:{output_path}")



def plot_temp_anomaly_heatmap(anomaly_df: pd.DataFrame, output_dir: str = 'output'):

    """绘制全球温度异常分布热力图(按年份)"""

    set_base_style()

    plt.rcParams['figure.figsize'] = (10, 5)



    # 转换为热力图所需的矩阵格式

    years = anomaly_df['年份'].values.reshape(1, -1)

    anomalies = anomaly_df['温度异常值'].values.reshape(1, -1)



    fig, ax = plt.subplots(1, 1)

    im = ax.imshow(anomalies, cmap='RdBu_r', aspect='auto')



    # 设置坐标轴

    ax.set_xticks(np.arange(len(years[0])))

    ax.set_xticklabels(years[0])

    ax.set_yticks([0])

    ax.set_yticklabels(['温度异常值'])



    # 添加数值标签

    for i in range(len(years[0])):

        text = ax.text(i, 0, f'{anomalies[0, i]:.1f}', ha='center', va='center', color='black', fontweight='bold')



    # 添加颜色条

    cbar = plt.colorbar(im, ax=ax, shrink=0.8)

    cbar.set_label('温度异常值', rotation=270, labelpad=15)



    ax.set_title('2020-2025年全球温度异常分布热力图', fontsize=14, fontweight='bold', pad=20)

    ax.set_facecolor('#F8F9FA')



    output_path = os.path.join(output_dir, '全球温度异常热力图.png')

    plt.tight_layout()

    plt.savefig(output_path, dpi=300, bbox_inches='tight', facecolor='white')

    plt.close()

    print(f"热力图已保存至:{output_path}")

report_generator.py

import pandas as pd

import os

from typing import Dict



def export_excel_results(annual_mean: pd.DataFrame, hemisphere_df: pd.DataFrame, anomaly_df: pd.DataFrame,

                         output_dir: str = 'output'):

    """

    导出统计结果到Excel文件,包含3个工作表

    """

    os.makedirs(output_dir, exist_ok=True)

    output_path = os.path.join(output_dir, '全球温度统计结果.xlsx')



    with pd.ExcelWriter(output_path, engine='openpyxl') as writer:

        annual_mean.to_excel(writer, sheet_name='年度温度与变化率', index=False)

        hemisphere_df.to_excel(writer, sheet_name='南北半球温度对比', index=False)

        anomaly_df.to_excel(writer, sheet_name='温度异常值', index=False)



    print(f"统计结果已导出至:{output_path}")

    return output_path



def generate_markdown_report(trend_result: Dict, hemisphere_df: pd.DataFrame, output_dir: str = 'output'):

    """

    生成《全球气候温度趋势分析报告》(Markdown格式)

    """

    os.makedirs(output_dir, exist_ok=True)

    report_path = os.path.join(output_dir, '全球气候温度趋势分析报告.md')



    # 提取核心结论

    annual_mean_df = trend_result['annual_mean_df']

    max_rise_year = trend_result['max_rise_year']

    max_rise_rate = trend_result['max_rise_rate']

    total_rise = trend_result['total_rise']

    overall_mean = annual_mean_df['全球平均温度'].mean()



    # 南北半球核心数据

    avg_north = hemisphere_df['北半球平均温度'].mean()

    avg_south = hemisphere_df['南半球平均温度'].mean()

    avg_temp_diff = hemisphere_df['南北半球温差'].mean()



    report_content = f"""# 全球气候温度趋势分析报告

## 一、数据来源说明

- 数据文件:全球.xlsx

- 分析时间段:2020-2025年

- 核心指标:全球平均温度、冬季平均温度(北半球模拟)、夏季平均温度(南半球模拟)

- 数据处理:自动过滤缺失值,保留有效数据{len(annual_mean_df)}条



## 二、核心统计结论

### 2.1 全球温度整体趋势

1. 分析期间全球平均温度:{overall_mean:.2f}

2. 2020-2025年全球温度总上升幅度:{total_rise:.2f}

3. 温度上升最显著年份:{max_rise_year}年(年际变化率:{max_rise_rate:.2f}%)

4. 年度平均温度范围:{annual_mean_df['全球平均温度'].min():.2f} - {annual_mean_df['全球平均温度'].max():.2f}



### 2.2 南北半球温度差异

1. 北半球平均温度:{avg_north:.2f}

2. 南半球平均温度:{avg_south:.2f}

3. 南北半球平均温差:{avg_temp_diff:.2f}

4. 南半球温度整体{"高于" if avg_south > avg_north else "低于"}北半球



### 2.3 年际变化特征

- 正增长年份数量:{len([r for r in annual_mean_df['年际变化率(%)'].dropna() if r > 0])}个

- 负增长年份数量:{len([r for r in annual_mean_df['年际变化率(%)'].dropna() if r < 0])}个

- 温度变化率波动范围:{annual_mean_df['年际变化率(%)'].dropna().min():.2f}% - {annual_mean_df['年际变化率(%)'].dropna().max():.2f}%



## 三、可视化结果说明

1. 全球平均温度变化折线图:标注温度异常峰值年份,直观呈现上升趋势

2. 南北半球温度对比柱状图:展示每年南北半球温度差异

3. 全球温度异常分布热力图:突出温度偏离均值的年份



## 四、建议

1. **气候监测**:重点关注{max_rise_year}年类似的快速升温时段,加强气象数据实时监测

2. **区域适配**:针对北半球温度波动更大的特征,制定差异化气候应对策略

3. **长期预警**:基于总上升幅度{total_rise:.2f}的趋势,建立长期温度预警机制

4. **数据完善**:建议补充南北半球直接观测数据,提升差异分析准确性



## 五、数据附录

- 年度温度与变化率表:全球温度统计结果.xlsx(工作表1)

- 南北半球温度对比表:全球温度统计结果.xlsx(工作表2)

- 温度异常值表:全球温度统计结果.xlsx(工作表3)



---

*报告生成时间:自动生成*

*数据版本:原始数据无清洗,保留完整原始信息*

"""



    with open(report_path, 'w', encoding='utf-8') as f:

        f.write(report_content)



    print(f"分析报告已生成至:{report_path}")

    return report_path

main.py

import os

from data_loader import load_excel_data

from data_extractor import extract_time_period, save_extracted_data

from data_analyzer import analyze_extracted_data

from data_trend_analyzer import calculate_annual_change_rate, compare_hemisphere_temp, get_temp_anomaly

from data_visualizer_extend import plot_temp_change_line, plot_hemisphere_bar, plot_temp_anomaly_heatmap

from report_generator import export_excel_results, generate_markdown_report

from data_visualizer import plot_monthly_temperature_2020_2025



def main():

    """主程序:完整实现趋势分析、多维度可视化、结果导出"""



    # 1. 配置参数

    RAW_DATA_PATH = '全球.xlsx'

    OUTPUT_DIR = 'output'

    TARGET_START_YEAR = 2020

    TARGET_END_YEAR = 2025



    # 2. 加载原始数据

    raw_df = load_excel_data(RAW_DATA_PATH)

    if raw_df is None:

        print("数据加载失败,程序终止")

        return



    # 3. 提取目标时间段数据

    extracted_df = extract_time_period(raw_df, TARGET_START_YEAR, TARGET_END_YEAR)

    if extracted_df is None:

        print("数据提取失败,程序终止")

        return

    save_extracted_data(extracted_df, OUTPUT_DIR, 'extracted_data_2020_2025_raw.xlsx')



    # 4. 基础数据分析

    analyze_extracted_data(extracted_df)



    # 5. 趋势统计分析(新增)

    annual_mean_df, trend_result = calculate_annual_change_rate(extracted_df)

    hemisphere_df = compare_hemisphere_temp(extracted_df)

    anomaly_df = get_temp_anomaly(extracted_df)

    print(f"趋势分析完成:2020-2025年温度总上升{trend_result['total_rise']},升温最显著年份{trend_result['max_rise_year']}年")



    # 6. 多维度可视化(新增)

    plot_temp_change_line(annual_mean_df, OUTPUT_DIR)  # 折线图

    plot_hemisphere_bar(hemisphere_df, OUTPUT_DIR)     # 柱状图

    plot_temp_anomaly_heatmap(anomaly_df, OUTPUT_DIR)  # 热力图

    plot_monthly_temperature_2020_2025(extracted_df, OUTPUT_DIR)  # 原有月度图



    # 7. 结果导出与报告生成(新增)

    export_excel_results(annual_mean_df, hemisphere_df, anomaly_df, OUTPUT_DIR)

    generate_markdown_report(trend_result, hemisphere_df, OUTPUT_DIR)



if __name__ == "__main__":

    main()

以下是绘制南北半球温度对比柱状图(每年)的代码

import pandas as pd

import matplotlib.pyplot as plt

import numpy as np



# 1. 基础配置

plt.switch_backend('Agg')

plt.rcParams['font.sans-serif'] = ['SimHei', 'WenQuanYi Zen Hei']

plt.rcParams['axes.unicode_minus'] = False



# 2. 加载数据

df_north = pd.read_excel('北半球.xlsx')

df_south = pd.read_excel('南半球.xlsx')



# 3. 数据对齐

df_merged = pd.merge(

    df_north[['年份', '全年平均']].rename(columns={'全年平均': '北半球温度'}),

    df_south[['年份', '全年平均']].rename(columns={'全年平均': '南半球温度'}),

    on='年份',

    how='inner'

).sort_values('年份')



# 4. 绘制对比柱状图

x = np.arange(len(df_merged))

width = 0.35



fig, ax = plt.subplots(figsize=(12, 6))

bars1 = ax.bar(x - width/2, df_merged['北半球温度'], width, label='北半球', color='#FF6B6B', alpha=0.8)

bars2 = ax.bar(x + width/2, df_merged['南半球温度'], width, label='南半球', color='#4ECDC4', alpha=0.8)



# 图表样式

ax.set_xlabel('年份', fontsize=11)

ax.set_ylabel('全年平均温度 (℃)', fontsize=11)

ax.set_title('南北半球年度全年平均温度对比', fontsize=13, fontweight='bold')

ax.set_xticks(x)

ax.set_xticklabels(df_merged['年份'], rotation=45)

ax.legend()



# 优化:数值标签显示在柱子内部(避免重叠)

def add_labels_inside(bars, color='white'):

    for bar in bars:

        height = bar.get_height()

        ax.text(bar.get_x() + bar.get_width()/2., height/2,  # 标签位置在柱子中间

                f'{height:.1f}', ha='center', va='center', fontsize=9, color=color, fontweight='bold')



add_labels_inside(bars1, color='white')  # 北半球柱子内白色标签

add_labels_inside(bars2, color='black')  # 南半球柱子内黑色标签(对比更清晰)



# 5. 保存图表

plt.tight_layout()

plt.savefig('南北半球年度温度对比图_优化版.png', dpi=300)

plt.close()

建议

1.气候监测:重点关注2023年类似的快速升温时段,加强气象数据实时监测

2.区域适配:针对北半球温度波动更大的特征,制定差异化气候应对策略

3.长期预警:基于总上升幅度28.00的趋势,建立长期温度预警机制

4.数据完善:建议补充南北半球直接观测数据,提升差异分析准确性

项目二:电商平台商品评论情感分析与热销款预测工具

项目背景

电商平台商品评论蕴含用户偏好与产品质量反馈,通过情感分析可挖掘用户满意度,结合销量数据还能预测热销款,为商家运营提供决策支持。本项目基于公开电商评论数据集,开发分析工具,贴合应用统计学专业 “数据分析与预测” 的实践需求。

核心功能要求

1.     数据获取与预处理:从公开数据源(如 Kaggle 平台 “Amazon Product Reviews” 数据集、天池平台 “淘宝商品评论数据集”)下载数据,包含 “商品 ID、商品类别、用户评论内容、评分(1-5 星)、评论时间、商品销量” 等字段。对评论内容进行预处理:去除特殊符号、停用词(如 “的”“了”),使用 jieba 库进行分词;将评分映射为情感标签(5 星 = 正面、3-4 星 = 中性、1-2 星 = 负面)。

2.     情感分析与统计:采用 “评分映射 + 关键词匹配” 结合的方式,统计每类商品的正面评论率(正面评论数 / 总评论数)、负面评论关键词 TOP10(如 “质量差”“物流慢”);对比不同类别商品的情感倾向差异(如 “电子产品正面评论率 82%,服装类 75%”)。

3.     热销款预测与可视化:以 “商品月销量” 为目标变量,“正面评论率、评论数量、商品价格” 为特征变量,使用线性回归模型(sklearn 库)构建热销款预测模型,预测下月可能成为热销款的商品(销量排名前 10%);绘制 “商品正面评论率与销量散点图”(标注预测热销款)、“不同类别商品销量对比饼图”。

4.     结果应用:将情感分析结果(如各商品负面关键词表)、热销款预测名单导出为 Excel 文件;生成《电商商品评论分析与热销款预测报告》,为商家提供建议(如 “针对‘物流慢’负面评论,优化快递合作商”“重点备货预测热销的 3 款电子产品”)。

代码:

import pandas as pd

import jieba

from collections import Counter



df = pd.read_excel('商品销量评论数据.xlsx')



def get_sentiment(score):

    if score == 5:

        return '正面'

    elif 3 <= score <= 4:

        return '中性'

    else:

        return '负面'

df['情感标签'] = df['评分(1-5星)'].apply(get_sentiment)



# 正面评论率统计

category_sentiment = df.groupby('商品类别')['情感标签'].value_counts().unstack(fill_value=0)

category_sentiment['总评论数'] = category_sentiment.sum(axis=1)

category_sentiment['正面评论率'] = (category_sentiment['正面'] / category_sentiment['总评论数']).round(4) * 100

print("\n=== 各类商品正面评论率 ===")

print(category_sentiment[['正面', '总评论数', '正面评论率']].astype({'正面': int, '总评论数': int}))



# 负面评论关键词TOP10

stop_words = {'的', '了', '是', '在', '我', '有', '和', '就', '不', '人', '都', '一', '这个', '很', '也', '还', '但'}

negative_comments = df[df['情感标签'] == '负面']['用户评论内容'].str.cat(sep=' ')

words = [w for w in jieba.lcut(negative_comments) if w not in stop_words and len(w) >= 2]

negative_top10 = Counter(words).most_common(10)

print("\n=== 负面评论关键词TOP10 ===")

for i, (word, count) in enumerate(negative_top10, 1):

    print(f"{i}. {word}: {count}次")



# 商品情感倾向对比

print("\n=== 商品情感倾向差异对比 ===")

for category in category_sentiment.index:

    rate = category_sentiment.loc[category, '正面评论率']

    print(f"{category}正面评论率{rate:.1f}%")

import pandas as pd
import numpy as np
import matplotlib
matplotlib.use('Agg')  # 使用非交互式后端,避免GUI问题
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score
from sklearn.preprocessing import StandardScaler

# 中文字体设置
plt.rcParams['font.sans-serif'] = ['WenQuanYi Zen Hei', 'SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False
np.random.seed(42)  # 固定随机种子,结果可复现

df = pd.read_excel('商品销量评论数据.xlsx')

sales_data = df.groupby('商品ID')['商品销量'].mean().reset_index(name='商品销量')
# 特征1:评论数量(每个商品的评论总数)
sales_data['评论数量'] = df.groupby('商品ID').size().values
# 特征2:正面评论率(5星为正面,计算占比)
positive_count = df[df['评分(1-5星)'] == 5].groupby('商品ID').size()
sales_data['正面评论率'] = (positive_count.reindex(sales_data['商品ID'], fill_value=0) / sales_data['评论数量']).round(4)
# 特征3:商品价格(模拟合理价格,可替换为真实数据)
sales_data['商品价格'] = np.random.uniform(30, 1800, len(sales_data)).round(2)

sales_data['正面评论率'] = sales_data['正面评论率'].fillna(0)

sales_data['平均评分'] = df.groupby('商品ID')['评分(1-5星)'].mean().values

sales_data['评分标准差'] = df.groupby('商品ID')['评分(1-5星)'].std().fillna(0).values

sales_data['价格销量比'] = sales_data['商品价格'] / (sales_data['商品销量'] + 1)  # +1避免除零

print("数据基本信息:")
print(f"总商品数:{len(sales_data)}")
print(f"特征数:{len(sales_data.columns) - 2}")  # 减去商品ID和商品销量

# 2. 线性回归建模与热销款预测
X = sales_data[['正面评论率', '评论数量', '商品价格', '平均评分', '评分标准差', '价格销量比']]
y = sales_data['商品销量']

# 数据标准化(提高模型稳定性)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 检查是否有NaN值
print("\n检查NaN值:")
for col in X.columns:
    nan_count = X[col].isna().sum()
    if nan_count > 0:
        print(f"{col}: {nan_count}个NaN值,使用均值填充")
        X[col] = X[col].fillna(X[col].mean())

# 划分训练集/测试集
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2, random_state=42)

# 训练模型并预测
lr = LinearRegression()
lr.fit(X_train, y_train)

# 全量商品预测销量
X_full_scaled = scaler.transform(X)
sales_data['预测销量'] = lr.predict(X_full_scaled)

# 确保预测销量非负
sales_data['预测销量'] = sales_data['预测销量'].clip(lower=0)

# 筛选热销款(预测销量前10%)
hot_threshold = sales_data['预测销量'].quantile(0.9)
sales_data['是否热销款'] = sales_data['预测销量'] >= hot_threshold
hot_count = sales_data['是否热销款'].sum()

# 模型评估
y_pred_test = lr.predict(X_test)
r2 = r2_score(y_test, y_pred_test)

print(f'\n=== 模型评估结果 ===')
print(f'模型R²评分:{r2:.4f}')
print(f'预测下月热销款数量:{hot_count}(总商品数:{len(sales_data)})')

# 显示模型系数
print(f'\n=== 特征重要性(标准化后)===')
feature_names = ['正面评论率', '评论数量', '商品价格', '平均评分', '评分标准差', '价格销量比']
for feature, coef in zip(feature_names, lr.coef_):
    importance = f'正向影响' if coef > 0 else f'负向影响'
    print(f'{feature:10s} 系数: {coef:8.4f} ({importance})')
print(f'截距: {lr.intercept_:.4f}')

# 3. 绘制「正面评论率与销量散点图」(单独保存)
plt.figure(figsize=(12, 8))
# 非热销款
plt.scatter(
    sales_data[sales_data['是否热销款'] == False]['正面评论率'],
    sales_data[sales_data['是否热销款'] == False]['商品销量'],
    c='#87CEEB', label=f'非热销款 ({len(sales_data) - hot_count}个)', alpha=0.6, s=50
)
# 预测热销款(突出显示)
plt.scatter(
    sales_data[sales_data['是否热销款'] == True]['正面评论率'],
    sales_data[sales_data['是否热销款'] == True]['商品销量'],
    c='#FF4500', label=f'预测热销款 ({hot_count}个)', alpha=0.9, s=150, marker='*', edgecolors='black', linewidth=1
)
plt.xlabel('正面评论率', fontsize=12)
plt.ylabel('商品销量', fontsize=12)
plt.title(f'商品正面评论率与销量关系\n模型R²评分: {r2:.4f}', fontsize=14, pad=20)
plt.legend(fontsize=10, loc='upper right')
plt.grid(alpha=0.3, linestyle='--')
plt.tight_layout()
plt.savefig('正面评论率与销量散点图.png', dpi=300, bbox_inches='tight')
plt.close()

# 4. 绘制「不同类别商品销量对比饼图」(单独保存)
# 计算各类别总销量
category_total_sales = df.groupby('商品类别')['商品销量'].sum().sort_values(ascending=False)

plt.figure(figsize=(12, 8))
# 饼图样式设置
wedges, texts, autotexts = plt.pie(
    category_total_sales.values,
    labels=category_total_sales.index,
    autopct='%1.1f%%',  # 显示百分比
    startangle=90,
    colors=plt.cm.Set3(np.linspace(0, 1, len(category_total_sales))),
    textprops={'fontsize': 10},
    explode=[0.05 if i < 3 else 0 for i in range(len(category_total_sales))]  # 突出前3名
)
# 优化百分比文字颜色
for autotext in autotexts:
    autotext.set_color('black')
    autotext.set_fontweight('bold')
    autotext.set_fontsize(9)

plt.title('不同类别商品销量占比', fontsize=14, pad=20)
plt.axis('equal')  # 保证饼图为正圆形
plt.tight_layout()
plt.savefig('不同类别商品销量饼图.png', dpi=300, bbox_inches='tight')
plt.close()

# 5. 新增:热销款特征分布图
plt.figure(figsize=(14, 10))
features_to_plot = ['正面评论率', '评论数量', '平均评分', '商品价格']

for i, feature in enumerate(features_to_plot, 1):
    plt.subplot(2, 2, i)

    # 热销款
    hot_values = sales_data[sales_data['是否热销款'] == True][feature]
    # 非热销款
    non_hot_values = sales_data[sales_data['是否热销款'] == False][feature]

    plt.hist(non_hot_values, bins=20, alpha=0.6, color='#87CEEB', label='非热销款', density=True)
    plt.hist(hot_values, bins=10, alpha=0.8, color='#FF4500', label='热销款', density=True)

    plt.xlabel(feature, fontsize=10)
    plt.ylabel('密度', fontsize=10)
    plt.title(f'{feature}分布对比', fontsize=11)
    plt.legend(fontsize=9)
    plt.grid(alpha=0.3, linestyle=':')

plt.suptitle('热销款与非热销款特征分布对比', fontsize=14, y=1.02)
plt.tight_layout()
plt.savefig('热销款特征分布图.png', dpi=300, bbox_inches='tight')
plt.close()

print('\n=== 热销款分析 ===')
hot_items = sales_data[sales_data['是否热销款'] == True].sort_values('预测销量', ascending=False)
print(f"热销款平均正面评论率: {hot_items['正面评论率'].mean():.3f}")
print(f"热销款平均评论数量: {hot_items['评论数量'].mean():.1f}")
print(f"热销款平均评分: {hot_items['平均评分'].mean():.2f}")
print(f"热销款平均价格: ¥{hot_items['商品价格'].mean():.2f}")

# 保存预测结果到Excel
sales_data.to_excel('商品热销预测结果.xlsx', index=False)
print('预测结果已保存到:商品热销预测结果.xlsx')

不同类别商品销量对比饼图

项目三:城市空气质量数据统计与健康建议系统

项目背景

城市空气质量(如 PM2.5、PM10 浓度)与居民健康密切相关,实时统计与分析空气质量数据,能为居民出行提供健康建议。本项目基于公开的城市空气质量数据集,开发分析系统,贴合基层环境监测与健康管理的实际需求。

核心功能要求

1.     数据获取与预处理:从公开数据源(如中国空气质量在线监测分析平台https://www.aqistudy.cn/提供的 “城市空气质量历史数据”、Kaggle “Beijing Air Quality Data” 数据集)下载 CSV 格式数据,包含 “日期、城市名称、PM2.5 浓度、PM10 浓度、SO₂浓度、AQI 指数、空气质量等级(优 / 良 / 轻度污染等)” 字段。清洗数据:剔除 AQI 指数为 0 的异常值、填充缺失的 PM2.5 数据(用同日期周边城市均值填充),提取 “2023 年某省份 10 个城市” 的数据进行分析。

2.     空气质量统计分析:计算 2023 年各城市的 “PM2.5 年均浓度”“优良天数占比(优良天数 / 全年天数)”“轻度及以上污染天数”;找出空气质量最优(优良天数占比最高)与最差(污染天数最多)的城市;分析季节性差异(如冬季 PM2.5 浓度是否显著高于夏季)。

3.     健康建议与可视化:根据 AQI 指数制定健康建议规则(如 AQI<50:适宜户外活动;AQI 151-200:敏感人群减少外出);绘制 “2023 年各城市 PM2.5 年均浓度柱状图”“某城市四季 AQI 指数变化折线图”“空气质量等级分布饼图(全省)”,图表标注对应健康建议。

4.     结果输出:将各城市空气质量统计数据导出为 Excel,生成《某省份 2023 年空气质量分析与健康建议报告》(Word 格式),包含数据来源、统计结论及分城市健康出行建议。

项目四:电影票房数据统计与类型偏好分析工具

项目背景

电影票房数据反映市场热度与观众偏好,分析票房与电影类型、上映时间的关联性,能为影视行业从业者提供参考。本项目基于公开电影票房数据集,开发分析工具,贴合数据统计与市场分析的实际场景。

核心功能要求

1.     数据获取与预处理:从公开数据源(如猫眼专业版https://piaofang.maoyan.com/提供的 “年度电影票房数据”、Kaggle “Movie Box Office Dataset” 数据集)下载 Excel/CSV 数据,包含 “电影名称、类型(如喜剧 / 动作 / 科幻)、上映日期、总票房、场均人次、评分(豆瓣 / 猫眼)” 等字段。清洗数据:统一电影类型格式(如 “喜剧片”“喜剧” 合并为 “喜剧”)、提取上映月份(用于分析档期影响)、剔除票房为 0 的未上映电影数据。

2.     票房统计与类型分析:计算 2023 年各类型电影的 “总票房占比”“平均票房”“最高票房电影”;分析档期对票房的影响(如春节档、国庆档电影的平均票房是否高于非档期);找出评分与票房的相关性(如评分≥8.0 的电影平均票房是否是评分<6.0 的 2 倍以上)。

3.     可视化与趋势呈现:绘制 “2023 年各类型电影总票房饼图”“春节档 vs 非档期电影平均票房柱状图”“电影评分与总票房散点图”(标注高评分高票房电影);通过可视化直观展示类型偏好与票房规律。

4.     结果导出与建议:将各类型电影统计数据(如平均票房、最高票房)导出为 Excel,生成《2023 年电影票房与类型偏好分析报告》,为影视制作方提供建议(如 “喜剧类型电影市场接受度高,可加大创作投入”“春节档是票房黄金档期,建议重点影片选择该档期上映”)。

在该网站下载数据:

https://www.kaggle.com/code/shivamb/netflix-shows-and-movies-exploratory-analysis

import pandas as pd
import numpy as np

# 1. 加载数据+预处理(同目录下文件)
df = pd.read_csv('shuju_with_aqi.csv')
df['date'] = pd.to_datetime(df[['year', 'month', 'day']])
df_2016 = df[df['year'] == 2016].copy()

# 2. 日级聚合(核心计算)
daily = df_2016.groupby('date').agg({
    'PM2.5': 'mean',  # PM2.5日均
    'AQI': 'mean'     # AQI日均
}).reset_index()

# 3. 空气质量等级映射
daily['等级'] = daily['AQI'].apply(
    lambda x: '优' if x<=50 else '良' if x<=100 else '轻度污染' if x<=150
    else '中度污染' if x<=200 else '重度污染' if x<=300 else '严重污染'
)

# 4. 2016年核心指标
pm25_annual = daily['PM2.5'].mean()  # PM2.5年均浓度
excellent_days = daily[daily['等级'].isin(['优', '良'])].shape[0]  # 优良天数
pollution_days = daily[~daily['等级'].isin(['优', '良'])].shape[0]  # 污染天数
excellent_ratio = (excellent_days / len(daily)) * 100  # 优良天数占比

# 5. 极值日期
best_day = daily[daily['等级'].isin(['优', '良'])].sort_values('AQI').iloc[0]  # 最优日
worst_day = daily[~daily['等级'].isin(['优', '良'])].sort_values('AQI', ascending=False).iloc[0]  # 最差日

# 6. 季节性差异(按季度分组)
daily['季度'] = daily['date'].dt.month.apply(
    lambda x: '春季' if x in [1,2,3] else '夏季' if x in [4,5,6] else '秋季' if x in [7,8,9] else '冬季'
)
seasonal_pm25 = daily.groupby('季度')['PM2.5'].mean().reindex(['春季', '夏季', '秋季', '冬季'])

# 7. 结果输出
print("=== 2016年空气质量核心指标 ===")
print(f"1. PM2.5年均浓度:{pm25_annual:.2f} μg/m³")
print(f"2. 优良天数:{excellent_days} 天,占比:{excellent_ratio:.1f}%")
print(f"3. 轻度及以上污染天数:{pollution_days} 天\n")

print("=== 极值日期 ===")
print(f"最优日:{best_day['date'].strftime('%Y-%m-%d')},PM2.5:{best_day['PM2.5']:.2f} μg/m³,等级:{best_day['等级']}")
print(f"最差日:{worst_day['date'].strftime('%Y-%m-%d')},PM2.5:{worst_day['PM2.5']:.2f} μg/m³,等级:{worst_day['等级']}\n")

print("=== 季节性PM2.5浓度 ===")
for season, pm25 in seasonal_pm25.items():
    print(f"{season}:{pm25:.2f} μg/m³")
print(f"\n结论:冬季PM2.5({seasonal_pm25['冬季']:.2f})显著高于夏季({seasonal_pm25['夏季']:.2f}),差值:{seasonal_pm25['冬季']-seasonal_pm25['夏季']:.2f} μg/m³")

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
plt.switch_backend('Agg')

# 1. 基础配置(解决后端+中文问题)
plt.switch_backend('Agg')
plt.rcParams['font.sans-serif'] = ['SimHei', 'WenQuanYi Zen Hei']
plt.rcParams['axes.unicode_minus'] = False

# 2. 加载数据(同目录下的shuju_with_aqi.csv)
df = pd.read_csv('shuju_with_aqi.csv')
df['date'] = pd.to_datetime(df[['year', 'month', 'day']])
df = df[(df['year'] >= 2013) & (df['year'] <= 2017)]  # 筛选2013-2017年

# 3. 数据聚合
# 3.1 PM2.5年均浓度(用于图1)
annual_pm25 = df.groupby('year')['PM2.5'].mean().reset_index()

# 3.2 2013-2017年每季AQI(用于图2)
df['季度'] = df['month'].apply(lambda x: ['春季', '夏季', '秋季', '冬季'][(x-1)//3])
df['年季'] = df['year'].astype(str) + '-' + df['季度']
season_order = [f'{y}-{s}' for y in range(2013,2018) for s in ['春季','夏季','秋季','冬季']]
seasonal_aqi = df.groupby('年季')['AQI'].mean()
seasonal_aqi = seasonal_aqi.reindex(season_order)  # 按时间排序

# 4. 图1:PM2.5年均浓度柱状图(单独保存)
plt.figure(figsize=(10, 5))
plt.bar(annual_pm25['year'], annual_pm25['PM2.5'], color='#FF6B6B', alpha=0.8)
plt.xlabel('年份', fontsize=11)
plt.ylabel('PM2.5浓度 (μg/m³)', fontsize=11)
plt.title('2013-2017年PM2.5年均浓度', fontsize=12, fontweight='bold')
plt.grid(axis='y', alpha=0.3)
# 添加数值标签
for x, y in zip(annual_pm25['year'], annual_pm25['PM2.5']):
    plt.text(x, y+2, f'{y:.1f}', ha='center', fontsize=10)
plt.tight_layout()
plt.savefig('2013-2017_PM25年均浓度图.png', dpi=300)
plt.close()

# 5. 图2:2013-2017年四季AQI折线图(单独保存)
plt.figure(figsize=(12, 5))
plt.plot(seasonal_aqi.index, seasonal_aqi.values, marker='o', linewidth=2,
         markersize=5, color='#4ECDC4', markerfacecolor='#FFD700')
plt.xlabel('年份-季度', fontsize=11)
plt.ylabel('AQI均值', fontsize=11)
plt.title('2013-2017年各季度AQI变化', fontsize=12, fontweight='bold')
plt.xticks(rotation=45)  # 旋转x轴标签防重叠
plt.grid(alpha=0.3)
# 每年春季标数值(避免拥挤)
for i, (x, y) in enumerate(seasonal_aqi.items()):
    if i % 4 == 0:
        plt.text(x, y+3, f'{y:.1f}', ha='center', fontsize=9)
plt.tight_layout()
plt.savefig('2013-2017_四季AQI变化图.png', dpi=300)
plt.close()

# 6. 结果提示
print("✅ AQI健康建议规则已生成(见上文表格)")
print("✅ 两张图表已分别保存:")
print("   1. 2013-2017_PM25年均浓度图.png")
print("   2. 2013-2017_四季AQI变化图.png")

各类型电影总票房饼图

春节档vs非档期电影平均票房柱状图

电影评分与总票房散点图

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐