一次python画图经历——涉及 matplotlib 的折线图,散点图,图中标注文字信息,设置横坐标密度,调整横坐标旋转角度,文字大小、各部分颜色、保存形式、美化调整

本文分享了在处理复杂数据集时的高效可视化技巧,包括数据预处理、折线图与散点图结合展示、动态标注信息及调整坐标轴显示,特别适用于时间序列分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

说明

此博客为任务驱动而记录,在一次画图工作中,总结了一些画图技巧,遂记录下来便于日后画图时有所参照,如果你也遇到一些画图上的问题,希望这篇博客有可能帮到你

任务详情

数据

example_csdn.csv
数据
需要对该文件进行绘图,要求是:

  1. ‘time’,‘data’:以‘time’列为横坐标,绘制‘data’列的折线图;
  2. ‘操作’,‘数量’,‘价格’:在上一要求画出的折线图中标注对应点,比如时刻‘2020/8/1 9:37’处在折线图对应点上标注出‘操作’、‘数量’、‘价格’信息;并且‘操作’中对‘买’、‘卖’标注出不同颜色。

操作

数据预处理

折线图好画,但是在某些点上进行分类标注,一时没想到什么简便方法,所以用一个笨法子,做一个预处理,根据‘data’列、‘操作’列生成新的两列:第一列为‘buy’,‘操作’列中为‘买’的时刻,‘buy’列对应时刻设置为‘data’列对应时刻的值,其他位置为空值;第二列为‘sell’,‘操作’列中为‘卖’的时刻,‘sell’列对应时刻设置为‘data’列对应时刻的值,其他位置为空值。如下图示
在这里插入图片描述
程序比较简单,如下:

def preprocess(ori_file, save_file):
    df = pd.DataFrame(pd.read_csv(ori_file, encoding='gbk'))
    df = df.set_index(pd.DatetimeIndex(pd.to_datetime(df.time)))
    df.drop(columns=['time'], inplace=True)

    print(df[:20])
    print(df.shape)
    print(df.columns)

    buy_list = []
    sell_list = []
    data_list = df['data']
    operation_list = df['操作']
    for i in range(df.shape[0]):
        if operation_list[i] == '0':
            buy_list.append(np.nan)
            sell_list.append(np.nan)
        if operation_list[i] == "买":
            buy_list.append(data_list[i])
            sell_list.append(np.nan)
        if operation_list[i] == "卖":
            buy_list.append(np.nan)
            sell_list.append(data_list[i])

    df['buy'] = buy_list
    df['sell'] = sell_list

    print(df[:20])
    print(df.shape)
    print(df.columns)

    df.to_csv(save_file, index=True, encoding='gbk')

做了数据预处理后,任务需求大概框架就可以是
依据‘data’列绘制折线图,在同一幅图中,使用相同横坐标依据‘buy’列、‘sell’列绘制散点图,对散点图上的点加标注信息。

画图

折线图
fig, ax = plt.subplots(1, 1)
# ---------- 标题 ----------
title = "示例 " + name
# fontsize: 字体大小
plt.title(title, fontsize=16)

# ---------- 折线图 ----------
# x 为横坐标 'time' 列
# y1 为 'data' 列
# color: 颜色
# lw: 线宽
ax.plot(x, y1, color='royalblue', lw=2.5, label='数据')
# 不加下面这句 label 显示不出来
plt.legend()

# 折线图中 label 的字体大小
font = {'size': 14}
matplotlib.rc('font', **font)
散点图
# ---------- 散点图 ----------
# x 为横坐标 'time' 列
# y2 为 'buy' 列
# y3 为 'sell' 列
# marker: 点的形状
# c: 颜色
ax.scatter(x, y2, marker='o', c='darkgreen')
ax.scatter(x, y3, marker='o', c='firebrick')
图中标注文字信息
# ---------- 标注 ----------
# ----- 生成要显示的信息 -----
# 操作为 '买' 时要显示的信息
text_buy = []
for i in range(len(x)):
    if (df['操作'][i] != 0) and (df['操作'][i] == "买"):
        str_info = '价格: ' + str(df['价格'][i]) + '\n' + '数量: ' + str(df['数量'][i]) + '\n' + '操作: ' + str(df['操作'][i])
        text_buy.append(str_info)
    else:
        text_buy.append(" ")
