11.1 Plotting Using PyLab

11 PLOTTING AND MORE ABOUT CLASSES

Often text is the best way to communicate information, but sometimes there is a lot of truth to the Chinese proverb, 圖片的意義可以表達近萬字. Yet most programs rely on textual output to communicate with their users. Why? Because in many programming languages presenting visual data is too hard. Fortunately, it is simple to do in Python.

11.1 Plotting Using PyLab

PyLab is a Python standard library module that provides many of the facilities of MATLAB, “a high- level technical computing language and interactive environment for algorithm development, data visualization, data analysis, and numeric computation.” Later in the book we will look at some of the more advanced features of PyLab, but in this chapter we focus on some of its facilities for plotting data. Other plotting facilities are introduced in later chapters. A complete user’s guide to the plotting capabilities of PyLab is at the Web site: User’s Guide — Matplotlib 2.0.0 documentation

There are also a number of Web sites that provide excellent tutorials. We will not try to provide a user’s guide or a complete tutorial here. Instead, in this chapter we will merely provide a few example plots and explain the code that generated them. Other examples appear in later chapters.

1.A simple example

(1) Simple

Let’s start with a simple example that uses pylab.plot to produce two plots. Executing:

import pylab
pylab.figure(1)
pylab.plot([1,2,3,4,5],[1,7,3,5,12])
pylab.show()

will cause a window to appear on your computer monitor. Its exact appearance may depend on your Python environment, but it will look similar to Figure 11.1 (which was produced using Anaconda). If you run this code with the default parameter settings of most installations of PyLab, the line will probably not be as thick as the line in Figure 11.1. We have used nonstandard default values for line width and font sizes so that the figures will reproduce better in black and white. We discuss how this is done later in this section.

Figure 11.1 A simple plot

Figure 11.1 A simple plot
Figure 11.1 A simple plot
1. Sequences of the same length

The bar at the top of the plot contains the name of the window, in this case “Figure 1.” The middle section of the window contains the plot generated by the invocation of pylab.plot. The two parameters of pylab.plot must be sequences of the same length. The first specifies the x-coordinates of the points to be plotted, and the second specifies the y-coordinates. Together, they provide a sequence of four <x, y> coordinate pairs,[(1,1), (2,7), (3,3), (4,5)]. These are plotted in order. As each point is plotted, a line is drawn connecting it to the previous point.

2. The problem of pylab.show()

The final line of code, pylab.show(), causes the window to appear on the computer screen. In some Python environments, if that line were not present, the figure would still have been produced, but it would not have been displayed. This is not as silly as it at first sounds, since one might well choose to write a figure directly to a file, as we will do later, rather than display it on the screen.

3. Introduction of the Buttons:

The bar at the top of the window contains a number of push buttons. The rightmost button pops up a window with options that can be used to adjust various aspects of the figure. The next button to the left is used to write the plot to a file. The button to the left of that is used to adjust the appearance of the plot in the window. The next two buttons are used for zooming and panning. The two buttons that look like arrows are used to see previous views (like the forward and backward arrows of a Web browser). And the button on the extreme left is used to restore the figure to its original appearance after you are done playing with other buttons.

(2) The multiple figures

It is possible to produce multiple figures and to write them to files. These files can have any name you like, but they will all have the file extension .png. The file extension .png indicates that the file is in the Portable Networks Graphics format. This is a public domain standard for representing images.

multiple figures

pylab.figure(1) #create figure 1
pylab.plot([1,2,3,4], [1,2,3,4]) #draw on figure 1
pylab.figure(2) #create figure 2
pylab.plot([1,4,2,3], [5,6,7,8]) #draw on figure 2
pylab.savefig('Figure-Addie') #save figure 2
pylab.figure(1) #go back to working on figure 1
pylab.plot([5,6,10,3]) #draw again on figure 1
pylab.savefig('Figure-Jane') #save figure 1

produces and saves to files named Figure-Jane.png and Figure- Addie.png the two plots in Figure 11.2.

1. One argument

Observe that the last call to pylab.plot is passed only one argument. This argument supplies the y values. ==The corresponding x values default to the sequence yielded by range(len([5, 6, 10, 3])), which is why they range from 0 to 3 in this case.

