03-matplotlib.md 7.86 KB
Newer Older
1
---
2
title: Analyzing Patient Data
3 4
teaching: 30
exercises: 20
5
questions:
6 7
- "How can I visualize tabular data files in Python?"
- "How can I group several plots together?"
8
objectives:
9
- "Plot simple graphs from data."
10
- "Group several graphs in a single figure."
11
keypoints:
Greg Wilson's avatar
Greg Wilson committed
12
- "Use the `pyplot` library from `matplotlib` for creating simple visualizations."
13 14
---

15 16 17 18
## Visualizing data
The mathematician Richard Hamming once said, "The purpose of computing is insight, not numbers," and
the best way to develop insight is often to visualize data.  Visualization deserves an entire
lecture of its own, but we can explore a few features of Python's `matplotlib` library here.  While
Tyler Reddy's avatar
Tyler Reddy committed
19
there is no official plotting library, `matplotlib` is the _de facto_ standard.  First, we will
20 21
import the `pyplot` module from `matplotlib` and use two of its functions to create and display a
heat map of our data:
22

23
~~~
24
import matplotlib.pyplot
25
image = matplotlib.pyplot.imshow(data)
26
matplotlib.pyplot.show()
Greg Wilson's avatar
Greg Wilson committed
27
~~~
28
{: .language-python}
29

30
![Heatmap of the Data](../fig/inflammation-01-imshow.svg)
31

32
Blue pixels in this heat map represent low values, while yellow pixels represent high values.  As we
33
can see, inflammation rises and falls over a 40-day period.  Let's take a look at the average inflammation over time:
34

35
~~~
36
ave_inflammation = numpy.mean(data, axis=0)
37
ave_plot = matplotlib.pyplot.plot(ave_inflammation)
38
matplotlib.pyplot.show()
Greg Wilson's avatar
Greg Wilson committed
39
~~~
40
{: .language-python}
41

42
![Average Inflammation Over Time](../fig/inflammation-01-average.svg)
43

44 45 46 47
Here, we have put the average per day across all patients in the variable `ave_inflammation`, then
asked `matplotlib.pyplot` to create and display a line graph of those values.  The result is a
roughly linear rise and fall, which is suspicious: we might instead expect a sharper rise and slower
fall.  Let's have a look at two other statistics:
48

49
~~~
50
max_plot = matplotlib.pyplot.plot(numpy.max(data, axis=0))
51
matplotlib.pyplot.show()
Greg Wilson's avatar
Greg Wilson committed
52
~~~
53
{: .language-python}
54

55
![Maximum Value Along The First Axis](../fig/inflammation-01-maximum.svg)
Greg Wilson's avatar
Greg Wilson committed
56

57
~~~
58
min_plot = matplotlib.pyplot.plot(numpy.min(data, axis=0))
59
matplotlib.pyplot.show()
Greg Wilson's avatar
Greg Wilson committed
60
~~~
61
{: .language-python}
62

63
![Minimum Value Along The First Axis](../fig/inflammation-01-minimum.svg)
64

65 66 67 68
The maximum value rises and falls smoothly, while the minimum seems to be a step function.  Neither
trend seems particularly likely, so either there's a mistake in our calculations or something is
wrong with our data.  This insight would have been difficult to reach by examining the numbers
themselves without visualization tools.
69

