用matplotlib复现一幅看到的图

当你看到一幅精美的图时,你想到了什么?

4月的一个晚上,一位同学在《Corporate Finance》上看到下图时,脑中产生了这么一个想法:“我能否用matplotlib把它画出来?”

在这里插入图片描述

每当脑中产生一些有趣的idea时,我都会把它记录下来。而今天这个idea我不需要记下来,因为我打算即刻就把它照进现实。

下面让我手把手教你,如何一步步复现上面这幅精美的图!或者在文末直接获取全部代码!

〇、 首先分别出图片的主要组成要素

在这里插入图片描述

x和y的数据
颜色
xy轴的刻度和标签等
黑点和虚线
箭头和文本

然后让我们来实现它们吧👣👣

一、 获取三条线x和y的数据

1. 分析下书上的数据

  • 第一点的数据来源是非常重要的,若果没有数据,那我怎么画出一样的线条呢?好在数据已经在书中给出。

在这里插入图片描述
在这里插入图片描述

三条线分别对应的是三种投资机会。简单分析下三种投资机会。

  • l 1 l_1 l1是把钱投给男朋友的项目, l 2 l_2 l2是自己投资两台无人机送外卖, l 3 l_3 l3是她发现自己其实可以投资十台无人机送外卖。
  • 这三种投资机会是互斥的,也就是她只能选择其中一种进行投资,因为她的时间和精力是有限的。
  • 此外,这三种投资方式的现金流模式时一样的,但是初始规模(Scale)和年金存在差异。

注意:上面两张ppt中展示的是假定折现率为12%的情况下三种投资方案的NPV,而我们要绘制净现值曲线,则需要遍历折现率的全部可能取值来获得三条 l l l

注意:内部收益率IRR和债券的到期收益率不同。 李奇霖:债券收益率曲线与利差分析


2. 使用numpy_financial计算不同折现率下的净现值

首先导入需要用的库:
➕ numpy
📊 matplotlib.pyplot
⌚ numpy_financial

这里介绍一下numpy_financial库。

numpy_financial可用于金融计算,相当于金融计计算器的作用。可以用它来计算现值,利率,终值等。

图中线条的数据涉及到不同折现率下的净现值,所以我要用numpy_financial来生计算现值。

import numpy as np
import matplotlib.pyplot as plt
import numpy_financial as npf

np.set_printoptions(threshold=10)  # 设置数组显示长度

👉生成从0.001到1,间隔0.001的1000个折现率数据。

R = np.linspace(0,1,1001)[1:]
R
array([0.001, 0.002, 0.003, ..., 0.998, 0.999, 1.   ])
  • 生成 l 1 l_1 l1的数据。
Solutions1 = []          # 存储l1的全部NPV
for r in R:
    rate = r             # 每期实际利率
    nper = 3             # 期数
    c = 5000             # 负号表示现金流支出
    fv = 0               # 终值
    when = 0             # 最后一个0表明现金六期末开始,但起始点都是期初也就是会折现到期初
    Solutions1.append(-npf.pv(rate, nper, c, fv, when)-10000)  #  将现值调为正数,然后带入NPV的计算公式

❗ 用金融计算器时,要注意它额外给你添加的正负号。如果现金流©是正值,那么初始值它会返回负值。

一个直观的理解是你在期初支出(-)了这个投资的钱,然后每一期获得了现金收入(+)。

❗ 但这里我们其实不需要用正负号表示这一多余的信息,我们只要知道年金的现值(本题它的数值本身显

然应该是正的——使用年金计算公式),然后带入NPV的计算公式。所以npv.pv的计算结果要加负号!

Net Present Value
NPV = PV(Benefits) - PV(Costs)
  • 如法炮制,生成 l 2 l_2 l2的数据。
Solutions2 = []
for r in R:
    rate = r             # 每期实际利率
    nper = 3             # 期数
    c = 6000             # 负号表示现金流支出
    fv = 0               # 终值
    when = 0             # 最后一个0表明现金六期末开始,但起始点都是期初也就是会折现到期初
    Solutions2.append(-npf.pv(rate, nper, c, fv, when)-10000)  #  将现值调为正数,然后带入NPV的计算公式
  • 生成 l 3 l_3 l3的数据。
Solutions3 = []
for r in R:
    rate = r             # 每期实际利率
    nper = 3             # 期数
    c = 25000            # 负号表示现金流支出
    fv = 0               # 终值
    when = 0             # 最后一个0表明现金六期末开始,但起始点都是期初也就是会折现到期初
    Solutions3.append(-npf.pv(rate, nper, c, fv, when)-50000)  #  将现值调为正数,然后带入NPV的计算公式

二、 颜色

颜色是画的灵魂,不同的颜色会对人的大脑产生不同的刺激,形成不一样的感受,这幅教科书上的颜色很
随和,沁人心脾。

要获得和原图一样的颜色,其实很简单,需要借助画图软件。

1.通过画图获取颜色

  1. 直接截图(windows+shift+s),将图片保存到桌面。
  2. 右键图片———打开方式———画图。

在这里插入图片描述

  1. 点击吸管工具,先吸取 l 1 l_1 l1橙色线条的颜色。

  2. 点击编辑颜色,出现编辑颜色界面。然后把矩形框中的红黄蓝三个分量记住,写到jupyter中!

在这里插入图片描述

  1. 由于画图中rgb显示的三个分量是十进制的,而python中画图需要十六进制的参数输入,故定义一个转换函数。