2. “Current figure.”

PyLab has a notion of “current figure.” Executing pylab.figure(x) sets the current figure to the figure numbered x. Subsequently executed calls of plotting functions implicitly refer to that figure until another invocation of pylab.figure occurs. This explains why the figure written to the file Figure-Addie.png was the second figure created.

Figure 11.2 Contents of Figure-Jane.png (left) and Figure- Addie.png (right)

2. Compound interest

(1) Simple

values.append(principal) 这里看到List的神奇之处,如若手工操作,多么的繁琐。

******Let’s look at another example. The code:**

principal = 10000 #initial investment
interestRate = 0.05
years = 20
values = []
for i in range(years + 1):
    values.append(principal)
    principal += principal*interestRate
pylab.plot(values)
pylab.savefig('FigurePlottingCompoundGrowth')

produces the plot on the left in Figure 11.3.

(2) informative Titles

If we look at the code, we can deduce that this is a plot showing the growth of an initial investment of $10,000 at an annually compounded interest rate of 5%. However, this cannot be easily inferred by looking only at the plot itself. That’s a bad thing. All plots should have informative titles, and all axes should be labeled. If we add to the end of our code the lines

The xlabel,ylabel,title

pylab.title('5% Growth, Compounded Annually') 
pylab.xlabel('Years of Compounding') 
pylab.ylabel('Value of Principal ($)')

we get the plot on the right in Figure 11.3.

Figure 11.3

Figure 11.3
Figure 11.3

(3) Optional argument

For every plotted curve, there is an optional argument that is a format string indicating the color and line type of the plot. The letters and symbols of the format string are derived from those used in MATLAB, and are composed of a color indicator followed by an optional line-style indicator. The default format string is 'b-', which produces a solid blue line. To plot the the growth in principal with black circles, one would replace the call

pylab.plot(values) by pylab.plot(values, 'ko', )

which produces the plot in Figure 11.4. For a complete list of color and line-style indicators, see

Figure 11.4 Another plot of compound growth

Figure 11.5 Strange-looking plot
Figure 11.5 Strange-looking plot

(4) Change the type size and line width

It is also possible to change the type size and line width used in plots. This can be done using keyword arguments in individual calls to functions. E.g., the code:

principal = 10000 #initial 
investment interestRate = 0.05 
years = 20 
values = [] 
for i in range(years + 1): 
    values.append(principal) 
    principal += principal*interestRate 
pylab.plot(values, linewidth = 30) 
pylab.title('5% Growth, Compounded Annually', fontsize = 'xx-large') pylab.xlabel('Years of Compounding', fontsize = 'x- small') 
pylab.ylabel('Value of Principal ($)')

produces the intentionally bizarre-looking plot in Figure 11.5.

Figure 11.5 Strange-looking plot

Figure 11.5 Strange-looking plot
Figure 11.5 Strange-looking plot

3. Change the default values:“rc settings.”

It is also possible to change the default values, which are known as “rc settings.” (The name “rc” is derived from the .rc file extension used for runtime configuration files in Unix.) These values are stored in a dictionary-like variable that can be accessed via the name pylab.rcParams. So, for example, you can set the default line width to 6 points by executing the code

pylab.rcParams['lines.linewidth'] = 6.

There are an enormous number rcParams settings. A complete list can be found at

Customizing matplotlib — Matplotlib 2.0.0 documentation

If you don’t want to worry about customizing individual parameters,there are pre-defined style sheets. A description of these can be found at

Customizing plots with style sheets — Matplotlib 1.5.3 documentation

The values used in most of the remaining examples in this book were set with the code

#set line width
pylab.rcParams['lines.linewidth'] = 4
#set font size for titles
pylab.rcParams['axes.titlesize'] = 20
#set font size for labels on axes
pylab.rcParams['axes.labelsize'] = 20
#set size of numbers on x-axis
pylab.rcParams['xtick.labelsize'] = 16
#set size of numbers on y-axis
pylab.rcParams['ytick.labelsize'] = 16 
#set size of ticks on x-axis
pylab.rcParams['xtick.major.size'] = 7
#set size of ticks on y-axis
pylab.rcParams['ytick.major.size'] = 7
#set size of markers, e.g., circles representing points
pylab.rcParams['lines.markersize'] = 10
#set number of times marker is shown when displaying legend
pylab.rcParams['legend.numpoints'] = 1

