10-综合案例-数据可视化-折线图

Python数据可视化综合案例:折线图可视化全面指南

一、项目概述

1.1 案例背景与目标

本案例基于Python数据可视化技术,通过pyecharts库实现新冠疫情数据的可视化分析,主要包含三个核心可视化效果:

案例目标

  • 掌握使用pyecharts进行数据可视化的完整流程
  • 理解JSON数据格式及其在Python中的处理
  • 学会创建专业的折线图并进行个性化配置
  • 培养数据分析和可视化表达能力

1.2 技术栈介绍

核心技术

  • 数据格式:JSON
  • 可视化库:pyecharts
  • 数据来源:百度疫情实时大数据报告
  • 图表类型:折线图、地图、动态图表

案例效果展示

二、JSON数据格式详解

2.1 JSON基础概念

2.1.1 什么是JSON

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,采用完全独立于编程语言的文本格式来存储和表示数据。

JSON的特点

  • 易于阅读和编写
  • 易于机器解析和生成
  • 支持嵌套数据结构
  • 跨语言兼容性好

2.1.2 JSON数据结构

// 对象形式
{
    "name": "张三",
    "age": 25,
    "is_student": false,
    "hobbies": ["读书", "运动", "音乐"],
    "address": {
        "city": "北京",
        "district": "海淀区"
    }
}

// 数组形式
[
    {"name": "张三", "age": 25},
    {"name": "李四", "age": 30},
    {"name": "王五", "age": 28}
]

2.2 Python与JSON数据转换

2.2.1 基本转换方法

import json

# Python数据 → JSON字符串
python_data = {
    "name": "张三",
    "age": 25,
    "hobbies": ["读书", "运动"]
}

# 序列化:Python对象 → JSON字符串
json_str = json.dumps(python_data, ensure_ascii=False, indent=2)
print("JSON字符串:")
print(json_str)

# 反序列化:JSON字符串 → Python对象
python_obj = json.loads(json_str)
print("\nPython对象:")
print(python_obj)

2.2.2 文件读写操作

import json

# 写入JSON文件
data = {"name": "李四", "scores": [85, 92, 78]}
with open('data.json', 'w', encoding='utf-8') as f:
    json.dump(data, f, ensure_ascii=False, indent=2)

# 读取JSON文件
with open('data.json', 'r', encoding='utf-8') as f:
    loaded_data = json.load(f)
    print("从文件读取的数据:", loaded_data)

2.2.3 处理复杂JSON数据

import json

# 复杂的疫情数据示例
epidemic_data = {
    "status": 0,
    "msg": "success", 
    "data": {
        "name": "日本",
        "trend": {
            "updateDate": ["1.20", "1.21", "1.22", "1.23"],
            "list": [
                {
                    "name": "确诊",
                    "data": [10, 15, 20, 25]
                },
                {
                    "name": "治愈", 
                    "data": [2, 3, 5, 8]
                }
            ]
        }
    }
}

# 提取特定数据
trend_data = epidemic_data["data"]["trend"]
dates = trend_data["updateDate"]
confirmed_cases = trend_data["list"][0]["data"]
print("日期:", dates)
print("确诊人数:", confirmed_cases)

三、pyecharts模块详解

3.1 pyecharts简介

3.1.1 什么是pyecharts

pyecharts是百度开源的数据可视化库Echarts的Python接口,结合了Python的数据处理能力和Echarts的丰富图表类型。

优势特点

  • 丰富的图表类型:折线图、柱状图、饼图、地图等
  • 交互性强:支持鼠标悬停、缩放、拖拽等交互
  • 配置灵活:高度可定制的图表样式
  • 输出多样:支持HTML、图片等多种输出格式

3.1.2 安装与环境配置

# 基础安装
pip install pyecharts

# 安装地图扩展(如果需要地图功能)
pip install echarts-countries-pypkg    # 世界地图
pip install echarts-china-provinces-pypkg  # 中国省份地图
pip install echarts-china-cities-pypkg     # 中国城市地图

3.2 官方资源与学习

四、pyecharts快速入门

4.1 基础折线图创建

4.1.1 最简单的折线图

from pyecharts.charts import Line

# 创建折线图对象
line = Line()

# 添加X轴数据(分类数据)
line.add_xaxis(["1月", "2月", "3月", "4月", "5月"])

