Drawing Cash Flow Diagrams with Python 2: Advanced Tips


Last time I blogged about Cash Flow Diagrams in Python

Someone asked me in a private message in the background:

We want to read the csv file into the list

Read the csv file into the list. The difference is that our cash flow statement includes more rows of information, including income tax depreciation, etc. There is no way to use your method to make a graph. We want to calculate the annual net cash flow first and then calculate it. Ask how to do it.

arrange.

library used


' 导入相应的库 '
import matplotlib.pyplot as plt
import numpy as np
import sys
import os

Read cash flow from .csv file

import CSV file

The original cash flow statement is as follows:

serial number project 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
1 Cash inflow 8895.00 7750.00 8590.00 8590.00 8590.00 8590.00 8590.00 8590.00 8590.00 8590.00 8590.00 8590.00 11367.66
1.1 operating income 6050.00 7750.00 8590.00 8590.00 8590.00 8590.00 8590.00 8590.00 8590.00 8590.00 8590.00 8590.00 8590.00
1.2 Recovery of residual value of fixed assets 2777.66
1.3 Recovery of working capital 2845.00
2 cash outflow 1390.00 1990.00 1190.00 5428.55 6655.63 7244.70 7209.02 7173.35 7137.67 7101.99 7066.32 7030.64 6994.96 6450.15 6450.15 8773.08
2.1 Project capital 1390.00 1990.00 1190.00
2.2 loan principal repayment 509.14 509.14 509.14 509.14 509.14 509.14 509.14 509.14 509.14 509.14 2845.00
2.3 loan interest payment 758.76 711.19 663.62 616.06 568.49 520.92 473.35 425.78 378.21 330.65 283.08 283.08 283.08
2.4 Operating costs 3630.00 4650.00 5155.00 5155.00 5155.00 5155.00 5155.00 5155.00 5155.00 5155.00 5155.00 5155.00 5155.00
2.5 Business tax and surcharges 345.00 442.00 490.00 490.00 490.00 490.00 490.00 490.00 490.00 490.00 490.00 490.00 490.00
2.6 income tax 185.65 343.29 426.93 438.82 450.72 462.61 474.50 486.39 498.28 510.18 522.07 522.07 0.00
2.7 Sustaining operating expenses 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
3 net cash flow -1390.00 -1990.00 -1190.00 3466.45 1094.37 1345.30 1380.98 1416.65 1452.33 1488.01 1523.68 1559.36 1595.04 2139.85 2139.85 2594.58

Text 表格显示看不清的话可以看下面的图片,在Excel中显示如下:

Excel表格

首先,将数据导入 CSV 表格并转置,转置是为了方便 Python 读取数据。

注意表头尽量使用英文,这样不容易给 Python 造成困扰。

如果一定要使用中文也行,但是我没试过,不知道可能出现什么问题。
CSV表格

data.csv 文件内容如下:


Year,operating_income,residual_value_of_fixed_assets_recovered,working_capital_recovered,project_capital,repayment_of_loan_principal,payment_of_loan_interest,operating_costs,business_taxes_and_surcharges,income_tax,maintain_operating_expenses
1,0,0,0,1390,0,0,0,0,0,0
2,0,0,0,1990,0,0,0,0,0,0
3,0,0,0,1190,0,0,0,0,0,0
4,6050,0,2845,0,509.142,758.761574,3630,345,185.6479034,0
5,7750,0,0,0,509.142,711.1931666,4650,442,343.2900052,0
6,8590,0,0,0,509.142,663.6247592,5155,490,426.9321071,0
7,8590,0,0,0,509.142,616.0563518,5155,490,438.8242089,0
8,8590,0,0,0,509.142,568.4879444,5155,490,450.7163108,0
9,8590,0,0,0,509.142,520.919537,5155,490,462.6084126,0
10,8590,0,0,0,509.142,473.3511296,5155,490,474.5005145,0
11,8590,0,0,0,509.142,425.7827222,5155,490,486.3926163,0
12,8590,0,0,0,509.142,378.2143148,5155,490,498.2847182,0
13,8590,0,0,0,509.142,330.6459074,5155,490,510.17682,0
14,8590,0,0,0,0,283.0775,5155,490,522.0689219,0
15,8590,0,0,0,0,283.0775,5155,490,522.0689219,0
16,8590,2777.65825,0,0,2845,283.0775,5155,490,0,0