def h16_10(n):
    ls = []   
    for i in n:
        m = hex(int(i))
        m=m[2:]
        ls.append(m)    
    return '#' + ''.join(ls)
h16_10([199,129,77])  # 以列表形式输入你记住的三个颜色的分量
'#c7814d'

"#c7814d"就是我们要的结果,它可以直接作为plot的color参数的赋值。

如法炮制获取 l 2 l_2 l2和左下角区域的颜色。

l 3 l_3 l3一看便知是紫色,故不需要通过画图获取颜色,右边近似小三角的区域和 l 1 l_1 l1的颜色一样。

至于 l 1 l_1 l1 l 2 l_2 l2之间的颜色则可以通过设置plot的alpha值(不透明度)来实现。

h16_10([64,138,122])  # 输入三个颜色的分量构成的列表
'#408a7a'

2. 既然已经获取了线条的xy以及颜色,那就来画图吧,然后再一点点加精

# 生成画布
fig = plt.figure(figsize=(10,8))
# 绘制三条曲线
plt.plot(R, Solutions1, c='#c57746', linewidth=2, zorder=1)   # zorder小的先画
plt.plot(R, Solutions2, c='#408a7a', linewidth=2, zorder=1)
plt.plot(R, Solutions3, c='purple', linewidth=2, zorder=1)
# 秀出你的图片
plt.show()

在这里插入图片描述


3 填充区域颜色

  • 在填充区域的颜色前先设置xy显示范围。

添加如下代码:

plt.ylim([-4000,12000])
plt.xlim(0,0.4)
# 生成画布
fig = plt.figure(figsize=(10,8))
# 绘制三条曲线
plt.plot(R, Solutions1, c='#c57746', linewidth=2, zorder=1)   
plt.plot(R, Solutions2, c='#408a7a', linewidth=2, zorder=1)
plt.plot(R, Solutions3, c='purple', linewidth=2, zorder=1)
# 设置xy显示范围
plt.ylim([-4000,12000])
plt.xlim(0,0.4)
# 秀出你的图片
plt.show()

在这里插入图片描述

  • 填充颜色。
    • 原图中的两个分界点23.4%和36.3%可以知道,他们应该对应R[233]和R[362]。

添加如下代码:

plt.fill_between(R[:363], Solutions1[:234]+[0]*129, color='#a7cd3a')  # y2默认为0
plt.fill_between(R[:363], Solutions1[:234]+[0]*129, Solutions2[:363],color='#a7cd3a', alpha=0.5)
plt.fill_between(R[363:], Solutions2[363:], color='#d26c53')  # y2默认为0
plt.fill_between(R[234:], Solutions1[234:], [0]*129+Solutions2[363:], color='#d26c53', alpha=0.5)
# 生成画布
fig = plt.figure(figsize=(10,8))
# 绘制三条曲线
plt.plot(R, Solutions1, c='#c57746', linewidth=2, zorder=1)   
plt.plot(R, Solutions2, c='#408a7a', linewidth=2)
plt.plot(R, Solutions3, c='purple', linewidth=2, zorder=1)
# 设置xy边界
plt.ylim([-4000,12000])
plt.xlim(0,0.4)
# 填充颜色
plt.fill_between(R[:363], Solutions1[:234]+[0]*129, color='#a7cd3a')  # y2默认为0
plt.fill_between(R[:363], Solutions1[:234]+[0]*129, Solutions2[:363],color='#a7cd3a', alpha=0.5)
plt.fill_between(R[363:], Solutions2[363:], color='#d26c53')  # y2默认为0
plt.fill_between(R[234:], Solutions1[234:], [0]*129+Solutions2[363:], color='#d26c53', alpha=0.5)
# 秀出你的图片
plt.show()

在这里插入图片描述


三、 xytick和xylabel

1. 让上边和右边的边框线消失

添加如下代码:

ax = plt.gca()  # 获得当前Axes对象
ax.spines['right'].set_color('none')  # 包围线消失
ax.spines['top'].set_color('none')

3. 设置x轴的位置。

  • 让原来的横轴消失,然后自己plot添加横轴。
  • 通过自己添加横轴可以让刻度线显示在x轴上方!

添加如下代码:

ax.spines['bottom'].set_color('none')             # 需要让x轴消失(不包括刻度线)自己画
ax.spines['bottom'].set_position(('axes',0.25))   # 设置x轴的tick的位置,移动到NPV=0上面
plt.plot(R, np.array(R)*0, c='k', linewidth=0.8)  # 自己画横轴 ```
# 生成画布
fig = plt.figure(figsize=(10,8))
# 绘制三条曲线
plt.plot(R, Solutions1, c='#c57746', linewidth=2, zorder=1)   
plt.plot(R, Solutions2, c='#408a7a', linewidth=2)
plt.plot(R, Solutions3, c='purple', linewidth=2, zorder=1)
# 设置xy边界
plt.ylim([-4000,12000])
plt.xlim(0,0.4)
# 填充颜色
plt.fill_between(R[:363], Solutions1[:234]+[0]*129, color='#a7cd3a')  # y2默认为0
plt.fill_between(R[:363], Solutions1[:234]+[0]*129, Solutions2[:363],color='#a7cd3a', alpha=0.5)
plt.fill_between(R[363:], Solutions2[363:], color='#d26c53')  # y2默认为0
plt.fill_between(R[234:], Solutions1[234:], [0]*129+Solutions2[363:], color='#d26c53', alpha=0.5)
# 让包围线消失
ax = plt.gca()  # 获得当前Axes对象
ax.spines['right'].set_color('none')  # 包围线消失
ax.spines['top'].set_color('none')
# 移动x轴并让刻度显示在x轴上方
ax.spines['bottom'].set_color('none')   # 需要让x轴消失(不包括刻度线)自己画
ax.spines['bottom'].set_position(('axes',0.25))  # 设置x轴的tick的位置,移动到NPV=0上面
plt.plot(R, np.array(R)*0, c='k', linewidth=0.8)  # 自己画横轴
# 秀出你的图片
plt.show()