# 操作为 '卖' 时要显示的信息
text_sell = []
for i in range(len(x)):
    if (df['操作'][i] != 0) and (df['操作'][i] == "卖"):
        str_info = '价格: ' + str(df['价格'][i]) + '\n' + '数量: ' + str(df['数量'][i]) + '\n' + '操作: ' + str(df['操作'][i])
        text_sell.append(str_info)
    else:
        text_sell.append(" ")

# ----- 买 -----
# 标注框
# boxstyle: 标注框形式
# fc: 标注框背景颜色
# alpha: 标注框背景透明度
bbox_buy = dict(boxstyle="round", fc='lightgreen', alpha=0.6)
# 标注箭头
# arrowstyle: 箭头形式
# connectionstyle: 连线形式
arrowprops_buy = dict(arrowstyle="->", connectionstyle="arc3,rad=0.")

# 标注框偏移量
# x 轴偏移量
offset_x = 60
# y 轴偏移量
offset_y = 60

# 交替标志
# 此标志的作用是防止两个标注信息点离得过近导致标注信息重叠
# 相邻的点存在标注信息时,通过不同的交替标志绘制不同方向的标注信息
flag = 0

# 对列循环进行标注
for i in range(len(x)):
    if text_buy[i] != " ":
        if flag == 0:
            flag = 1
            # text_buy[i] 为要显示的标注信息列表,注意要与横坐标同尺寸
            # xy: 标注信息的位置,(横坐标, 纵坐标)
            # xytext: 标注信息相对于标注信息位置的偏移量,由 offset_x 和 offset_y 限定
            # textcoords: 配合 xytext 使用,具体含义未研究,sorry,可取 'offset points' or 'offset pixels'
            # bbox: 标注框设置
            # arrowprops: 标注设置
            # size: 标注框大小,标注框里的信息会随框大小变化而变化
            plt.annotate(text_buy[i],
                         xy=(x[i], y2[i]),
                         xytext=(-offset_x * 4, offset_y * 0.9),
                         textcoords='offset points',
                         bbox=bbox_buy,
                         arrowprops=arrowprops_buy,
                         size=15)
        else:
            flag = 0
            plt.annotate(text_buy[i],
                         xy=(x[i], y2[i]),
                         xytext=(offset_x, -offset_y * 0.9),
                         textcoords='offset points',
                         bbox=bbox_buy,
                         arrowprops=arrowprops_buy,
                         size=15)
    else:
        plt.annotate(text_buy[i],
                     xy=(x[i], y2[i]),
                     xytext=(offset_x, -offset_y * 0.9 / 2),
                     textcoords='offset points',
                     bbox=bbox_buy,
                     arrowprops=arrowprops_buy,
                     size=15)

# ----- 卖 -----
# 标注框
bbox_sell = dict(boxstyle="round", fc='salmon', alpha=0.6)
# 标注箭头
arrowprops_sell = dict(arrowstyle="->", connectionstyle="arc3,rad=0.")

# 交替标志
flag = 0

# 对列循环进行标注
for i in range(len(x)):
    if text_sell[i] != " ":
        if flag == 0:
            flag = 1
            plt.annotate(text_sell[i],
                         xy=(x[i], y3[i]),
                         xytext=(-offset_x * 4, offset_y * 0.9),
                         textcoords='offset points',
                         bbox=bbox_sell,
                         arrowprops=arrowprops_sell,
                         size=15)
        else:
            flag = 0
            plt.annotate(text_sell[i],
                         xy=(x[i], y3[i]),
                         xytext=(offset_x, -offset_y * 0.9),
                         textcoords='offset points',
                         bbox=bbox_sell,
                         arrowprops=arrowprops_sell,
                         size=15)
    else:
        plt.annotate(text_sell[i],
                     xy=(x[i], y3[i]),
                     xytext=(offset_x, -offset_y * 0.9 / 2),
                     textcoords='offset points',
                     bbox=bbox_sell,
                     arrowprops=arrowprops_sell,
                     size=15)
设置横坐标密度,调整横坐标旋转角度

由于横坐标为时间,并且数据列比较长,如果不做处理的话横坐标无法正常显示,类似下图情况、
在这里插入图片描述
这里对这种密集的时间横坐标做两个操作

  • 调整图中横坐标显示密度,或者说对‘time’列进行采样后再显示
  • 由于横坐标一个刻度显示内容过多,刻度间间隔过小,所以调整横坐标刻度值显示方向:旋转90°显示