If you are viewing plots on a color display, you will have little reason to customize these settings. We customized the settings we used so that it would be easier to read the plots when we shrank them and converted them to black and white.

11.2 Plotting Mortgages, an Extended Example

In Chapter 8, we worked our way through a hierarchy of mortgages as way of illustrating the use of subclassing. We concluded that chapter by observing that “our program should be producing plots designed to show how the mortgage behaves over time.” Figure 11.6 enhances class Mortgage by adding methods that make it convenient to produce such plots. (The function findPayment, which appears in Figure 8.9, is discussed in Section 8.4.)

Figure 11.6 Class Mortgage with plotting methods

print('class Mortgage')

class Mortgage(object):
    """Abstract class for building different kinds of mortgages"""

    def __init__(self, loan, annRate, months):
        """Create a new mortgage"""
        self.loan = loan
        self.rate = annRate/12.0
        self.months = months
        self.paid = [0.0]
        self.outstanding = [loan]
        self.payment = findPayment(loan, self.rate, months)
        self.legend = None #description of mortgage

    def makePayment(self):
        """Make a payment"""
        self.paid.append(self.payment)
        reduction = self.payment - self.outstanding[-1]*self.rate
        self.outstanding.append(self.outstanding[-1] - reduction)

    def getTotalPaid(self):
        """Return the total amount paid so far"""
        return sum(self.paid)

    def __str__(self):
        return self.legend

    def plotPayments(self, style):
        pylab.plot(self.paid[1:], style, label = self.legend)

    def plotBalance(self, style):
        pylab.plot(self.outstanding, style, label = self.legend)

    def plotTotPd(self, style):
        """Plot the cumulative total of the payments made"""
        totPd = [self.paid[0]]
        for i in range(1, len(self.paid)):
            totPd.append(totPd[-1] + self.paid[i])
        pylab.plot(totPd, style, label = self.legend)

    def plotNet(self, style):
        """Plot an approximation to the total cost of the mortgage over time by plotting the cash expended minus the equity acquired by paying off part of the loan"""
        totPd = [self.paid[0]]
        for i in range(1, len(self.paid)):
            totPd.append(totPd[-1] + self.paid[i])
            #Equity acquired through payments is amount of original loan
            #paid to date, which is amount of loan minus what is still outstanding
            equityAcquired = pylab.array([self.loan]*len(self.outstanding))
            equityAcquired = equityAcquired - pylab.array(self.outstanding)
            net = pylab.array(totPd) - equityAcquired
            pylab.plot(net, style, label = self.legend)

The nontrivial methods in class Mortgage are plotTotPd and plotNet. The method plotTotPd simply plots the cumulative total of the payments made. The method plotNet plots an approximation to the total cost of the mortgage over time by plotting the cash expended minus the equity acquired by paying off part of the loan.

The expression pylab.array(self.outstanding) in the function plotNet performs a type conversion. Thus far, we have been calling the plotting functions of PyLab with arguments of type list. Under the covers, PyLab has been converting these lists to a different type, array, which PyLab inherits from numpy. The invocation pylab.array makes this explicit. There are a number of convenient ways to manipulate arrays that are not readily available for lists. In particular, expressions can be formed using arrays and arithmetic operators. There are a number of ways to create arrays in PyLab, but the most common way is to first create a list, and then convert it. Consider the code:

a1 = pylab.array([1, 2, 4]) 
print('a1 =', a1) 
a2 = a1*2 
print('a2 =', a2) 
print('a1 + 3 =', a1 + 3) 
print('3 - a1 =', 3 - a1) 
print('a1 - a2 =', a1 - a2) 
print('a1*a2 =', a1*a2)