# 添加Y轴数据(数值数据)
line.add_yaxis("销售额", [100, 200, 150, 300, 250])

# 生成HTML文件
line.render("basic_line.html")

4.1.2 多系列折线图

from pyecharts.charts import Line

line = Line()

# X轴数据
months = ["1月", "2月", "3月", "4月", "5月", "6月"]

# 添加多个数据系列
line.add_xaxis(months)
line.add_yaxis("产品A", [120, 132, 101, 134, 90, 230])
line.add_yaxis("产品B", [150, 182, 191, 234, 290, 330])
line.add_yaxis("产品C", [320, 332, 301, 334, 390, 330])

# 渲染图表
line.render("multi_series_line.html")

4.2 全局配置选项

4.2.1 标题配置

from pyecharts import options as opts
from pyecharts.charts import Line

line = Line()

# 基础数据
line.add_xaxis(["1月", "2月", "3月", "4月"])
line.add_yaxis("销量", [100, 200, 150, 300])

# 全局配置
line.set_global_opts(
    title_opts=opts.TitleOpts(
        title="2023年销售趋势",      # 主标题
        subtitle="单位:万元",        # 副标题
        pos_left="center",           # 标题位置
        title_textstyle_opts=opts.TextStyleOpts(color="#333", font_size=16)
    )
)

line.render("title_config.html")

4.2.2 坐标轴配置

line.set_global_opts(
    # X轴配置
    xaxis_opts=opts.AxisOpts(
        name="时间",                  # 轴名称
        name_location="end",          # 名称位置
        name_gap=20,                 # 名称与轴线距离
        axislabel_opts=opts.LabelOpts(rotate=45)  # 标签旋转
    ),
    
    # Y轴配置  
    yaxis_opts=opts.AxisOpts(
        name="销量",
        name_location="end",
        name_gap=30,
        min_=0,                      # 最小值
        max_=400                     # 最大值
    )
)

4.2.3 图例与工具箱

line.set_global_opts(
    # 图例配置
    legend_opts=opts.LegendOpts(
        is_show=True,                 # 是否显示图例
        pos_left="right",            # 图例位置
        orient="vertical",           # 排列方向
        textstyle_opts=opts.TextStyleOpts(font_size=12)
    ),
    
    # 工具箱配置
    toolbox_opts=opts.ToolboxOpts(
        is_show=True,                # 显示工具箱
        feature={
            "saveAsImage": {},          # 保存图片
            "restore": {},             # 还原
            "dataView": {},            # 数据视图
            "dataZoom": {},            # 数据缩放
            "magicType": {"type": ["line", "bar"]}  # 图表类型切换
        }
    )
)

五、数据处理实战

5.1 疫情数据预处理

5.1.1 原始数据格式分析

// 原始数据格式(包含JSONP包装)
jsonp_1629350871167_29498({
    "status": 0,
    "msg": "success", 
    "data": {
        "name": "日本",
        "trend": {
            "updateDate": ["1.20", "1.21", "1.22", "1.23"],
            "list": [
                {
                    "name": "确诊",
                    "data": [10, 15, 20, 25]
                }
            ]
        }
    }
})

5.1.2 数据清洗与提取

import json
import re

def process_epidemic_data(raw_data):
    """
    处理疫情原始数据,提取结构化信息
    
    :param raw_data: 原始数据字符串
    :return: 处理后的数据字典
    """
    try:
        # 1. 移除JSONP包装
        # 匹配 jsonp_xxx(...) 格式
        pattern = r'^[a-zA-Z_][a-zA-Z0-9_]*\('
        cleaned_data = re.sub(pattern, '', raw_data)
        cleaned_data = cleaned_data.rstrip(');')
        
        # 2. 解析JSON数据
        data_dict = json.loads(cleaned_data)
        
        # 3. 提取趋势数据
        trend_data = data_dict["data"]["trend"]
        
        # 4. 按年份筛选数据(2020年)
        dates = trend_data["updateDate"]
        confirmed_data = trend_data["list"][0]["data"]  # 确诊数据
        
        # 假设前314个数据点是2020年的
        dates_2020 = dates[:314]
        confirmed_2020 = confirmed_data[:314]
        
        return {
            "country": data_dict["data"]["name"],
            "dates": dates_2020,
            "confirmed": confirmed_2020
        }
        
    except (json.JSONDecodeError, KeyError, IndexError) as e:
        print(f"数据处理错误: {e}")
        return None