在这里插入图片描述


4. 调整刻度间隔,并在xtick后添加"%“,在ytick前添加”$"。

添加如下代码:

# 设置xtick
plt.xticks([0.12,0.2,0.234,0.363], [str(i) + '%' for i in [12,20,23.4,36.3]],x=0,y=-100)  
# 设置ytick
plt.yticks([i*2000-4000 for i in range(9)],['$'+str(i*2000-4000) for i in range(9)])      
# 生成画布
fig = plt.figure(figsize=(10,8))
# 绘制三条曲线
plt.plot(R, Solutions1, c='#c57746', linewidth=2, zorder=1)   
plt.plot(R, Solutions2, c='#408a7a', linewidth=2)
plt.plot(R, Solutions3, c='purple', linewidth=2, zorder=1)
# 设置xy边界
plt.ylim([-4000,12000])
plt.xlim(0,0.4)
# 填充颜色
plt.fill_between(R[:363], Solutions1[:234]+[0]*129, color='#a7cd3a')  # y2默认为0
plt.fill_between(R[:363], Solutions1[:234]+[0]*129, Solutions2[:363],color='#a7cd3a', alpha=0.5)
plt.fill_between(R[363:], Solutions2[363:], color='#d26c53')  # y2默认为0
plt.fill_between(R[234:], Solutions1[234:], [0]*129+Solutions2[363:], color='#d26c53', alpha=0.5)
# 让包围线消失
ax = plt.gca()  # 获得当前Axes对象
ax.spines['right'].set_color('none')  # 包围线消失
ax.spines['top'].set_color('none')
# 移动x轴并让刻度显示在x轴上方
ax.spines['bottom'].set_color('none')
ax.spines['bottom'].set_position(('axes',0.26))  # 设置x轴的tick的位置,移动到NPV=0上面
plt.plot(R, np.array(R)*0, c='k', linewidth=0.8)  # 自己画横轴
# 调整刻度间隔,并在xtick后添加"%",在ytick前添加"$"。
plt.xticks([0.12,0.2,0.234,0.363], [str(i) + '%' for i in [12,20,23.4,36.3]],x=0,y=-100)   # 设置xtick
plt.yticks([i*2000-4000 for i in range(9)],['$'+str(i*2000-4000) for i in range(9)])   # 设置ytick
# 秀出你的图片
plt.show()

在这里插入图片描述


5. 添加xylabel

  • 注意这里的xlabel不能直接添加在x轴下方,因为x轴已经移动到了大约26%y轴的位置
  • 如果还要xlabel像原图一样留在底部,不妨用plt.suptitle函数来设置

!首先设置字体

# 设置ylabel字体
font1 = {'family': 'Times New Roman',
         'weight': 'bold',  # bold:粗体
         'style':'normal',
         'size': 16}
# 设置subtitle字体
font2 = {'family': 'Times New Roman',
         'weight': 'bold',
        'style':'normal',  # normal:普通;italic:斜体
         'size': 16}

参数设置:

  • style{‘normal’, ‘italic’, ‘oblique’}   italic:斜体 italic和oblique的区别
  • weight{a numeric value in range 0-1000, ‘ultralight’, ‘light’, ‘normal’, ‘regular’, ‘book’, ‘medium’, ‘roman’, ‘semibold’, ‘demibold’, ‘demi’, ‘bold’, ‘heavy’, ‘extra bold’, ‘black’}

添加如下代码:

plt.ylabel('NPV', fontdict=font1, weight='bold')
plt.suptitle('Discount Rate', fontproperties=font2, y=0.1)  # font properties:字体属性
  • 一个需要注意的点:subtitle要通过fontproperties来传入字体的字典参数,而ylabel用font。
