之前我们学习python的爬虫,在数据科学中,爬虫是获取数据的重要工具。Python的作用并不止步于此,我们还可以通过python进行数据分析。比如我们现在有两个项目
- 项目A: 初始投入1500,之后6年每年获得收益450
- 项目B: 初始投入1200,到第六年年底一次性获得3000
我们投资需要用的钱是借来的,借来是有利率的,
我们要通过python来对这两项投资进行收益分析(净现值曲线),通过作图来进行判断,根据利率的变化来选择不同的项目
工具准备
作为工业级通用编程语言,python之所以能在数据领域有一席之地,与matplotlib,numpy,Scipy的存在有很大的关系,而我们这次使用的是Pylab,一个类似于matlab的集成平台,它整合了:
- numpy:扩展科学计算程式库,支持高阶纬度阵列,矩阵运算,以及大量函数程式。底层纯C开发,性能极其强悍
- scipy :科学计算程式库,主要用于数学计算,与numpy一样,底层纯C开发
- matplotlib: 标准的绘图库,使用它我们可以绘制精美的图表
- ipython:
- jupyter (ipython notebook):
本文章我们只使用numpy和matplotlib来做基础的绘图操作,后面主要使用matplotlib,使用到了numpy的部分有标注
import pylab as plt
数据准备
根据之前的项目的细节,我们分别使用两个列表来表示项目资金的流动
项目A, 初始投入1500,之后6年每年获得收益450,我们得到
1 | proj_A = [-1500,450,450,450,450,450,450] |
项目B, 初始投入1200,到第六年年底一次性获得3000
1 | proj_B = [-1200,0,0,0,0,0,3000] |
接下来我们设置利率变化区间
1 2 3 | cost_of_capital_rate =[i/100 for i in range(0,21)] #返回如下列表 [0.0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.2] |
接下来我们生成利率区间下对应的NPV,刚好numpy为我们提供了npv的函数,格式为npv(利率,现金流量列表)
1 2 | proj_A_npv = [plt.npv(i,proj_A) for i in cost_of_capital_rate] proj_B_npv = [plt.npv(i,proj_B) for i in cost_of_capital_rate] |
在这里我们使用了列表解析式直接生成,如果这样不习惯我们可以改写成:
1 2 3 4 5 6 7 8 | proj_A_npv = [] proj_B_npv = [] for i in cost_of_capital: npv_A = plt.npv(i,proj_A) npv_B = plt.npv(i,proj_B) proj_A_npv.append() proj_B_npv.append() |
我们来看一下proj_A_npv里面是什么:
[1200.0, 1107.9644135606968, 1020.6439008106788, 937.73614974518443, 858.96158553585792, 784.06143027035068, 712.79594670242523, 644.94284689384756, 580.29584878253513, 518.66336560391937, 459.86731475800104, 403.74203418221509, 350.13329558504614, 298.89740503913117, 249.90038244412847, 203.01721226533152, 158.13115874789341, 115.13313951123695, 73.921152054113463, 34.399748257616636, -3.5204475308640326]
这些数字就是与我们之前不同利率相对应的NPV,proj_B_npv也是一样
到这里我们的数据就都准备好了,进入下一步,绘图。
作图
新的图表
plt.figure('A / B NPV Profile')
重叠与清除
在我们开启了一个新的figure后,我们的所有操作都会叠加到这张图上。比如画了一条曲线后我们可以再添加一条曲线。如果我们不使用清除操作的话,在下次使用该figure时很可能和之前的曲线再次叠加也就是四条曲线。这样干扰了我们正常做图。所以我们每次声明新图表之后最好添加一个清除,日后方便重复使用
plt.clf()
绘制图表
1 2 | plt.plot(cost_of_capital_rate,proj_A_npv,'#92e3d7',label = 'Project A',linewidth = 2.0) plt.plot(cost_of_capital_rate,proj_B_npv,'#f4ab84',label = 'Project B',linewidth = 2.0) |
前面两个分别是横坐标纵坐标的变量,我们使用利率为横坐标,npv为纵坐标,后面的参数分别是
- '#92e3d7' 这一处代表线条的颜色,我们可以使用rgb或者其他的参数,这里使用了hex
- label = 'Project A' 这里表示该条线在图例中的标签
- linewidth 线条的粗细
到这里我们就已经完成了图片的绘制。我们可以通过show()方法来显示图片。
修改细节
尽管我们已经完成了基本的绘图工作,我们的图还是有一些粗糙,并不能让读者更直观地理解数据,这时我们需要修改细节
上限
我们对y轴的上限进行设置,从0-2000,
1 | plt.ylim(0,2000) |
2000这个值是怎么来的,很简单,试出来的。在我们之前绘制的片是有刻度和上限的,但是这个刻度和上限有时候并不能够最好地展示我们的折线图,当我们看到这个折线与y轴交汇最高点接近并低于2000时,我们使用2000作为上限可以最好地展示我们的图片
刻度
现在我们要修改我们的刻度,默认刻度间距有点大,这时候我们并不能很好地对数据进行判断。
这里我们使用了numpy的arange功能,快速生成一个刻度列表,使用方法是
1 2 3 4 | numpy.arange(起点,终点(不包含),间隔) >>>plt.arange(0, 2000+1, 400) 生成了以下列表 [0,400,800,1200,1600,2000] |
接着我们分别使用yticks,xticks来修改x,y轴坐标
1 2 | plt.yticks(plt.arange(0, 2000+1, 400)) plt.xticks(plt.arange(0,0.26+0.01,0.02)) |
图例
为了让读者更好地分辨两条曲线,我们在这里添加图例
plt.legend()
如果我们要标记图例的位置为右上方,我们添加参数loc
plt.legend(loc = 'upper right')
标题
最后我们添加标题
plt.title('Project A/B NPV Analysis')
好了,使用show()方法来显示我们的NPV Profile吧
plt.show()
结果如下:
交点
根据上面的图我们可以看出在交点前后NPV最大的公司是不同的,但是我们并不能够知道这个点所代表的利率是多少,所以我们需要将它在图上进行标记。我们先求出这个点的坐标。什么?求出来还要标吗?是的,因为图上的线是点连接成的,有一定偏差;而且这份图不一定只是自己看的,需要求出坐标画图。
交点坐标
在求出交点坐标之前,我们需要知道这个交点背后的意义是什么。在该点的利率下,项目A和项目B的净现值(NPV)是持平的。求出两个项目现金流量的差,然后我们使用这个差求出该点的IRR(IRR是NPV为0时的利率)作为横坐标。最后我们通过该点求出项目A或者B的NPV作为纵坐标。
先对AB求差:
1 2 3 4 5 6 | i = 0 intersection = [] for i in len(proj_A): intersection.append(proj_A[i] - proj_B[i]) 得到[ -300, 450, 450, 450, 450, 450, -2550] |
有没有更简单的?有
intersection = [proj_A[i] - proj_B[i] for i in len(proj_A)]
有没有更简单更快速的?当然有,这里我们用numpy的array来代替python的list,然后直接进行加减运算,
1 2 | a= plt.array(proj_A)#将proj_A从list转换到array b= plt.array(proj_B) |
这时候我们可以看到a是这个样子
1 | array([-1500, 450, 450, 450, 450, 450, 450]) |
然后我们只需
intersection = a-b
就直接得到了我们想要的结果,这么做另一个好处是当我们使用numpy时,速度快了不止一个数量级,具备了大型运算的能力
接下来我们使用numpy的irr和npv函数即可求出x,y坐标
1 2 | intersection_irr = plt.irr(intersection) intersection_npv =plt.npv(intersection_irr,a) |
文字标记
matplotlib提供了annotate函数来帮助我们对图进行标记
1 | plt.annotate("intersection = %.4f"%intersection_irr,xy=(intersection_irr,intersection_npv),xytext=(intersection_irr+0.02,intersection_npv+100),arrowprops=dict(facecolor='black', shrink=0.05)) |
- 开头的'intersection = %.4f'%intersection_irr 是我们想在图片上显示的内容%.4f'%intersection_irr表示取intersection_irr小数点后四位
- xy 参数接收交点的横纵坐标,是箭头指向的位置
- xytext 是文字所处的坐标,注意修改时要考虑刻度
- arrowprops 是我们箭头的属性,shrink = 0.05表示箭头长度只有95%,保留和文字之间的空隙
结果和分析
好了,这次再来看我们的折线图
从上图我们可以得出
1. A / B 的盈利利率区间,盈利范围
2. 在利率为11.01%时,A/B净现值相等,低于该点B项目收益高,高于该点A项目收益高
这么看着好麻烦,我们为什么要使用python而不是EXCEL这样更方便的工具?
这次只是一个小的例子。在数据科学中,我们往往需要处理成千上万的数据,如果使用excel的话,性能可能会差强人意或者会很卡(比如五千家公司十年来每天的股票收盘价格。。。另外python可以通过api接口,爬虫或者数据库直接对接从而获取大量数据,非常方便
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | import pylab as plt proj_A = [-1500,450,450,450,450,450,450] proj_B = [-1200,0,0,0,0,0,3000] cost_of_capital_rate =[i/100 for i in range(0,21)] proj_A_npv = [plt.npv(i,proj_A) for i in cost_of_capital_rate] proj_B_npv = [plt.npv(i,proj_B) for i in cost_of_capital_rate] plt.figure('A / B NPV Profile') plt.clf() plt.plot(cost_of_capital_rate,proj_A_npv,'#92e3d7',label = 'Project A',linewidth = 2.0) plt.plot(cost_of_capital_rate,proj_B_npv,'#f4ab84',label = 'Project B',linewidth = 2.0) plt.ylim(0,2000) plt.yticks(plt.arange(0, 2000+1, 400)) plt.xticks(plt.arange(0,0.24+0.01,0.02)) plt.legend(loc = 'upper right') plt.title('Project A/B NPV Analysis') a= plt.array(proj_A) b= plt.array(proj_B) intersection = a-b intersection_irr = plt.irr(intersection) intersection_npv =plt.npv(intersection_irr,a) plt.annotate("intersection = %.4f"%intersection_irr,xy=(intersection_irr,intersection_npv),xytext=(intersection_irr+0.02,intersection_npv+100),arrowprops=dict(facecolor='black', shrink=0.05)) plt.show() |