The expression a1*2 multiplies each element of a1 by the constant 2. The expression a1 + 3 adds the integer3 to each element of a1. The expression a1 - a2 subtracts each element of a2from the corresponding element of a1 (if the arrays had been of different length, an error would have occurred). The expression a1*a2 multiplies each element of a1 by the corresponding element of a2. When the above code is run it prints:

a1 = [1 2 4] 
a2 = [2 4 8] 
a1 + 3 = [4 5 7] 
3 - a1 = [ 2 1 -1] 
a1 - a2 = [-1 -2 -4] 
a1*a2 = [ 2 8 32]

Figure 11.7 repeats the three subclasses of Mortgage from Figure 8.10. Each has a distinct __init__method that overrides the__init__ method in Mortgage. The subclass TwoRate also overrides the makePayment method of Mortgage.

Figure 11.7 Subclasses of Mortgage

class Fixed(Mortgage):
    def __init__(self, loan, r, months):
        Mortgage.__init__(self, loan, r, months)
        self.legend = 'Fixed, ' + str(r*100) + '%'

class FixedWithPts(Mortgage):
    def __init__(self, loan, r, months, pts):
        Mortgage.__init__(self, loan, r, months)
        self.pts = pts
        self.paid = [loan*(pts/100.0)]
        self.legend = 'Fixed, ' + str(r*100) + '%, '+ str(pts) + ' points'

class TwoRate(Mortgage):
    def __init__(self, loan, r, months, teaserRate, teaserMonths):
         Mortgage.__init__(self, loan, teaserRate, months)
         self.teaserMonths = teaserMonths
         self.teaserRate = teaserRate
         self.nextRate = r/12.0
         self.legend = str(teaserRate*100)\
                        + '% for ' + str(self.teaserMonths)\
                        + ' months, then ' + str(r*100) + '%'

def makePayment(self):
    if len(self.paid) == self.teaserMonths + 1:
        self.rate = self.nextRate
        self.payment = findPayment(self.outstanding[-1], self.rate,
                                    self.months - self.teaserMonths)
    Mortgage.makePayment(self)

Figure 11.8 and Figure 11.9 contain functions that can be used to generate plots intended to provide insight about the different kinds of mortgages.

The function compareMortgages, Figure 11.8, creates a list of different kinds of mortgages, and simulates making a series of payments on each, as it did in Figure 8.11. It then calls plotMortgages, Figure 11.9, to produce the plots.

Figure 11.8 Compare mortgages

print('compareMortgages-Figure 11.8 Compare mortgages')
def compareMortgages(amt, years, fixedRate, pts, ptsRate,
                    varRate1, varRate2, varMonths):
    totMonths = years*12
    fixed1 = Fixed(amt, fixedRate, totMonths)
    fixed2 = FixedWithPts(amt, ptsRate, totMonths, pts)
    twoRate = TwoRate(amt, varRate2, totMonths, varRate1, varMonths)
    morts = [fixed1, fixed2, twoRate]
    for m in range(totMonths):
        for mort in morts:
            mort.makePayment()
    plotMortgages(morts, amt)

The function plotMortgages in Figure 11.9 uses the plotting methods in Mortgage to produce plots containing information about each of three kinds of mortgages. The loop in plotMortgages uses the index i to select elements from the lists morts and styles in a way that ensures that different kinds of mortgages are represented in a consistent way across figures. For example, since the third element in morts is a variable-rate mortgage and the third element in styles is 'k:', the variable-rate mortgage is always plotted using a black dotted line. The local function labelPlot is used to generate appropriate titles and axis labels for each plot. The calls of pylab.figure ensure that titles and labels are associated with the appropriate plot.

The call:

compareMortgages(amt=200000, years=30, 
                 fixedRate=0.07, pts = 3.25, 
                 ptsRate=0.05, varRate1=0.045, 
                 varRate2=0.095, varMonths=48)

produces plots (Figure 11.10 - Figure 11.12) that shed some light on the mortgages discussed in Section 8.4.

Figure 11.9 Generate mortgage plots