# 使用示例
raw_data = 'jsonp_1629350871167_29498({"status":0,"msg":"success","data":{"name":"日本","trend":{"updateDate":["1.20","1.21"],"list":[{"name":"确诊","data":[10,15]}]}}})'
processed_data = process_epidemic_data(raw_data)
print(processed_data)

5.2 多国数据整合

def load_multiple_countries_data():
    """
    加载并整合多国疫情数据
    """
    countries_data = {}
    
    # 模拟多个国家的数据处理
    countries = ["美国", "印度", "日本"]
    
    for country in countries:
        # 这里应该是从实际数据源加载数据
        # 模拟数据
        if country == "美国":
            dates = [f"2020.{i+1:02d}.{j+1:02d}" for i in range(12) for j in range(26)]
            confirmed = [i*1000 + j*100 for i in range(12) for j in range(26)]
        elif country == "印度":
            dates = [f"2020.{i+1:02d}.{j+1:02d}" for i in range(12) for j in range(26)]
            confirmed = [i*800 + j*80 for i in range(12) for j in range(26)]
        else:  # 日本
            dates = [f"2020.{i+1:02d}.{j+1:02d}" for i in range(12) for j in range(26)]
            confirmed = [i*600 + j*60 for i in range(12) for j in range(26)]
        
        countries_data[country] = {
            "dates": dates[:314],  # 取2020年数据
            "confirmed": confirmed[:314]
        }
    
    return countries_data

# 加载数据
epidemic_data = load_multiple_countries_data()
print("加载的国家:", list(epidemic_data.keys()))

六、创建专业折线图

6.1 完整疫情折线图实现

6.1.1 基础图表结构

from pyecharts.charts import Line
from pyecharts import options as opts
from pyecharts.commons.utils import JsCode

def create_epidemic_chart(epidemic_data):
    """
    创建疫情数据折线图
    
    :param epidemic_data: 处理后的疫情数据
    :return: 折线图对象
    """
    # 初始化图表
    line = Line(
        init_opts=opts.InitOpts(
            width="1400px",
            height="700px",
            bg_color="#f5f5f5"
        )
    )
    
    return line

6.1.2 添加数据系列

def add_data_series(line, epidemic_data):
    """添加数据系列到图表"""
    
    # 使用第一个国家的日期作为X轴(假设所有国家日期相同)
    first_country = list(epidemic_data.keys())[0]
    dates = epidemic_data[first_country]["dates"]
    
    # 添加X轴数据
    line.add_xaxis(dates)
    
    # 定义颜色方案
    colors = ["#c23531", "#2f4554", "#61a0a8", "#d48265"]
    
    # 为每个国家添加数据系列
    for i, (country, data) in enumerate(epidemic_data.items()):
        line.add_yaxis(
            series_name=f"{country}确诊人数",
            y_axis=data["confirmed"],
            is_smooth=True,  # 平滑曲线
            symbol="circle",  # 数据点形状
            symbol_size=6,    # 数据点大小
            color=colors[i % len(colors)],
            
            # 线条样式
            linestyle_opts=opts.LineStyleOpts(width=3),
            
            # 标签样式
            label_opts=opts.LabelOpts(
                is_show=False,  # 默认不显示数据标签
                position="top",
                font_size=10
            ),
            
            # 区域填充
            areastyle_opts=opts.AreaStyleOpts(
                opacity=0.1,  # 填充透明度
                color=JsCode(f"""
                    function(params) {{
                        return '{colors[i % len(colors)]}';
                    }}
                """)
            )
        )
    
    return line

6.1.3 完整图表配置