读取 CSV 文件

读取 CSV 文件,常用的方法有 panda.csv_read()numpy.loadtxt()

这里使用后者,因为前者我尝试过不太行。(原因不明)

参考这篇博文:关于python中numpy.loadtxt()的详细用法

参数 作用
fname 被读取的文件名(文件的相对地址或者绝对地址)
dtype 指定读取后数据的数据类型
comments 跳过文件中指定参数开头的行(即不读取)
delimiter 指定读取文件中数据的分割符
converters 对读取的数据进行预处理
skiprows 选择跳过的行数
usecols 指定需要读取的列
unpack 选择是否将数据进行向量输出
encoding 对读取的文件进行预编码

显然,我们这里要跳过第一行的表头分别读取每一列。

故有 skiprows=1usecols=[你要读取的列数]

我在这里的示例数据是已经计算好营业税金、所得税的。

但是考虑到可能会需要单独用利率计算营业税的情形,这里作为示例选择把每一列单独作为一个变量读取。

代码如下:


" 从 csv 数据表中读取相应的数据序列 "

# 这里把每一列数据分别导入
# 方便在有必要的情况下
# 针对各个现金流做单独的分析

# 年份
Year = np.loadtxt(open("data.csv","rb"),delimiter=',',
                   skiprows=1,
                   usecols=[0])
# 营业收入
operating_income = np.loadtxt(open("data.csv","rb"),
                               delimiter=',',
                               skiprows=1,
                               usecols=[1])
# 回收固定资产余值
residual_value_of_fixed_assets_recovered = np.loadtxt(open("data.csv","rb"),
                                                       delimiter=',',
                                                       skiprows=1,
                                                       usecols=[2])
# 回收流动资金
working_capital_recovered = np.loadtxt(open("data.csv","rb"),
                                        delimiter=',',
                                        skiprows=1,
                                        usecols=[3])
# 项目资本金
project_capital = np.loadtxt(open("data.csv","rb"),
                              delimiter=',',
                              skiprows=1,
                              usecols=[4])
# 借款本金偿还
repayment_of_loan_principal = np.loadtxt(open("data.csv","rb"),
                                          delimiter=',',
                                          skiprows=1,
                                          usecols=[5])
# 借款利息支付
payment_of_loan_interest = np.loadtxt(open("data.csv","rb"),
                                       delimiter=',',
                                       skiprows=1,
                                       usecols=[6])
# 经营成本
operating_costs = np.loadtxt(open("data.csv","rb"),
                              delimiter=',',
                              skiprows=1,
                              usecols=[7])
# 营业税金及附加
business_taxes_and_surcharges = np.loadtxt(open("data.csv","rb"),
                                            delimiter=',',
                                            skiprows=1,
                                            usecols=[8])
# 所得税
income_tax = np.loadtxt(open("data.csv","rb"),
                         delimiter=',',
                         skiprows=1,
                         usecols=[9])
# 维持运营费用
maintain_operating_expenses = np.loadtxt(open("data.csv","rb"),
                                          delimiter=',',
                                          skiprows=1,
                                          usecols=[10])

解决文件路径问题

如果你和我一样使用 Visual Stdio Code 之类的开发环境,可能会遇到执行代码的时候由于终端 shell 不在代码的执行路径下而报错的情形。我在这篇博文里找到了解决方案:

Pandas读取CSV的时候报错文件不存在的经验小记

解决的代码如下:


import sys
import os

" 搜索文件的绝对路径 "

# Python 日常抽风,找不到 data.csv 的文件路径
# 用这段代码寻找
# 参考 https://blog.csdn.net/wuzhongqiang/article/details/118332777

BASE_DIR = os.path.dirname(os.path.abspath(__file__))  
sys.path.insert(0, os.path.join(BASE_DIR))
sys.path.insert(0, os.path.join(BASE_DIR, 'data'))  
# 把data所在的绝对路径加入到了搜索路径,这样也可以直接访问dataset.csv文件了

# 这句代码进行切换目录
os.chdir(BASE_DIR)

接下来就可以进行现金流的计算了。

现金流的计算

分别计算现金流入、流出,还有净现金流量。

注意这里有一个格式转换的问题:

通过 numpy.loadtxt() 读取的数据为 numpy.array 类型的,这里利用 .tolist() 方法转化为列表格式。