# ---------- 坐标轴 ----------
# 设置 x 轴采样大小
tick_spacing = 10
ax.xaxis.set_major_locator(ticker.MultipleLocator(tick_spacing))
# 设置 x 坐标轴标签的显示内容和大小
plt.xlabel('时间', fontsize=14)
# 设置 x 坐标轴刻度的旋转方向和大小
# rotation: 旋转方向
plt.xticks(rotation=90, fontsize=14)
# 设置 y 坐标轴刻度大小
plt.yticks(fontsize=14)
让内容处于整幅图中合适的位置
# 调整图的位置
plt.subplots_adjust(top=0.9, bottom=0.2, left=0.15, right=0.95)
保存设置
# 设置保存尺寸
fig.set_size_inches(28, 20)
# 设置保存清晰度,并保存图像
plt.savefig(save_picture, dpi=300)

结果

在这里插入图片描述

完整程序

import numpy as np
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker


def preprocess(ori_file, save_file):
    df = pd.DataFrame(pd.read_csv(ori_file, encoding='gbk'))
    df = df.set_index(pd.DatetimeIndex(pd.to_datetime(df.time)))
    df.drop(columns=['time'], inplace=True)

    print(df[:20])
    print(df.shape)
    print(df.columns)

    buy_list = []
    sell_list = []
    data_list = df['data']
    operation_list = df['操作']
    for i in range(df.shape[0]):
        if operation_list[i] == '0':
            buy_list.append(np.nan)
            sell_list.append(np.nan)
        if operation_list[i] == "买":
            buy_list.append(data_list[i])
            sell_list.append(np.nan)
        if operation_list[i] == "卖":
            buy_list.append(np.nan)
            sell_list.append(data_list[i])

    df['buy'] = buy_list
    df['sell'] = sell_list

    print(df[:20])
    print(df.shape)
    print(df.columns)

    df.to_csv(save_file, index=True, encoding='gbk')


