用 Python 绘制现金流量图 2 :进阶技巧


上一次我发了博客文章 用 Python 绘制现金流量图

有人在后台里私信问我:

我们是想把csv文件读取入,列表

将csv文件读取入列表,不同的是我们的现金流量表包括更多行的信息,包括所得税 折旧等,没办法使用你的方法做图,我们想先计算出年净现金流然后计算,想咨询一下如何实现。

安排。

用到的库


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

从 .csv 格式的文件里读取现金流

导入 CSV 文件

原现金流量表如下:

序号 项目 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
1 现金流入 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 营业收入 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 回收固定资产余值 2777.66
1.3 回收流动资金 2845.00
2 现金流出 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 项目资本金 1390.00 1990.00 1190.00
2.2 借款本金偿还 509.14 509.14 509.14 509.14 509.14 509.14 509.14 509.14 509.14 509.14 2845.00
2.3 借款利息支付 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 经营成本 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 营业税金及附加 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 所得税 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 维持运营费用 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 净现金流量 -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()

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

完美。

猜你喜欢

转载自blog.csdn.net/BOXonline1396529/article/details/130864614
今日推荐