" 现金流的计算 "

# 现金流入
cash_inflow = operating_income + \
              residual_value_of_fixed_assets_recovered + \
              working_capital_recovered
CI = cash_inflow.tolist()

# 现金流出
cash_outflow = project_capital + \
               repayment_of_loan_principal + \
               payment_of_loan_interest + \
               operating_costs + \
               business_taxes_and_surcharges + \
               income_tax
CO = cash_outflow.tolist()

# 净现金流量
net_cash_flow = cash_inflow - cash_outflow
NCF = net_cash_flow.tolist()

这里也可以作税率计算之类的操作。篇幅有限在这里就不演示了。

最后把数据 Append 到我们的二阶列表里面来:


# 用二阶列表保存现金流量的值
A = []
A.append(CI)
A.append(CO)
A.append(NCF)

绘图设置

分别为现金的流入、流出和净现金流量、坐标轴和标题设置颜色。


" 颜色设置 "
arrow_color = [ 'green', 'red', 'black' ]
axis_color  = 'black'
title_color = 'black'

因为这一次画的图表是极其复杂的,所以要单独为数字和标题设置字体大小,防止图标元素混杂在一起,看不清。


" 调整字体大小 "
numberFontSize = 8
titleFontSize = 15

上次的文章里面没说清楚的一件事情:这里有一个 distance 变量,用于调整图表元素的离散程度。


" 调整图表元素离散程度 "
distance=1000

如果图标元素太紧凑,可以适当减小 distance 的值。相反,如果图表元素已经超出绘图区看不见了,则增大它的值。

完整代码及绘图效果

其余的代码和上一次的文章里面基本一致。

完整代码如下:


"""    绘制资金流量图    """

" 颜色设置 "
arrow_color = [ 'green', 'red', 'black' ]
axis_color  = 'black'
title_color = 'black'

" 调整字体大小 "
numberFontSize = 8
titleFontSize = 15

" 调整图表元素离散程度 "
distance=1000

' 导入相应的库 '
import matplotlib.pyplot as plt
import numpy as np
import sys
import os

" 搜索文件的绝对路径 "

# Python 日常抽风,找不到 data.csv 的文件路径
# 用这段代码寻找
# 参考 https://blog.csdn.net/wuzhongqiang/article/details/118332777

BASE_DIR = os.path.dirname(os.path.abspath(__file__))  
sys.path.insert(0, os.path.join(BASE_DIR))
sys.path.insert(0, os.path.join(BASE_DIR, 'data'))  
# 把data所在的绝对路径加入到了搜索路径,这样也可以直接访问dataset.csv文件了

# 这句代码进行切换目录
os.chdir(BASE_DIR)

" 从 csv 数据表中读取相应的数据序列 "

# 这里把每一列数据分别导入
# 方便在有必要的情况下
# 针对各个现金流做单独的分析

# 年份
Year = np.loadtxt(open("data.csv","rb"),delimiter=',',
                   skiprows=1,
                   usecols=[0])
# 营业收入
operating_income = np.loadtxt(open("data.csv","rb"),
                               delimiter=',',
                               skiprows=1,
                               usecols=[1])
# 回收固定资产余值
residual_value_of_fixed_assets_recovered = np.loadtxt(open("data.csv","rb"),
                                                       delimiter=',',
                                                       skiprows=1,
                                                       usecols=[2])
# 回收流动资金
working_capital_recovered = np.loadtxt(open("data.csv","rb"),
                                        delimiter=',',
                                        skiprows=1,
                                        usecols=[3])
# 项目资本金
project_capital = np.loadtxt(open("data.csv","rb"),
                              delimiter=',',
                              skiprows=1,
                              usecols=[4])
# 借款本金偿还
repayment_of_loan_principal = np.loadtxt(open("data.csv","rb"),
                                          delimiter=',',
                                          skiprows=1,
                                          usecols=[5])
# 借款利息支付
payment_of_loan_interest = np.loadtxt(open("data.csv","rb"),
                                       delimiter=',',
                                       skiprows=1,
                                       usecols=[6])
# 经营成本
operating_costs = np.loadtxt(open("data.csv","rb"),
                              delimiter=',',
                              skiprows=1,
                              usecols=[7])
# 营业税金及附加
business_taxes_and_surcharges = np.loadtxt(open("data.csv","rb"),
                                            delimiter=',',
                                            skiprows=1,
                                            usecols=[8])