def configure_chart(line):
    """配置图表全局选项"""
    
    line.set_global_opts(
        # 标题配置
        title_opts=opts.TitleOpts(
            title="2020年新冠疫情累计确诊人数趋势",
            subtitle="数据来源:百度疫情实时大数据报告",
            pos_left="center",
            title_textstyle_opts=opts.TextStyleOpts(
                color="#2c3e50",
                font_size=20,
                font_weight="bold"
            ),
            subtitle_textstyle_opts=opts.TextStyleOpts(
                color="#7f8c8d", 
                font_size=12
            )
        ),
        
        # 图例配置
        legend_opts=opts.LegendOpts(
            is_show=True,
            pos_left="10%",
            pos_top="10%",
            orient="horizontal",
            textstyle_opts=opts.TextStyleOpts(font_size=12)
        ),
        
        # X轴配置
        xaxis_opts=opts.AxisOpts(
            name="时间",
            name_location="end",
            type_="category",
            axislabel_opts=opts.LabelOpts(
                rotate=45,
                font_size=10,
                interval=30  # 间隔显示标签
            ),
            axiline_opts=opts.AxisLineOpts(
                linestyle_opts=opts.LineStyleOpts(color="#bdc3c7")
            )
        ),
        
        # Y轴配置
        yaxis_opts=opts.AxisOpts(
            name="累计确诊人数",
            name_location="end",
            type_="value",
            name_gap=30,
            axislabel_opts=opts.LabelOpts(
                font_size=10,
                formatter="{value} 人"
            ),
            splitline_opts=opts.SplitLineOpts(
                is_show=True,
                linestyle_opts=opts.LineStyleOpts(
                    color="#ecf0f1",
                    type_="dashed"
                )
            )
        ),
        
        # 工具箱配置
        toolbox_opts=opts.ToolboxOpts(
            is_show=True,
            pos_left="right",
            feature={
                "saveAsImage": {
                    "title": "保存为图片",
                    "pixel_ratio": 2
                },
                "restore": {"title": "还原"},
                "dataView": {
                    "title": "数据视图",
                    "lang": ["数据视图", "关闭", "刷新"]
                },
                "dataZoom": {
                    "title": {"zoom": "区域缩放", "back": "缩放还原"}
                },
                "magicType": {
                    "type": ["line", "bar"],
                    "title": {"line": "折线图", "bar": "柱状图"}
                }
            }
        ),
        
        # 数据区域缩放
        datazoom_opts=[
            opts.DataZoomOpts(
                type_="inside",  # 内置型数据区域缩放
                xaxis_index=[0],  # 控制第一个x轴
                range_start=0,
                range_end=100
            ),
            opts.DataZoomOpts(
                type_="slider",  # 滑动条型数据区域缩放
                xaxis_index=[0],
                pos_bottom="5%",
                range_start=0, 
                range_end=100
            )
        ],
        
        # 提示框配置
        tooltip_opts=opts.TooltipOpts(
            trigger="axis",
            axispointer_opts=opts.AxisPointerOpts(
                type_="cross",
                label=opts.LabelOpts(background_color="#6a7985")
            ),
            background_color="rgba(0,0,0,0.7)",
            border_color="#ccc",
            textstyle_opts=opts.TextStyleOpts(color="#fff")
        )
    )
    
    return line

6.2 高级功能扩展

6.2.1 鼠标悬停效果增强

def enhance_tooltip(line):
    """增强提示框功能"""
    
    line.set_series_opts(
        tooltip_opts=opts.TooltipOpts(
            trigger="axis",
            formatter=JsCode("""
                function(params) {
                    let result = params[0].name + '<br/>';
                    params.forEach(function(item) {
                        // 格式化数字,添加千分位分隔符
                        let value = item.value.toLocaleString();
                        result += item.marker + ' ' + item.seriesName + ': ' + value + '人<br/>';
                    });
                    return result;
                }
            """)
        )
    )
    
    return line

6.2.2 响应式配置

def make_chart_responsive(line):
    """使图表响应式"""
    
    # 添加响应式JavaScript代码
    line.add_js_funcs("""
        // 窗口大小改变时重绘图表
        window.addEventListener('resize', function() {
            chart.resize();
        });
    """)
    
    return line

七、完整案例实现

7.1 主程序整合

import json
import re
from pyecharts.charts import Line
from pyecharts import options as opts
from pyecharts.commons.utils import JsCode

def main():
    """主函数:完整的疫情数据可视化案例"""
    
    print("开始处理疫情数据可视化...")
    
    # 1. 加载和预处理数据
    epidemic_data = load_multiple_countries_data()
    print("数据加载完成")
    
    # 2. 创建基础图表
    line_chart = create_epidemic_chart(epidemic_data)
    
    # 3. 添加数据系列
    line_chart = add_data_series(line_chart, epidemic_data)
    
    # 4. 配置图表
    line_chart = configure_chart(line_chart)
    line_chart = enhance_tooltip(line_chart)
    line_chart = make_chart_responsive(line_chart)
    
    # 5. 生成图表
    output_file = "2020_pandemic_trend.html"
    line_chart.render(output_file)
    print(f"图表已生成: {output_file}")
    
    return line_chart