# 生成画布
fig = plt.figure(figsize=(10,8))
# 绘制三条曲线
plt.plot(R, Solutions1, c='#c57746', linewidth=2, zorder=1)   
plt.plot(R, Solutions2, c='#408a7a', linewidth=2)
plt.plot(R, Solutions3, c='purple', linewidth=2, zorder=1)
# 设置xy边界
plt.ylim([-4000,12000])
plt.xlim(0,0.4)
# 填充颜色
plt.fill_between(R[:363], Solutions1[:234]+[0]*129, color='#a7cd3a')  # y2默认为0
plt.fill_between(R[:363], Solutions1[:234]+[0]*129, Solutions2[:363],color='#a7cd3a', alpha=0.5)
plt.fill_between(R[363:], Solutions2[363:], color='#d26c53')  # y2默认为0
plt.fill_between(R[234:], Solutions1[234:], [0]*129+Solutions2[363:], color='#d26c53', alpha=0.5)
#  让包围线消失
ax = plt.gca()  # 获得当前Axes对象
ax.spines['right'].set_color('none')  # 包围线消失
ax.spines['top'].set_color('none')
# 移动x轴并让刻度显示在x轴上方
ax.spines['bottom'].set_color('none')
ax.spines['bottom'].set_position(('axes',0.26))                                            # 设置x轴的tick的位置,移动到NPV=0上面
plt.plot(R, np.array(R)*0, c='k', linewidth=0.8)                                            # 自己画横轴
# 调整刻度间隔,并在xtick后添加"%",在ytick前添加"$"。
plt.xticks([0.12,0.2,0.234,0.363], [str(i) + '%' for i in [12,20,23.4,36.3]],x=0,y=-100)   # 设置xtick
plt.yticks([i*2000-4000 for i in range(9)],['$'+str(i*2000-4000) for i in range(9)])       # 设置ytick
# 添加xylabel
plt.ylabel('NPV', fontdict=font1, weight='bold')
plt.suptitle('Discount Rate', fontproperties=font2, y=0.1)  # font properties:字体属性
# 秀出你的图片
plt.show()

在这里插入图片描述


四、添加黑点和虚线

  1. 添加黑点,
    添加如下代码:
plt.scatter(0.2,Solutions2[200], c='k')
plt.scatter(0.12,Solutions1[120], c='k')
plt.scatter(0.12,Solutions2[120], c='k')
plt.scatter(0.12,Solutions3[120], c='k')```
  1. 添加虚线,
    添加如下代码:
plt.plot([0.12,0.12], [0,Solutions2[120]],dashes=[6, 3],c='k')  # dashes=[6, 3]表示虚线长6,空白长3 
plt.plot([0.2,0.2], [0.2,Solutions2[200]],dashes=[6, 3],c='k')
  • dashes=[6, 3]表示虚线长6,空白长3
# 生成画布
fig = plt.figure(figsize=(10,8))
# 绘制三条曲线
plt.plot(R, Solutions1, c='#c57746', linewidth=2, zorder=1)   
plt.plot(R, Solutions2, c='#408a7a', linewidth=2)
plt.plot(R, Solutions3, c='purple', linewidth=2, zorder=1)
# 设置xy边界
plt.ylim([-4000,12000])
plt.xlim(0,0.4)
# 填充颜色
plt.fill_between(R[:363], Solutions1[:234]+[0]*129, color='#a7cd3a')  # y2默认为0
plt.fill_between(R[:363], Solutions1[:234]+[0]*129, Solutions2[:363],color='#a7cd3a', alpha=0.5)
plt.fill_between(R[363:], Solutions2[363:], color='#d26c53')  # y2默认为0
plt.fill_between(R[234:], Solutions1[234:], [0]*129+Solutions2[363:], color='#d26c53', alpha=0.5)
# 让包围线消失
ax = plt.gca()  # 获得当前Axes对象
ax.spines['right'].set_color('none')  # 包围线消失
ax.spines['top'].set_color('none')
# 移动x轴并让刻度显示在x轴上方
ax.spines['bottom'].set_color('none')
ax.spines['bottom'].set_position(('axes',0.26))                                            # 设置x轴的tick的位置,移动到NPV=0上面
plt.plot(R, np.array(R)*0, c='k', linewidth=0.8)                                            # 自己画横轴
# 调整刻度间隔,并在xtick后添加"%",在ytick前添加"$"。
plt.xticks([0.12,0.2,0.234,0.363], [str(i) + '%' for i in [12,20,23.4,36.3]],x=0,y=-100)   # 设置xtick
plt.yticks([i*2000-4000 for i in range(9)],['$'+str(i*2000-4000) for i in range(9)])       # 设置ytick
# 添加xylabel
plt.ylabel('NPV', fontdict=font1, weight='bold')
plt.suptitle('Discount Rate', fontproperties=font2, y=0.1)  # font properties:字体属性
# 添加黑点
plt.scatter(0.2,Solutions2[200], c='k')
plt.scatter(0.12,Solutions1[120], c='k')
plt.scatter(0.12,Solutions2[120], c='k')
plt.scatter(0.12,Solutions3[120], c='k')
# 添加虚线
plt.plot([0.12,0.12], [0,Solutions2[120]],dashes=[6, 3],c='k')  # dashes=[6, 3]表示虚线长6,空白长3 
plt.plot([0.2,0.2], [0.2,Solutions2[200]],dashes=[6, 3],c='k')
# 秀出你的图片
plt.show()

在这里插入图片描述

五、添加箭头和文本

1. 箭头+箭头的文本标注

添加如下代码:

ax.annotate("Boyfriends's\nbusiness\nIRR", xy=(0.363, 200), xytext=(0.363, 2000),
             arrowprops=dict(arrowstyle="->",connectionstyle="angle3,angleA=0,angleB=-90"), 
             horizontalalignment='center',verticalalignment ='bottom')  #,fontsize='x-large')
ax.annotate("IRR", xy=(0.234, -500), xytext=(0.234, -2000),
             arrowprops=dict(arrowstyle="->",connectionstyle="angle3,angleA=0,angleB=-90"),
             horizontalalignment='center',verticalalignment ='bottom')  #,fontsize='x-large')
ax.annotate("Crossover", xy=(0.2, Solutions2[200]), xytext=(0.25, Solutions2[200]+1000),
             arrowprops=dict(arrowstyle="->",connectionstyle="arc3, rad=0"),
             horizontalalignment='center',verticalalignment ='bottom')  #,fontsize='x-large')
ax.annotate(' Cost of \nCapital', xy=(0.12, -500), xytext=(0.12, -2000),
             arrowprops=dict(arrowstyle="->",connectionstyle="angle3,angleA=0,angleB=-90"),
             horizontalalignment='center',verticalalignment ='top')  #,fontsize='x-large')

参数说明:

ax.annotate(“文本”,xy=(箭头位置),xytext(文本位置),
            arrowarprops=dict(箭头样式),
            horizontalalignment=‘center’ # 把x作为文本框水平方向的中心点的位置,
            verticalalignment =‘bottom’ # 把y作为文本框垂直方向的底部的位置)
            # 上面两行就是设定xytext指的是文本框中间底部的坐标。

  • 还可设置fontsize='x-large’让文字变大。
# 生成画布
fig = plt.figure(figsize=(10,8))
# 绘制三条曲线
plt.plot(R, Solutions1, c='#c57746', linewidth=2, zorder=1)   
plt.plot(R, Solutions2, c='#408a7a', linewidth=2)
plt.plot(R, Solutions3, c='purple', linewidth=2, zorder=1)
# 设置xy边界
plt.ylim([-4000,12000])
plt.xlim(0,0.4)
# 填充颜色
plt.fill_between(R[:363], Solutions1[:234]+[0]*129, color='#a7cd3a')  # y2默认为0
plt.fill_between(R[:363], Solutions1[:234]+[0]*129, Solutions2[:363],color='#a7cd3a', alpha=0.5)
plt.fill_between(R[363:], Solutions2[363:], color='#d26c53')  # y2默认为0
plt.fill_between(R[234:], Solutions1[234:], [0]*129+Solutions2[363:], color='#d26c53', alpha=0.5)
# 让包围线消失
ax = plt.gca()  # 获得当前Axes对象
ax.spines['right'].set_color('none')  # 包围线消失
ax.spines['top'].set_color('none')
# 移动x轴并让刻度显示在x轴上方
ax.spines['bottom'].set_color('none')
ax.spines['bottom'].set_position(('axes',0.26))                                            # 设置x轴的tick的位置,移动到NPV=0上面
plt.plot(R, np.array(R)*0, c='k', linewidth=0.8)                                            # 自己画横轴
# 调整刻度间隔,并在xtick后添加"%",在ytick前添加"$"。
plt.xticks([0.12,0.2,0.234,0.363], [str(i) + '%' for i in [12,20,23.4,36.3]],x=0,y=-100)   # 设置xtick
plt.yticks([i*2000-4000 for i in range(9)],['$'+str(i*2000-4000) for i in range(9)])       # 设置ytick
# 添加xylabel
plt.ylabel('NPV', fontdict=font1, weight='bold')
plt.suptitle('Discount Rate', fontproperties=font2, y=0.1)  # font properties:字体属性
# 添加黑点
plt.scatter(0.2,Solutions2[200], c='k')
plt.scatter(0.12,Solutions1[120], c='k')
plt.scatter(0.12,Solutions2[120], c='k')
plt.scatter(0.12,Solutions3[120], c='k')
# 添加虚线
plt.plot([0.12,0.12], [0,Solutions2[120]],dashes=[6, 3],c='k')  # dashes=[6, 3]表示虚线长6,空白长3 
plt.plot([0.2,0.2], [0.2,Solutions2[200]],dashes=[6, 3],c='k')
# 添加箭头和文本
ax.annotate("Boyfriends's\nbusiness\nIRR", xy=(0.363, 200), xytext=(0.363, 2000),
             arrowprops=dict(arrowstyle="->",connectionstyle="angle3,angleA=0,angleB=-90"), 
             horizontalalignment='center',verticalalignment ='bottom')  #,fontsize='x-large')
ax.annotate("IRR", xy=(0.234, -500), xytext=(0.234, -2000),
             arrowprops=dict(arrowstyle="->",connectionstyle="angle3,angleA=0,angleB=-90"),
             horizontalalignment='center',verticalalignment ='bottom')  #,fontsize='x-large')
ax.annotate("Crossover", xy=(0.2, Solutions2[200]), xytext=(0.25, Solutions2[200]+1000),
             arrowprops=dict(arrowstyle="->",connectionstyle="arc3, rad=0"),
             horizontalalignment='center',verticalalignment ='bottom')  #,fontsize='x-large')
ax.annotate(' Cost of \nCapital', xy=(0.12, -500), xytext=(0.12, -2000),
             arrowprops=dict(arrowstyle="->",connectionstyle="angle3,angleA=0,angleB=-90"),
             horizontalalignment='center',verticalalignment ='top')  
# 秀出你的图片
plt.show()

在这里插入图片描述

2. 仅添加文本和全局设定字体大小

  • 上面的图中字体都有点小,所以首先全局设定字体类型和大小。
plt.rcParams['font.family'] = 'Times New Roman'
plt.rcParams['font.size'] = 18
  • 然后添加 l 2 l_2 l2 l 3 l_3 l3的两处文本说明(不带箭头的):
plt.text( 0.13, Solutions3[120],'Delivery Service\n (10 drones)', c='k')
plt.text( 0.363, Solutions1[363]-1200, 'Delivery Service \n(2 drones)', c='k', horizontalalignment='center')
# 全局设定字体类型和大小
plt.rcParams['font.family'] = 'Times New Roman'
plt.rcParams['font.size'] = 18
# 生成画布
fig = plt.figure(figsize=(10,8))
# 绘制三条曲线
plt.plot(R, Solutions1, c='#c57746', linewidth=2, zorder=1)   
plt.plot(R, Solutions2, c='#408a7a', linewidth=2)
plt.plot(R, Solutions3, c='purple', linewidth=2, zorder=1)
# 设置xy边界
plt.ylim([-4000,12000])
plt.xlim(0,0.4)
# 填充颜色
plt.fill_between(R[:363], Solutions1[:234]+[0]*129, color='#a7cd3a')  # y2默认为0
plt.fill_between(R[:363], Solutions1[:234]+[0]*129, Solutions2[:363],color='#a7cd3a', alpha=0.5)
plt.fill_between(R[363:], Solutions2[363:], color='#d26c53')  # y2默认为0
plt.fill_between(R[234:], Solutions1[234:], [0]*129+Solutions2[363:], color='#d26c53', alpha=0.5)
# 让包围线消失
ax = plt.gca()  # 获得当前Axes对象
ax.spines['right'].set_color('none')  # 包围线消失
ax.spines['top'].set_color('none')
# 移动x轴并让刻度显示在x轴上方
ax.spines['bottom'].set_color('none')
ax.spines['bottom'].set_position(('axes',0.26))                                            # 设置x轴的tick的位置,移动到NPV=0上面
plt.plot(R, np.array(R)*0, c='k', linewidth=0.8)                                            # 自己画横轴
# 调整刻度间隔,并在xtick后添加"%",在ytick前添加"$"。
plt.xticks([0.12,0.2,0.234,0.363], [str(i) + '%' for i in [12,20,23.4,36.3]],x=0,y=-100)   # 设置xtick
plt.yticks([i*2000-4000 for i in range(9)],['$'+str(i*2000-4000) for i in range(9)])       # 设置ytick
# 添加xylabel
plt.ylabel('NPV', fontdict=font1, weight='bold')
plt.suptitle('Discount Rate', fontproperties=font2, y=0.1)  # font properties:字体属性
# 添加黑点
plt.scatter(0.2,Solutions2[200], c='k')
plt.scatter(0.12,Solutions1[120], c='k')
plt.scatter(0.12,Solutions2[120], c='k')
plt.scatter(0.12,Solutions3[120], c='k')
# 添加虚线
plt.plot([0.12,0.12], [0,Solutions2[120]],dashes=[6, 3],c='k')  # dashes=[6, 3]表示虚线长6,空白长3 
plt.plot([0.2,0.2], [0.2,Solutions2[200]],dashes=[6, 3],c='k')
# 添加箭头和文本
ax.annotate("Boyfriends's\nbusiness\nIRR", xy=(0.363, 200), xytext=(0.363, 2000),
             arrowprops=dict(arrowstyle="->",connectionstyle="angle3,angleA=0,angleB=-90"), 
             horizontalalignment='center',verticalalignment ='bottom')  #,fontsize='x-large')
ax.annotate("IRR", xy=(0.234, -500), xytext=(0.234, -2000),
             arrowprops=dict(arrowstyle="->",connectionstyle="angle3,angleA=0,angleB=-90"),
             horizontalalignment='center',verticalalignment ='bottom')  #,fontsize='x-large')
ax.annotate("Crossover", xy=(0.2, Solutions2[200]), xytext=(0.25, Solutions2[200]+1000),
             arrowprops=dict(arrowstyle="->",connectionstyle="arc3, rad=0"),
             horizontalalignment='center',verticalalignment ='bottom')  #,fontsize='x-large')
ax.annotate(' Cost of \nCapital', xy=(0.12, -500), xytext=(0.12, -2000),
             arrowprops=dict(arrowstyle="->",connectionstyle="angle3,angleA=0,angleB=-90"),
             horizontalalignment='center',verticalalignment ='top')  
# 添加纯文本
plt.text( 0.13, Solutions3[120],'Delivery Service\n (10 drones)', c='k')
plt.text( 0.363, Solutions1[363]-1200, 'Delivery Service \n(2 drones)', c='k', horizontalalignment='center')
# 秀出你的图片
plt.show()

在这里插入图片描述


3. 在 l 3 l_3 l3和刻度值23.4%处添加白色block

添加如下代码:

for label in ax.get_xticklabels()[2:3]:    
    label.set_fontsize(16)    
    label.set_bbox(dict(facecolor='white', edgecolor='None', alpha=2)
# 全局设定字体类型和大小
plt.rcParams['font.family'] = 'Times New Roman'
plt.rcParams['font.size'] = 18
# 生成画布
fig = plt.figure(figsize=(10,8))
# 绘制三条曲线
plt.plot(R, Solutions1, c='#c57746', linewidth=2, zorder=1)   
plt.plot(R, Solutions2, c='#408a7a', linewidth=2)
plt.plot(R, Solutions3, c='purple', linewidth=2, zorder=1)
# 设置xy边界
plt.ylim([-4000,12000])
plt.xlim(0,0.4)
# 填充颜色
plt.fill_between(R[:363], Solutions1[:234]+[0]*129, color='#a7cd3a')  # y2默认为0
plt.fill_between(R[:363], Solutions1[:234]+[0]*129, Solutions2[:363],color='#a7cd3a', alpha=0.5)
plt.fill_between(R[363:], Solutions2[363:], color='#d26c53')  # y2默认为0
plt.fill_between(R[234:], Solutions1[234:], [0]*129+Solutions2[363:], color='#d26c53', alpha=0.5)
# 让包围线消失
ax = plt.gca()  # 获得当前Axes对象
ax.spines['right'].set_color('none')  # 包围线消失
ax.spines['top'].set_color('none')
# 移动x轴并让刻度显示在x轴上方
ax.spines['bottom'].set_color('none')
ax.spines['bottom'].set_position(('axes',0.26))                                            # 设置x轴的tick的位置,移动到NPV=0上面
plt.plot(R, np.array(R)*0, c='k', linewidth=0.8)                                            # 自己画横轴
# 调整刻度间隔,并在xtick后添加"%",在ytick前添加"$"。
plt.xticks([0.12,0.2,0.234,0.363], [str(i) + '%' for i in [12,20,23.4,36.3]],x=0,y=-100)   # 设置xtick
plt.yticks([i*2000-4000 for i in range(9)],['$'+str(i*2000-4000) for i in range(9)])       # 设置ytick
# 添加xylabel
plt.ylabel('NPV', fontdict=font1, weight='bold')
plt.suptitle('Discount Rate', fontproperties=font2, y=0.1)  # font properties:字体属性
# 添加黑点
plt.scatter(0.2,Solutions2[200], c='k')
plt.scatter(0.12,Solutions1[120], c='k')
plt.scatter(0.12,Solutions2[120], c='k')
plt.scatter(0.12,Solutions3[120], c='k')
# 添加虚线
plt.plot([0.12,0.12], [0,Solutions2[120]],dashes=[6, 3],c='k')  # dashes=[6, 3]表示虚线长6,空白长3 
plt.plot([0.2,0.2], [0.2,Solutions2[200]],dashes=[6, 3],c='k')
# 添加箭头和文本
ax.annotate("Boyfriends's\nbusiness\nIRR", xy=(0.363, 200), xytext=(0.363, 2000),
             arrowprops=dict(arrowstyle="->",connectionstyle="angle3,angleA=0,angleB=-90"), 
             horizontalalignment='center',verticalalignment ='bottom')  #,fontsize='x-large')
ax.annotate("IRR", xy=(0.234, -500), xytext=(0.234, -2000),
             arrowprops=dict(arrowstyle="->",connectionstyle="angle3,angleA=0,angleB=-90"),
             horizontalalignment='center',verticalalignment ='bottom')  #,fontsize='x-large')
ax.annotate("Crossover", xy=(0.2, Solutions2[200]), xytext=(0.25, Solutions2[200]+1000),
             arrowprops=dict(arrowstyle="->",connectionstyle="arc3, rad=0"),
             horizontalalignment='center',verticalalignment ='bottom')  #,fontsize='x-large')
ax.annotate(' Cost of \nCapital', xy=(0.12, -500), xytext=(0.12, -2000),
             arrowprops=dict(arrowstyle="->",connectionstyle="angle3,angleA=0,angleB=-90"),
             horizontalalignment='center',verticalalignment ='top')  
# 添加纯文本
plt.text( 0.13, Solutions3[120],'Delivery Service\n (10 drones)', c='k')
plt.text( 0.363, Solutions1[363]-1200, 'Delivery Service \n(2 drones)', c='k', horizontalalignment='center')
# 在l3和23.4%交叉处添加白色矩形
for label in ax.get_xticklabels()[2:3]:    
    label.set_fontsize(16)    
    label.set_bbox(dict(facecolor='white', edgecolor='None', alpha=2))
# 秀出你的图片
plt.show()

在这里插入图片描述
至此,大功告成。基本实现了与原图百分之90的相似度。

全部代码如下!

# 复现一幅教科书上看到的图
import numpy as np
import matplotlib.pyplot as plt
import numpy_financial as npf
# 生成数据
R = np.linspace(0,1,1001)[1:]
Solutions1 = []          # 存储l1的全部NPV
for r in R:
    rate = r             # 每期实际利率
    nper = 3             # 期数
    c = 5000             # 负号表示现金流支出
    fv = 0               # 终值
    when = 0             # 最后一个0表明现金六期末开始,但起始点都是期初也就是会折现到期初
    Solutions1.append(-npf.pv(rate, nper, c, fv, when)-10000)  #  将现值调为正数,然后带入NPV的计算公式
Solutions2 = []
for r in R:
    rate = r             # 每期实际利率
    nper = 3             # 期数
    c = 6000             # 负号表示现金流支出
    fv = 0               # 终值
    when = 0             # 最后一个0表明现金六期末开始,但起始点都是期初也就是会折现到期初
    Solutions2.append(-npf.pv(rate, nper, c, fv, when)-10000)  #  将现值调为正数,然后带入NPV的计算公式
Solutions3 = []
for r in R:
    rate = r             # 每期实际利率
    nper = 3             # 期数
    c = 25000            # 负号表示现金流支出
    fv = 0               # 终值
    when = 0             # 最后一个0表明现金六期末开始,但起始点都是期初也就是会折现到期初
    Solutions3.append(-npf.pv(rate, nper, c, fv, when)-50000)  #  将现值调为正数,然后带入NPV的计算公式
# 开始绘图
# 全局设定字体类型和大小
plt.rcParams['font.family'] = 'Times New Roman'
plt.rcParams['font.size'] = 18
# 设置ylabel字体
font1 = {'family': 'Times New Roman',
         'weight': 'bold',  # bold:粗体
         'style':'normal',
         'size': 16}
# 设置subtitle字体
font2 = {'family': 'Times New Roman',
         'weight': 'bold',
        'style':'normal',  # normal:普通;italic:斜体
         'size': 16}
# 生成画布
fig = plt.figure(figsize=(10,8))
# 绘制三条曲线
plt.plot(R, Solutions1, c='#c57746', linewidth=2, zorder=1)   
plt.plot(R, Solutions2, c='#408a7a', linewidth=2)
plt.plot(R, Solutions3, c='purple', linewidth=2, zorder=1)
# 设置xy边界
plt.ylim([-4000,12000])
plt.xlim(0,0.4)
# 填充颜色
plt.fill_between(R[:363], Solutions1[:234]+[0]*129, color='#a7cd3a')  # y2默认为0
plt.fill_between(R[:363], Solutions1[:234]+[0]*129, Solutions2[:363],color='#a7cd3a', alpha=0.5)
plt.fill_between(R[363:], Solutions2[363:], color='#d26c53')  # y2默认为0
plt.fill_between(R[234:], Solutions1[234:], [0]*129+Solutions2[363:], color='#d26c53', alpha=0.5)
# 让包围线消失
ax = plt.gca()  # 获得当前Axes对象
ax.spines['right'].set_color('none')  # 包围线消失
ax.spines['top'].set_color('none')
# 移动x轴并让刻度显示在x轴上方
ax.spines['bottom'].set_color('none')
ax.spines['bottom'].set_position(('axes',0.26))                                            # 设置x轴的tick的位置,移动到NPV=0上面
plt.plot(R, np.array(R)*0, c='k', linewidth=0.8)                                            # 自己画横轴
# 调整刻度间隔,并在xtick后添加"%",在ytick前添加"$"。
plt.xticks([0.12,0.2,0.234,0.363], [str(i) + '%' for i in [12,20,23.4,36.3]],x=0,y=-100)   # 设置xtick
plt.yticks([i*2000-4000 for i in range(9)],['$'+str(i*2000-4000) for i in range(9)])       # 设置ytick
# 添加xylabel
plt.ylabel('NPV', fontdict=font1, weight='bold')
plt.suptitle('Discount Rate', fontproperties=font2, y=0.1)  # font properties:字体属性
# 添加黑点
plt.scatter(0.2,Solutions2[200], c='k')
plt.scatter(0.12,Solutions1[120], c='k')
plt.scatter(0.12,Solutions2[120], c='k')
plt.scatter(0.12,Solutions3[120], c='k')
# 添加虚线
plt.plot([0.12,0.12], [0,Solutions2[120]],dashes=[6, 3],c='k')  # dashes=[6, 3]表示虚线长6,空白长3 
plt.plot([0.2,0.2], [0.2,Solutions2[200]],dashes=[6, 3],c='k')
# 添加箭头和文本
ax.annotate("Boyfriends's\nbusiness\nIRR", xy=(0.363, 200), xytext=(0.363, 2000),
             arrowprops=dict(arrowstyle="->",connectionstyle="angle3,angleA=0,angleB=-90"), 
             horizontalalignment='center',verticalalignment ='bottom')  #,fontsize='x-large')
ax.annotate("IRR", xy=(0.234, -500), xytext=(0.234, -2000),
             arrowprops=dict(arrowstyle="->",connectionstyle="angle3,angleA=0,angleB=-90"),
             horizontalalignment='center',verticalalignment ='bottom')  #,fontsize='x-large')
ax.annotate("Crossover", xy=(0.2, Solutions2[200]), xytext=(0.25, Solutions2[200]+1000),
             arrowprops=dict(arrowstyle="->",connectionstyle="arc3, rad=0"),
             horizontalalignment='center',verticalalignment ='bottom')  #,fontsize='x-large')
ax.annotate(' Cost of \nCapital', xy=(0.12, -500), xytext=(0.12, -2000),
             arrowprops=dict(arrowstyle="->",connectionstyle="angle3,angleA=0,angleB=-90"),
             horizontalalignment='center',verticalalignment ='top')  
# 添加纯文本
plt.text( 0.13, Solutions3[120],'Delivery Service\n (10 drones)', c='k')
plt.text( 0.363, Solutions1[363]-1200, 'Delivery Service \n(2 drones)', c='k', horizontalalignment='center')
# 在l3和23.4%交叉处添加白色矩形
for label in ax.get_xticklabels()[2:3]:    
    label.set_fontsize(16)    
    label.set_bbox(dict(facecolor='white', edgecolor='None', alpha=2))
# 秀出你的图片
plt.show()

困惑和不足

  1. 通过什么方法来调整 l 3 l_3 l3和“ 23.4 % 23.4\% 23.4%”交界处白色block的位置?

  2. 原图中的到底是什么字体?有点像写手写印刷体。

  3. 原图中的字体有点拉长,较细,怎么实现?没办法通过调整字体的stretch和weight来实现。

  4. 可以看到,这个代码啊,越写越长。那在我下一次要画类似的图时,难道又要写那么长的代码吗?
    能否用一下marplotlob的style功能,将上面的图保存成一个风格?

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值