# 所得税
income_tax = np.loadtxt(open("data.csv","rb"),
                         delimiter=',',
                         skiprows=1,
                         usecols=[9])
# 维持运营费用
maintain_operating_expenses = np.loadtxt(open("data.csv","rb"),
                                          delimiter=',',
                                          skiprows=1,
                                          usecols=[10])

" 现金流的计算 "

# 现金流入
cash_inflow = operating_income + \
              residual_value_of_fixed_assets_recovered + \
              working_capital_recovered
CI = cash_inflow.tolist()

# 现金流出
cash_outflow = project_capital + \
               repayment_of_loan_principal + \
               payment_of_loan_interest + \
               operating_costs + \
               business_taxes_and_surcharges + \
               income_tax
CO = cash_outflow.tolist()

# 净现金流量
net_cash_flow = cash_inflow - cash_outflow
NCF = net_cash_flow.tolist()


' 设置绘图标题 '
plt.title("企业的现金流入、现金流出及净的现金流量图", c=title_color, fontsize=titleFontSize)
plt.ylabel("资金(万元)")

# 用二阶列表保存现金流量的值
A = []
A.append(CI)
A.append(CO)
A.append(NCF)

' 根据列表绘制资金流量图 '
# 区分正负值
# 是为了让负值的标签位置偏下不影响观感
for j in range(0,len(A)):
        for i in range(0,len(A[j])):
                if A[j][i] > 0:
                        plt.arrow(i, 0, 0, A[j][i]-distance, 
                                  fc=arrow_color[j], 
                                  ec=arrow_color[j], 
                                  shape="full", 
                                  head_width=0.1, 
                                  head_length=distance, 
                                  overhang=0.5)
                        plt.text(i+len(A)*0.01, 
                                 A[j][i]+distance, 
                                 str(round(A[j][i],2)),
                                 fontsize=numberFontSize)
                elif A[j][i] < 0:
                        plt.arrow(i, 0, 0, A[j][i]+distance, 
                                  fc=arrow_color[j], 
                                  ec=arrow_color[j], 
                                  shape="full", 
                                  head_width=0.1, 
                                  head_length=distance, 
                                  overhang=0.5)
                        plt.text(i+len(A)*0.01, 
                                 A[j][i]-distance, 
                                 str(round(A[j][i],2)),
                                 fontsize=numberFontSize)
                        
# 添加一些标签
plt.text(0, 250000, r'利率 $i_{c} = 10 %$')

' 设置图表中各个元素的特征 '
# 下面这些其实没啥,主要都是美化图表

ax = plt.gca()

# 设置四个坐标轴不可见
ax.spines['top'].set_visible(False) # 设置坐标轴,下同
ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['bottom'].set_visible(False)

# 把 X 轴及其数据标签挪到图表当中
ax.spines['bottom'].set_position(('data',0))
plt.setp( ax.xaxis.get_majorticklabels(), ha="left" ) # right 表示 X 坐标数据标签向右对齐
for tick in ax.xaxis.get_major_ticks(): 
  tick.label1.set_fontsize(numberFontSize)
plt.arrow(-0.1, 0, len(A[0])+1.2, 0, 
          fc=axis_color, 
          ec=axis_color, 
          shape="full", 
          head_width=distance*0.5, 
          head_length=0.3, 
          overhang=0.5)

# 隐藏 y 坐标
plt.yticks([])

# 设置 X 轴的刻度为1
x_major_locator=plt.MultipleLocator(1)      # 把x轴的刻度间隔设置为1,并存在变量里
ax.xaxis.set_major_locator(x_major_locator) # 把x轴的主刻度设置为1的倍数

# 设置图表 X Y 范围,防止绘图区太大或太小
plt.xlim(-0.1, len(A[0])+1.4)
plt.ylim(-15*distance,15*distance)

'添加图例'
line1, = plt.plot(1,1, 'g', 
                  label='现金流入')
line2, = plt.plot(2,2, 'r', 
                  label='现金流出')
plt.legend(handles=[line1, line2], 
           loc='lower right')

' 调整中文显示 '
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False    # 用来正常显示负号

' 绘图 '
plt.show()

最后绘制的现金流量图效果如下:
现金流量图

完美。

Guess you like

Origin blog.csdn.net/BOXonline1396529/article/details/130864614