70
### Grouping plots
71
You can group similar plots in a single figure using subplots.
72
This script below uses a number of new commands. The function `matplotlib.pyplot.figure()`
73
creates a space into which we will place all of our plots. The parameter `figsize`
74
tells Python how big to make this space. Each subplot is placed into the figure using
75 76 77
its `add_subplot` [method]({{ page.root }}/reference/#method). The `add_subplot` method takes 3
parameters. The first denotes how many total rows of subplots there are, the second parameter
refers to the total number of subplot columns, and the final parameter denotes which subplot
78 79 80
your variable is referencing (left-to-right, top-to-bottom). Each subplot is stored in a
different variable (`axes1`, `axes2`, `axes3`). Once a subplot is created, the axes can
be titled using the `set_xlabel()` command (or `set_ylabel()`).
81
Here are our three plots side by side:
82

83
~~~
84 85
import numpy
import matplotlib.pyplot
86

87
data = numpy.loadtxt(fname='inflammation-01.csv', delimiter=',')
88

89
fig = matplotlib.pyplot.figure(figsize=(10.0, 3.0))
90

91 92 93
axes1 = fig.add_subplot(1, 3, 1)
axes2 = fig.add_subplot(1, 3, 2)
axes3 = fig.add_subplot(1, 3, 3)
94

95
axes1.set_ylabel('average')
96
axes1.plot(numpy.mean(data, axis=0))
97

98
axes2.set_ylabel('max')
99
axes2.plot(numpy.max(data, axis=0))
100

101
axes3.set_ylabel('min')
102
axes3.plot(numpy.min(data, axis=0))
103 104

fig.tight_layout()
105

106
matplotlib.pyplot.show()
Greg Wilson's avatar
Greg Wilson committed
107
~~~
108
{: .language-python}
109

110
![The Previous Plots as Subplots](../fig/inflammation-01-group-plot.svg)
111

112
The [call]({{ page.root }}/reference/#function-call) to `loadtxt` reads our data,
113 114
and the rest of the program tells the plotting library
how large we want the figure to be,
115
that we're creating three subplots,
116 117
what to draw for each one,
and that we want a tight layout.
Brian Jackson's avatar
Brian Jackson committed
118
(If we leave out that call to `fig.tight_layout()`,
119
the graphs will actually be squeezed together more closely.)
120

mboisson's avatar
mboisson committed
121

122
> ## Plot Scaling
Greg Wilson's avatar
Greg Wilson committed
123
>
124
> Why do all of our plots stop just short of the upper end of our graph?
125 126 127 128 129 130 131 132
>
> > ## Solution
> > Because matplotlib normally sets x and y axes limits to the min and max of our data
> > (depending on data range)
> {: .solution}
>
> If we want to change this, we can use the `set_ylim(min, max)` method of each 'axes',
> for example:
133
>
134
> ~~~
135 136
> axes3.set_ylim(0,6)
> ~~~
137
> {: .language-python}
138
>
139 140 141 142 143 144 145 146 147 148
> Update your plotting code to automatically set a more appropriate scale.
> (Hint: you can make use of the `max` and `min` methods to help.)
>
> > ## Solution
> > ~~~
> > # One method
> > axes3.set_ylabel('min')
> > axes3.plot(numpy.min(data, axis=0))
> > axes3.set_ylim(0,6)
> > ~~~
149
> > {: .language-python}
150 151 152 153 154 155 156 157 158 159
> {: .solution}
>
> > ## Solution
> > ~~~
> > # A more automated approach
> > min_data = numpy.min(data, axis=0)
> > axes3.set_ylabel('min')
> > axes3.plot(min_data)
> > axes3.set_ylim(numpy.min(min_data), numpy.max(min_data) * 1.1)
> > ~~~
160
> > {: .language-python}
161
> {: .solution}
162
{: .challenge}
Greg Wilson's avatar
Greg Wilson committed
163

164
> ## Drawing Straight Lines
Greg Wilson's avatar
Greg Wilson committed
165
>
Brian Jackson's avatar
Brian Jackson committed
166
> In the center and right subplots above, we expect all lines to look like step functions because
167 168 169
> non-integer value are not realistic for the minimum and maximum values. However, you can see
> that the lines are not always vertical or horizontal, and in particular the step function
> in the subplot on the right looks slanted. Why is this?
170 171
>
> > ## Solution
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
> > Because matplotlib interpolates (draws a straight line) between the points.
> > One way to do avoid this is to use the Matplotlib `drawstyle` option:
> >
> > ~~~
> > import numpy
> > import matplotlib.pyplot
> >
> > data = numpy.loadtxt(fname='inflammation-01.csv', delimiter=',')
> >
> > fig = matplotlib.pyplot.figure(figsize=(10.0, 3.0))
> >
> > axes1 = fig.add_subplot(1, 3, 1)
> > axes2 = fig.add_subplot(1, 3, 2)
> > axes3 = fig.add_subplot(1, 3, 3)
> >
> > axes1.set_ylabel('average')
> > axes1.plot(numpy.mean(data, axis=0), drawstyle='steps-mid')
> >
> > axes2.set_ylabel('max')
> > axes2.plot(numpy.max(data, axis=0), drawstyle='steps-mid')
> >
> > axes3.set_ylabel('min')
> > axes3.plot(numpy.min(data, axis=0), drawstyle='steps-mid')
> >
> > fig.tight_layout()
> >
> > matplotlib.pyplot.show()
> > ~~~
200
> > {: .language-python}
201
> ![Plot with step lines](../fig/inflammation-01-line-styles.svg)
202
> {: .solution}
203
{: .challenge}
Greg Wilson's avatar
Greg Wilson committed
204

205
> ## Make Your Own Plot
Greg Wilson's avatar
Greg Wilson committed
206
>
207 208 209 210 211
> Create a plot showing the standard deviation (`numpy.std`)
> of the inflammation data for each day across all patients.
>
> > ## Solution
> > ~~~
212
> > std_plot = matplotlib.pyplot.plot(numpy.std(data, axis=0))
213 214
> > matplotlib.pyplot.show()
> > ~~~
215
> > {: .language-python}
216
> {: .solution}
217
{: .challenge}
218

219
> ## Moving Plots Around
220
>
221 222 223 224 225 226 227 228
> Modify the program to display the three plots on top of one another
> instead of side by side.
>
> > ## Solution
> > ~~~
> > import numpy
> > import matplotlib.pyplot
> >
229
> > data = numpy.loadtxt(fname='inflammation-01.csv', delimiter=',')
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
> >
> > # change figsize (swap width and height)
> > fig = matplotlib.pyplot.figure(figsize=(3.0, 10.0))
> >
> > # change add_subplot (swap first two parameters)
> > axes1 = fig.add_subplot(3, 1, 1)
> > axes2 = fig.add_subplot(3, 1, 2)
> > axes3 = fig.add_subplot(3, 1, 3)
> >
> > axes1.set_ylabel('average')
> > axes1.plot(numpy.mean(data, axis=0))
> >
> > axes2.set_ylabel('max')
> > axes2.plot(numpy.max(data, axis=0))
> >
> > axes3.set_ylabel('min')
> > axes3.plot(numpy.min(data, axis=0))
> >
> > fig.tight_layout()
> >
> > matplotlib.pyplot.show()
> > ~~~
252
> > {: .language-python}
253
> {: .solution}
254
{: .challenge}
255

Maxim Belkin's avatar
Maxim Belkin committed
256
{% include links.md %}