import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.ticker import FuncFormatter
from tkinter import Tk, filedialog, messagebox
# 使用Tkinter创建文件选择对话框并显示提示信息
def select_file(file_type):
root = Tk()
root.withdraw() # 隐藏主窗口
if file_type == 'csv':
messagebox.showinfo("选择CSV文件", "请选择监控数据的CSV文件")
file_path = filedialog.askopenfilename(title="选择CSV文件", filetypes=[("CSV files", "*.csv")])
elif file_type == 'excel':
messagebox.showinfo("选择Excel文件", "请选择主机数据的Excel文件")
file_path = filedialog.askopenfilename(title="选择Excel文件", filetypes=[("Excel files", "*.xls *.xlsx")])
root.destroy()
return file_path
# 选择文件路径
csv_file_path = select_file('csv')
xls_file_path = select_file('excel')
# 文件列名变量
csv_columns = {
'datetime': '数据时间',
'instance_id': '实例ID',
'monitor_value': '监控值'
}
xls_columns = {
'name': '名称',
'instance_id': '云主机ID'
}
# 读取CSV文件,尝试不同的编码
try:
data = pd.read_csv(csv_file_path, encoding='utf-8')
except UnicodeDecodeError:
data = pd.read_csv(csv_file_path, encoding='gbk')
# 读取Excel主机列表文件,并重命名列名以匹配
host_list = pd.read_excel(xls_file_path, usecols=[xls_columns['name'], xls_columns['instance_id']])
host_list = host_list.rename(columns={xls_columns['instance_id']: csv_columns['instance_id']})
# 将数据时间列转换为datetime格式
data[csv_columns['datetime']] = pd.to_datetime(data[csv_columns['datetime']])
# 提取日期部分
data['日期'] = data[csv_columns['datetime']].dt.date
# 与主机列表合并,以获取主机名称
data = data.merge(host_list, on=csv_columns['instance_id'], how='left')
# 计算每日最高监控值
daily_max = data.groupby([xls_columns['name'], '日期'])[csv_columns['monitor_value']].max().reset_index()
# 计算每日最高峰值的总和并求平均值
daily_max_sum = daily_max.groupby(xls_columns['name'])[csv_columns['monitor_value']].sum().reset_index()
daily_max_avg = daily_max_sum.copy()
daily_max_avg[csv_columns['monitor_value']] = daily_max_sum[csv_columns['monitor_value']] / daily_max['日期'].nunique()
# 找出TOP10的主机
top10_hosts = daily_max_avg.nlargest(10, csv_columns['monitor_value'])
# 设置Seaborn样式
sns.set(style="whitegrid")
# 自定义颜色
custom_color = (51 / 255, 152 / 255, 219 / 255)
# 设置字体
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用黑体显示中文
plt.rcParams['axes.unicode_minus'] = False # 正常显示负号
# 修改figsize以减小图像尺寸
smaller_figsize = (10, 5)
# 生成TOP10主机的每日最高峰值平均值柱状图
plt.figure(figsize=smaller_figsize)
bars = sns.barplot(x=xls_columns['name'], y=csv_columns['monitor_value'], data=top10_hosts, color=custom_color, width=0.5)
plt.xlabel('', fontsize=12) # 主机名称设置为空标签
plt.ylabel('', fontsize=12) # 平均监控值设置为空标签
plt.title('', fontsize=15) # TOP10主机每日最高峰值平均值设置为空标签
plt.xticks(rotation=45)
# 在每个柱子上显示数值
for bar in bars.patches:
height = bar.get_height()
plt.text(bar.get_x() + bar.get_width() / 2.,
height,
f'{height:.2f}',
ha='center', va='bottom',
fontsize=10) # 这里设置字体大小
# 自定义Y轴刻度标签为百分比格式
def percent_formatter(x, pos):
return f'{x:.0f}%'
ax = plt.gca()
ax.yaxis.set_major_formatter(FuncFormatter(percent_formatter))
# 设置Y轴范围为0到100
ax.set_ylim(0, 100)
# 取消柱状图的边框
for spine in ax.spines.values():
spine.set_visible(False)
# 取消竖线
ax.xaxis.grid(False)
plt.tight_layout()
plt.savefig('top10_daily_max_avg_bar_with_values.png', bbox_inches='tight')
plt.show()
# 计算每日最高监控值并绘图
fig = plt.figure(figsize=smaller_figsize) # 这里调整线形图的尺寸
ax = fig.add_subplot(1, 1, 1)
for host in top10_hosts[xls_columns['name']]:
host_data = daily_max[daily_max[xls_columns['name']] == host]
sns.lineplot(x='日期', y=csv_columns['monitor_value'], data=host_data, marker='o', label=host, ax=ax)
plt.xlabel('', fontsize=12) # 日期标签设置为空
plt.ylabel('', fontsize=12) # 最高监控值标签设置为空
# plt.title('TOP10主机每日最高监控值', fontsize=15)
# 隐藏图形的边框
for spine in ax.spines.values():
spine.set_visible(False)
# 设置x轴范围为数据的最小和最大日期
min_date = daily_max['日期'].min()
max_date = daily_max['日期'].max()
ax.set_xlim([min_date, max_date])
# 自定义Y轴刻度标签为百分比格式
ax.yaxis.set_major_formatter(FuncFormatter(percent_formatter))
# 设置Y轴范围为0到100
ax.set_ylim(0, 100)
# 取消竖线
ax.xaxis.grid(False)
# 显示x轴的下边框
ax.spines['bottom'].set_visible(True)
# 正确处理图例位置,将其移到图像上方,并设置frameon=False使其透明
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles, labels, loc='lower center', bbox_to_anchor=(0.5, 1.02), fancybox=True, shadow=False, ncol=5, frameon=False)
plt.xticks(rotation=45)
plt.tight_layout(rect=[0, 0, 1, 0.95]) # 调整布局以适应图例
plt.savefig('top10_daily_max_line.png', bbox_inches='tight')
plt.show()
备注:云主机ID需要调整下