def plotMortgages(morts, amt):
    def labelPlot(figure, title, xLabel, yLabel):
        pylab.figure(figure)
        pylab.title(title)
        pylab.xlabel(xLabel)
        pylab.ylabel(yLabel)
        pylab.legend(loc = 'best')
    styles = ['k-','k-', 'k:']
    #Give names to figure numbers
    payments, cost, balance, netCost = 0, 1, 2, 3
    for i in range(len(morts)):
        pylab.figure(payments)
        morts[i].plotPayments(styles[i])
        pylab.figure(cost)
        morts[i].plotTotPd(styles[i])
        pylab.figure(balance)
        morts[i].plotBalance(styles[i])
        pylab.figure(netCost)
        morts[i].plotNet(styles[i])
        pylab.figure(payments)
    labelPlot(payments, 'Monthly Payments of $' + str(amt) +
                ' Mortgages', 'Months', 'Monthly Payments')
    labelPlot(cost, 'Cash Outlay of $' + str(amt) +
                ' Mortgages', 'Months','Total Payments')
    labelPlot(balance, 'Balance Remaining of $' + str(amt) +
                'Mortgages', 'Months', 'Remaining Loan Balance of $')
    labelPlot(netCost, 'Net Cost of $' + str(amt) + ' Mortgages',
              'Months', 'Payments - Equity $')

The plot in Figure 11.10, which was produced by invocations of plotPayments, simply plots each payment of each mortgage against time. The box containing the key appears where it does because of the value supplied to the keyword argument loc used in the call to pylab.legend. When loc is bound to 'best' the location is chosen automatically. This plot makes it clear how the monthly payments vary (or don’t) over time, but doesn’t shed much light on the relative costs of each kind of mortgage.

Figure 11.10 Monthly payments of different kinds of mortgages

运行报错:

Traceback (most recent call last):
  File "11.PlottingAndMoreAboutClasses.py", line 237, in <module>
    compareMortgages(amt=200000, years=30, fixedRate=0.07, pts=3.25, ptsRate=0.05, varRate1=0.045, varRate2=0.095, varMonths=48)
  File "11.PlottingAndMoreAboutClasses.py", line 199, in compareMortgages
    plotMortgages(morts, amt)
  File "11.PlottingAndMoreAboutClasses.py", line 220, in plotMortgages
    morts[i].plotNet(styles[i])
  File "11.PlottingAndMoreAboutClasses.py", line 145, in plotNet
    net = pylab.array(totPd) - equityAcquired
ValueError: operands could not be broadcast together with shapes (2,) (361,) 
Gaowei:F3.MIT_Python Gaowei$ 

The plots in Figure 11.11 were produced by invocations of plotTotPd. They shed some light on the cost of each kind of mortgage by plotting the cumulative costs that have been incurred at the start of each month. The entire plot is on the left, and an enlargement of the left part of the plot is on the right.

Figure 11.11 Cost over time of different kinds of mortgages

没有运行

The plots in Figure 11.12 show the remaining debt (on the left) and the total net cost of having the mortgage (on the right).

Figure 11.12 Balance remaining and net cost for different kinds of mortgages

没有运行

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • 偶尔见一顶羽毛 轻轻落下 划过天空 那身影 轻盈迷人 长吹一口气 能打几个滚 顺着风儿 漫步而去 竟忘了从哪儿来
    青云无言阅读 184评论 2 1
  • 淡泊之守,须从秾艳场中试来;镇定之操,还向纷纭境上勘过。 ——《小窗幽记•醒》 淡泊清...
    朵娘说阅读 211评论 0 1
  • 光谷蓝灯分社第一期沙盘互助团体从10月18日至今,已经成功走过两次初体验的遴选,并深度走过五轮,下周最后一轮总结后...
    子霞_e0db阅读 764评论 0 0
  • 很早之前注册了简书,由于工作原因,一直没有写过文章,现在项目提测,每天等测试提提bug,分配分配任务。时间稍微充裕...
    FortunateStone阅读 209评论 2 3
  • 早上的阳光伴着些许的清凉,特别勾人忆往。清风扑来,脑海里都是小时候奶奶带着我去买凉鞋的情景。 我记事很晚,现在的好...
    一只清新脱俗的蚊子阅读 216评论 0 1