You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
report_app/app/tools/draw_picture.py

242 lines
8.7 KiB
Python

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# -*- coding: utf-8 -*-
import matplotlib
matplotlib.use("agg")
import matplotlib.pyplot as plt
import numpy as np
import os
from datetime import datetime
from matplotlib.font_manager import FontProperties
# # 数据
# data = {
# "停电用户\n万户": {"昨天": 200.87, "今天": 132.59},
# "过载配变\n": {"昨天": 126, "今天": 119},
# "95598供电类投诉\n": {"昨天": 18, "今天": 12},
# "涉电力供应类舆情风险信息\n": {"昨天": 79, "今天": 40}
# }
def plot_electricity_comparison(year, month, day, data):
year = int(year)
month = int(month)
day = int(day)
# # 设置中文字体
plt.rcParams["font.sans-serif"] = [
"Microsoft YaHei"
] # 字体设置,用来正常显示中文标签
plt.rcParams["axes.unicode_minus"] = False # 用来正常显示负号
# # 创建一个大图形1行4列的子图布局
# fig, axs = plt.subplots(1, 4, figsize=(8, 4)) # 1行4列的子图布局
# 定义横轴标签
categories = ["昨天", "今天"]
x = np.arange(len(categories))
# 计算变化百分比
def calculate_change_percentage(yesterday, today):
return ((today - yesterday) / yesterday) * 100
# 检查数据完整性并过滤掉不完整的数据
valid_data = {}
for title, values in data.items():
if "昨天" in values and "今天" in values:
if values["昨天"] is not None and values["今天"] is not None:
valid_data[title] = values
# 如果没有有效的数据,返回 None 或其他指示
if not valid_data:
return None # 没有有效的数据,不生成图片
# 根据有效数据的数量动态创建子图布局
num_valid_data = len(valid_data)
fig, axs = plt.subplots(
1, num_valid_data, figsize=(2 * num_valid_data, 4)
) # 动态调整布局
# 如果只有一个子图axs 是一个单个的 Axes 对象而不是数组,需要将其转换为列表
if num_valid_data == 1:
axs = [axs]
# 绘制每个子图
for i, (title, values) in enumerate(valid_data.items()):
ax = axs[i] # 获取当前子图
y = list(values.values())
# 将蓝色柱子改为暗蓝色
bars = ax.bar(x, y, color=["#1E3A8A", "#FF8C00"], width=0.6)
# 设置子图标题和坐标轴标签
ax.set_title(
title, fontsize=12, fontweight="bold", color="#00008B"
) # 设置标题字体加粗深蓝色
# 设置坐标轴标签字体加粗深蓝色
ax.set_xticks(x)
ax.set_xticklabels(categories, fontsize=10, fontweight="bold", color="#00008B")
# 动态设置纵坐标范围
max_y = max(y) * 1.2 # 增加20%的范围
ax.set_ylim(0, max_y)
# 隐藏纵轴刻度线
ax.tick_params(axis="y", length=0)
ax.tick_params(axis="x", length=0)
# 添加自定义的淡颜色细长分割线
for y_tick in ax.get_yticks():
ax.axhline(y=y_tick, color="#87CEEB", linestyle="--", alpha=0.3)
# 设置刻度标签字体加粗深蓝色
ax.tick_params(axis="y", labelsize=12, labelcolor="#00008B")
# 添加数据标签
for bar in bars:
height = bar.get_height()
# 根据柱子颜色设置数据标签颜色
if bar == bars[0]:
color = "#1E3A8A" # 暗蓝色
else:
color = "#FF8C00" # 暗橙色
ax.text(
bar.get_x() + bar.get_width() / 2,
height,
f"{height}",
ha="center",
va="bottom",
fontsize=10,
fontweight="bold",
color=color,
)
# 添加变化百分比和箭头
change_percent = calculate_change_percentage(y[0], y[1])
# 根据变化百分比设置符号和颜色
if change_percent < 0:
symbol = "\u25bc" # 倒三角
color = "#006400" # 深绿色
# 调整箭头起始点和终点位置:从柱子的边角开始指向边角
bar0_height = bars[0].get_height()
bar1_height = bars[1].get_height()
ax.annotate(
"",
xy=(x[1] - bars[1].get_width() / 2+0.1, bar1_height),
#xy=(x[1], bar1_height),
#xytext=((x[0] + bars[0].get_width() / 2) + 0.05, bar0_height * 0.95),
xytext=((x[0] + bars[0].get_width() / 2), bar0_height),
arrowprops=dict(
arrowstyle="-|>",
mutation_scale=20, # 箭头大小
connectionstyle="arc3,rad=-0.4", # 调整为负值,箭头凸起
color="#FFD580",
linewidth=3,
capstyle='round',
joinstyle='round'
),
) # 浅橙色箭头,加粗
# 在子图中间显示变化百分比
ax.text(
0.5,
0.9,
f"{symbol}{abs(change_percent):.2f}%",
ha="center",
va="center",
transform=ax.transAxes,
fontsize=12,
fontweight="bold",
color=color,
)
elif change_percent > 0:
symbol = "\u25b2" # 正三角
color = "#FF0000" # 红色
# 调整箭头起始点和终点位置:从柱子的边角开始指向边角
bar0_height = bars[0].get_height()
bar1_height = bars[1].get_height()
ax.annotate(
"",
#xy=(x[1] - bars[1].get_width() / 2, bar1_height),
xy=(x[1] - bars[1].get_width() / 2, bar1_height),
#xytext=((x[0] + bars[0].get_width() / 2) + 0.05, bar0_height),
xytext=((x[0] + bars[0].get_width() / 2), bar0_height),
arrowprops=dict(
arrowstyle="-|>",
mutation_scale=20, # 箭头大小
connectionstyle="arc3,rad=0.4", # 调整为负值,箭头凸起
color="#FFD580",
linewidth=3,
),
) # 浅橙色箭头,加粗
# 在子图中间显示变化百分比
ax.text(
0.5,
0.9,
f"{symbol}{abs(change_percent):.2f}%",
ha="center",
va="center",
transform=ax.transAxes,
fontsize=12,
fontweight="bold",
color=color,
)
else:
symbol = ""
color = "#FFA500" # 橙色
# 调整箭头起始点和终点位置:从柱子的边角开始指向边角
bar0_height = bars[0].get_height()
bar1_height = bars[1].get_height()
ax.annotate(
"",
xy=(x[1] - bars[1].get_width() / 2+0.1, bar1_height),
xytext=((x[0] + bars[0].get_width() / 2), bar0_height),
arrowprops=dict(
arrowstyle="-|>",
mutation_scale=20, # 箭头大小
connectionstyle="arc3,rad=0", # 调整为负值,箭头凸起
color="#FFD580",
linewidth=3,
),
) # 浅橙色箭头,加粗
# 在子图中间显示变化百分比
ax.text(
0.5,
0.9,
f"持平",
ha="center",
va="center",
transform=ax.transAxes,
fontsize=12,
fontweight="bold",
color=color,
)
# 调整子图间距
plt.subplots_adjust(wspace=0) # 进一步减小子图之间的水平间距
plt.tight_layout(rect=[0, 0, 1, 0.95]) # 调整整体布局
# 获取当前脚本的绝对路径
current_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(os.path.dirname(current_dir))
# 创建 temp_picture 目录
temp_picture_dir = os.path.join(project_root, "temp_picture")
if not os.path.exists(temp_picture_dir):
os.makedirs(temp_picture_dir)
# 按年月创建子目录
month_dir = os.path.join(temp_picture_dir, f"{year}{month:02d}")
if not os.path.exists(month_dir):
os.makedirs(month_dir)
# 保存图形到指定目录
file_name = f"电力供应数据变化对比{year}{month:02d}{day:02d}.png"
file_path = os.path.join(month_dir, file_name)
plt.savefig(file_path, dpi=1200, bbox_inches="tight")
return file_path
# # 显示图形
# plt.show()
# plot_electricity_comparison(data)