def draw_picture(save_file, name, save_picture):

    df = pd.DataFrame(pd.read_csv(save_file, encoding='gbk'))

    x = df['time']
    y1 = df['data']

    y2 = df["buy"]
    y3 = df["sell"]

    fig, ax = plt.subplots(1, 1)
    # ---------- 标题 ----------
    title = "示例 " + name
    # fontsize: 字体大小
    plt.title(title, fontsize=16)

    # ---------- 折线图 ----------
    # color: 颜色
    # lw: 线宽
    ax.plot(x, y1, color='royalblue', lw=2.5, label='data')
    # 不加下面这句 label 显示不出来
    plt.legend()

    # 折线图中 label 的字体大小
    font = {'size': 14}
    matplotlib.rc('font', **font)

    # ---------- 散点图 ----------
    # marker: 点的形状
    # c: 颜色
    ax.scatter(x, y2, marker='o', c='darkgreen')
    ax.scatter(x, y3, marker='o', c='firebrick')

    # ---------- 标注 ----------
    # ----- 生成要显示的信息 -----
    # 操作为 '买' 时要显示的信息
    text_buy = []
    for i in range(len(x)):
        if (df['操作'][i] != 0) and (df['操作'][i] == "买"):
            str_info = '价格: ' + str(df['价格'][i]) + '\n' + '数量: ' + str(df['数量'][i]) + '\n' + '操作: ' + str(df['操作'][i])
            text_buy.append(str_info)
        else:
            text_buy.append(" ")
    # 操作为 '卖' 时要显示的信息
    text_sell = []
    for i in range(len(x)):
        if (df['操作'][i] != 0) and (df['操作'][i] == "卖"):
            str_info = '价格: ' + str(df['价格'][i]) + '\n' + '数量: ' + str(df['数量'][i]) + '\n' + '操作: ' + str(df['操作'][i])
            text_sell.append(str_info)
        else:
            text_sell.append(" ")

    # ----- 买 -----
    # 标注框
    # boxstyle: 标注框形式
    # fc: 标注框背景颜色
    # alpha: 标注框背景透明度
    bbox_buy = dict(boxstyle="round", fc='lightgreen', alpha=0.6)
    # 标注箭头
    # arrowstyle: 箭头形式
    # connectionstyle: 连线形式
    arrowprops_buy = dict(arrowstyle="->", connectionstyle="arc3,rad=0.")

    # 标注框偏移量
    # x 轴偏移量
    offset_x = 60
    # y 轴偏移量
    offset_y = 60

    # 交替标志
    # 此标志的作用是防止两个标注信息点离得过近导致标注信息重叠
    # 相邻的点存在标注信息时,通过不同的交替标志绘制不同方向的标注信息
    flag = 0

    # 对列循环进行标注
    for i in range(len(x)):
        if text_buy[i] != " ":
            if flag == 0:
                flag = 1
                # text_buy[i] 为要显示的标注信息列表,注意要与横坐标同尺寸
                # xy: 标注信息的位置,(横坐标, 纵坐标)
                # xytext: 标注信息相对于标注信息位置的偏移量,由 offset_x 和 offset_y 限定
                # textcoords: 配合 xytext 使用,具体含义未研究,sorry,可取 'offset points' or 'offset pixels'
                # bbox: 标注框设置
                # arrowprops: 标注设置
                # size: 标注框大小,标注框里的信息会随框大小变化而变化
                plt.annotate(text_buy[i],
                             xy=(x[i], y2[i]),
                             xytext=(-offset_x * 4, offset_y * 0.9),
                             textcoords='offset points',
                             bbox=bbox_buy,
                             arrowprops=arrowprops_buy,
                             size=15)
            else:
                flag = 0
                plt.annotate(text_buy[i],
                             xy=(x[i], y2[i]),
                             xytext=(offset_x, -offset_y * 0.9),
                             textcoords='offset points',
                             bbox=bbox_buy,
                             arrowprops=arrowprops_buy,
                             size=15)
        else:
            plt.annotate(text_buy[i],
                         xy=(x[i], y2[i]),
                         xytext=(offset_x, -offset_y * 0.9 / 2),
                         textcoords='offset points',
                         bbox=bbox_buy,
                         arrowprops=arrowprops_buy,
                         size=15)

    # ----- 卖 -----
    # 标注框
    bbox_sell = dict(boxstyle="round", fc='salmon', alpha=0.6)
    # 标注箭头
    arrowprops_sell = dict(arrowstyle="->", connectionstyle="arc3,rad=0.")

    # 交替标志
    flag = 0

    # 对列循环进行标注
    for i in range(len(x)):
        if text_sell[i] != " ":
            if flag == 0:
                flag = 1
                plt.annotate(text_sell[i],
                             xy=(x[i], y3[i]),
                             xytext=(-offset_x * 4, offset_y * 0.9),
                             textcoords='offset points',
                             bbox=bbox_sell,
                             arrowprops=arrowprops_sell,
                             size=15)
            else:
                flag = 0
                plt.annotate(text_sell[i],
                             xy=(x[i], y3[i]),
                             xytext=(offset_x, -offset_y * 0.9),
                             textcoords='offset points',
                             bbox=bbox_sell,
                             arrowprops=arrowprops_sell,
                             size=15)
        else:
            plt.annotate(text_sell[i],
                         xy=(x[i], y3[i]),
                         xytext=(offset_x, -offset_y * 0.9 / 2),
                         textcoords='offset points',
                         bbox=bbox_sell,
                         arrowprops=arrowprops_sell,
                         size=15)

    # ---------- 坐标轴 ----------
    # 设置 x 轴显示密度
    tick_spacing = 10
    ax.xaxis.set_major_locator(ticker.MultipleLocator(tick_spacing))
    # 设置 x 坐标轴标签的显示内容和大小
    plt.xlabel('时间', fontsize=14)
    # 设置 x 坐标轴刻度的旋转方向和大小
    plt.xticks(rotation=90, fontsize=14)
    # 设置 y 坐标轴刻度大小
    plt.yticks(fontsize=14)

    # 坐标轴中文显示
    plt.rcParams['font.sans-serif'] = ['SimHei']

    # # x 坐标轴显示完整
    # plt.tight_layout()  # 此例中使用标注导致此条语句不能使用

    # 调整图的位置
    plt.subplots_adjust(top=0.9, bottom=0.2, left=0.15, right=0.95)

    # 设置保存尺寸
    fig.set_size_inches(28, 20)
    # 设置保存清晰度,并保存图像
    plt.savefig(save_picture, dpi=300)

    plt.show()


if __name__ == "__main__":
    ori_file = './example_csdn.csv'
    save_file = './example_csdn_processed.csv'
    save_picture = './examples_csdn.jpg'
    name = 'example'

    preprocess(ori_file, save_file)
    draw_picture(save_file, name, save_picture)

附录

颜色设置:
在这里插入图片描述

参考

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值