def load_multiple_countries_data():
    """
    模拟加载多国疫情数据
    实际应用中应从API或文件加载真实数据
    """
    # 这里使用模拟数据代替真实数据加载
    countries_data = {}
    
    countries = ["美国", "印度", "日本"]
    
    for country in countries:
        # 生成模拟的日期和数据
        dates = []
        confirmed = []
        
        # 生成2020年的日期(简化版)
        for month in range(1, 13):
            for day in range(1, 27):  # 每月26天,简化处理
                dates.append(f"2020.{month:02d}.{day:02d}")
        
        # 生成模拟的确诊数据(指数增长趋势)
        base_cases = 100
        growth_rate = 1.05  # 日增长率5%
        
        current_cases = base_cases
        for _ in range(len(dates)):
            confirmed.append(int(current_cases))
            current_cases *= growth_rate
        
        countries_data[country] = {
            "dates": dates,
            "confirmed": confirmed
        }
    
    return countries_data

if __name__ == "__main__":
    # 运行主程序
    chart = main()
    
    # 在Jupyter Notebook中显示图表
    # chart.render_notebook()

7.2 效果优化与调试

7.2.1 性能优化

def optimize_performance(line):
    """图表性能优化"""
    
    # 对于大数据量,启用数据采样
    line.set_series_opts(
        large=True,  # 开启大数据模式
        large_threshold=2000  # 数据量阈值
    )
    
    return line

7.2.2 移动端适配

def mobile_optimization(line):
    """移动端优化"""
    
    line.set_global_opts(
        # 移动端触摸优化
        touch_opts=opts.TouchOpts(
            is_touch=True,
            is_prevent_default=False
        )
    )
    
    return line

八、扩展功能

8.1 数据导出功能

import pandas as pd
from pyecharts.components import Table
from pyecharts.charts import Page

def export_data_table(epidemic_data, filename="疫情数据汇总.html"):
    """导出数据表格"""
    
    # 创建数据表格
    table = Table()
    
    headers = ["日期"] + list(epidemic_data.keys())
    rows = []
    
    # 获取第一个国家的日期作为基准
    first_country = list(epidemic_data.keys())[0]
    dates = epidemic_data[first_country]["dates"]
    
    for i, date in enumerate(dates):
        row = [date]
        for country in epidemic_data.keys():
            if i < len(epidemic_data[country]["confirmed"]):
                row.append(str(epidemic_data[country]["confirmed"][i]))
            else:
                row.append("N/A")
        rows.append(row)
    
    table.add(headers, rows)
    
    # 创建页面,包含图表和表格
    page = Page(layout=Page.SimplePageLayout)
    page.add(
        create_epidemic_chart(epidemic_data),
        table
    )
    
    page.render(filename)
    print(f"数据报表已生成: {filename}")

# 使用示例
# export_data_table(epidemic_data)

8.2 自动化报告生成

def generate_automated_report(epidemic_data):
    """生成自动化分析报告"""
    
    # 计算统计信息
    stats = {}
    for country, data in epidemic_data.items():
        cases = data["confirmed"]
        stats[country] = {
            "最大确诊数": max(cases),
            "最终确诊数": cases[-1],
            "平均日增长": (cases[-1] - cases[0]) / len(cases)
        }
    
    # 生成分析文本
    analysis_text = "## 2020年新冠疫情分析报告\n\n"
    analysis_text += "### 各国疫情数据汇总\n\n"
    
    for country, stat in stats.items():
        analysis_text += f"**{country}**:\n"
        analysis_text += f"- 最高确诊: {stat['最大确诊数']:,} 人\n"
        analysis_text += f"- 年末确诊: {stat['最终确诊数']:,} 人\n"
        analysis_text += f"- 日均增长: {stat['平均日增长']:,.0f} 人/天\n\n"
    
    print(analysis_text)
    return analysis_text

# 生成报告
# report = generate_automated_report(epidemic_data)

九、最佳实践总结

posted @ 2026-01-23 14:54  FxorG  阅读(1)  评论(0)    收藏  举报