说明
此博客为任务驱动而记录,在一次画图工作中,总结了一些画图技巧,遂记录下来便于日后画图时有所参照,如果你也遇到一些画图上的问题,希望这篇博客有可能帮到你
任务详情
数据
example_csdn.csv
需要对该文件进行绘图,要求是:
- ‘time’,‘data’:以‘time’列为横坐标,绘制‘data’列的折线图;
- ‘操作’,‘数量’,‘价格’:在上一要求画出的折线图中标注对应点,比如时刻‘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)
附录
颜色设置: