forked from swcarpentry/matlab-novice-inflammation
-
Notifications
You must be signed in to change notification settings - Fork 0
/
05-cond.html
315 lines (295 loc) · 15.1 KB
/
05-cond.html
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="generator" content="pandoc">
<title>Software Carpentry: Programming with MATLAB</title>
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="css/bootstrap/bootstrap.css" />
<link rel="stylesheet" type="text/css" href="css/bootstrap/bootstrap-theme.css" />
<link rel="stylesheet" type="text/css" href="css/swc.css" />
<link rel="alternate" type="application/rss+xml" title="Software Carpentry Blog" href="http://software-carpentry.org/feed.xml"/>
<meta charset="UTF-8" />
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body class="lesson">
<div class="container card">
<div class="banner">
<a href="http://software-carpentry.org" title="Software Carpentry">
<img alt="Software Carpentry banner" src="img/software-carpentry-banner.png" />
</a>
</div>
<article>
<div class="row">
<div class="col-md-10 col-md-offset-1">
<h1 class="title">Programming with MATLAB</h1>
<h2 class="subtitle">Making Choices</h2>
<div id="learning-objectives" class="objectives panel panel-warning">
<div class="panel-heading">
<h2><span class="glyphicon glyphicon-certificate"></span>Learning Objectives</h2>
</div>
<div class="panel-body">
<ul>
<li>Learn about if, elseif, and else</li>
<li>Learn to test equality, AND, and OR conditions</li>
<li>Learn to nest loops</li>
</ul>
</div>
</div>
<p>Our previous lessons have shown us how to manipulate data, define our own functions, and repeat things. However, the programs we have written so far always do the same things, regardless of what data they’re given. We want programs to make choices based on the values they are manipulating.</p>
<p>The tool that MATLAB gives us for doing this is called a <a href="reference.html#conditional-statement">conditional statement</a>, and it looks like this:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">num = <span class="fl">37</span>;
if num > <span class="fl">100</span>
disp(<span class="st">'greater'</span>);
else
disp(<span class="st">'not greater'</span>);
end
disp(<span class="st">'done'</span>);</code></pre>
<pre class="output"><code>not greater
done</code></pre>
<p>The second line of this code uses the keyword <code>if</code> to tell MATLAB that we want to make a choice. If the test that follows is true, the body of the <code>if</code> (i.e., the lines between <code>if</code> and <code>else</code>) are executed. If the test is false, the body of the <code>else</code> (i.e., the lines between <code>else</code> and <code>end</code>) are executed instead. Only one or the other is ever executed.</p>
<p>Conditional statements don’t have to have an <code>else</code> block. If there isn’t one, MATLAB simply doesn’t do anything if the test is false:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">num = <span class="fl">53</span>
disp(<span class="st">'before conditional...'</span>)
if num > <span class="fl">100</span>:
print <span class="st">'53 is greater than 100'</span>
end
disp(<span class="st">'...after conditional'</span>)</code></pre>
<pre class="output"><code>before conditional...
...after conditional</code></pre>
<p>We can also chain several tests together using <code>elseif</code>. This makes it simple to write a function that returns the sign of a number:</p>
<pre><code>% function sign_of.m
function out = sign_of(num)
if num > 0
out = 1;
elseif num == 0
out = 0;
else
out = -1;
end</code></pre>
<p>One important thing to notice in the code above is that we use a double equals sign <code>==</code> to test for equality rather than a single equals sign. This is because the latter is used to mean assignment. In our test, we want to check for the equality of <code>num</code> and <code>0</code>, not <em>assign</em> 0 to <code>num</code>. This convention was inherited from C, and it does take a bit of getting used to…</p>
<p>We can also combine tests, using <code>&&</code> (and) and <code>||</code> (or). <code>&&</code> is true if both tests are true:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">if ((<span class="fl">1</span> > <span class="fl">0</span>) && (-<span class="fl">1</span> > <span class="fl">0</span>))
disp(<span class="st">'both parts are true'</span>);
else
disp(<span class="st">'one part is not true'</span>);
end</code></pre>
<pre class="output"><code>one part is not true</code></pre>
<p><code>||</code> is true if either test is true:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">if (<span class="fl">1</span> < <span class="fl">0</span>) || (<span class="fl">3</span> < <span class="fl">4</span>)
disp(<span class="st">'at least one part is true'</span>);
end</code></pre>
<pre class="output"><code>at least one part is true</code></pre>
<p>In this case, “either” means “either or both”, not “either one or the other but not both”.</p>
<div id="true-and-false-statements" class="challenge panel panel-success">
<div class="panel-heading">
<h2><span class="glyphicon glyphicon-pencil"></span>True and false statements</h2>
</div>
<div class="panel-body">
<ol style="list-style-type: decimal">
<li><p><code>1</code> and <code>0</code> aren’t the only values in MATLAB that are true or false. In fact, <em>any</em> value can be used in an <code>if</code> or <code>elseif</code>. After reading and running the code below, explain what the rule is for which values that are considered true and which are considered false.</p></li>
<li><p><sub>~</sub> if disp(‘empty string is true’) end <sub>~</sub></p></li>
<li></li>
</ol>
<p><sub>~</sub> if ‘foo’ disp(‘non empty string is true’) end <sub>~</sub></p>
<p>3.</p>
<p><sub>~</sub> if [] disp (‘empty array is true’) end <sub>~</sub></p>
<p>4.</p>
<p><sub>~</sub> if [22.5, 1.0] disp (‘non empty array is true’) end <sub>~</sub></p>
<p>5.</p>
<p><sub>~</sub> if [0, 0] disp (‘array of zeros is true’) end <sub>~</sub></p>
<p>6.</p>
<p><sub>~</sub> if true disp(‘true is true’) end <sub>~</sub></p>
<ol start="2" style="list-style-type: decimal">
<li>Write a function called <code>near</code> that returns <code>1</code> when its first parameter is within 10% of its second and <code>0</code> otherwise. Compare your implementation with your partner’s: do you return the same answer for all possible pairs of numbers?</li>
</ol>
</div>
</div>
<p>Another thing to realize is that <code>if</code> statements can be combined with loops just as easily as they can be combined with functions. For example, if we want to sum the positive numbers in a list, we can write this:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">numbers = [-<span class="fl">5</span>, <span class="fl">3</span>, <span class="fl">2</span>, -<span class="fl">1</span>, <span class="fl">9</span>, <span class="fl">6</span>];
total = <span class="fl">0</span>;
for n = numbers
if n >= <span class="fl">0</span>
total = total + n;
end
end
disp([<span class="st">'sum of positive values: '</span>, num2str(total)])</code></pre>
<pre class="output"><code>sum of positive values: 20</code></pre>
<p>With a little extra effort, we can calculate the positive and negative sums in a loop:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">pos_total = <span class="fl">0</span>;
neg_total = <span class="fl">0</span>;
for n = numbers
if n >= <span class="fl">0</span>
pos_total = total + n;
else
neg_total = neg_total + n;
end
end
disp([<span class="st">'sum of positive values: '</span>, num2str(pos_total)]);
disp([<span class="st">'sum of negative values: '</span>, num2str(neg_total)]);</code></pre>
<pre class="output"><code>sum of positive values: 26
sum of negative values: -6</code></pre>
<p>We can even put one loop inside another:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">for consonant = <span class="st">'bcd'</span>
for vowel = <span class="st">'ae'</span>
disp (strcat(consonant, vowel));
end
end</code></pre>
<pre class="output"><code>ba
be
ca
ce
da
de</code></pre>
<div id="nesting" class="challenge panel panel-success">
<div class="panel-heading">
<h2><span class="glyphicon glyphicon-pencil"></span>Nesting</h2>
</div>
<div class="panel-body">
<ol style="list-style-type: decimal">
<li><p>Will changing the order of nesting in the above loop change the output? Why? Write down the output you might expect from changing the order of the loops, then rewrite the code to test your hypothesis.</p></li>
<li><p>MATLAB (and most other languges in the C family) provides <a href="reference.html#in-place-operator">in-place operators</a> that work like this:</p></li>
</ol>
<pre><code>x = 1;
x += 1;
x *= 3;</code></pre>
<p>Rewrite the code that sums the positive and negative values in an array using these in-place operators. Do you think that the result is more or less readable than the original?</p>
</div>
</div>
<p>The last step is to turn our data into something we can see and make sense of. As in previous lessons, we need to first get the data in memory:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">patient_data = csvread(<span class="st">'inflammation-01.csv'</span>);</code></pre>
<p>The heatmap from lesson 1 is useful, but fairly hard to read:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">imagesc(patient_data);
colorbar();</code></pre>
<div>
<img src="img/01-intro_1.png" style="height:350px">
</div>
<p>Let’s write some code to give us a plot that’s a little more useful:</p>
<pre><code>[height, width] = size(patient_data);
heatmap = zeros(height, width);
for y = 1:height
for x = 1:width
if patient_data(y, x) > mean(patient_data(:))
heatmap(y, x) = 1;
elseif patient_data(y, x) < mean(patient_data(:))
heatmap(y, x) = -1;
else
heatmap(y, x) = 0;
end
end
end
imagesc(heatmap);
colorbar();</code></pre>
<div>
<img src="img/04-cond_1.png" style="height:350px">
</div>
<p>This is slightly better, but there are still a few things wrong with it:</p>
<ol style="list-style-type: decimal">
<li><p>Red against blue is pretty hard on the eyes.</p></li>
<li><p>The heatmap shows only two colors because none of the (integer) measurements has the exact value as the mean.</p></li>
<li><p>We are calculating the mean of the data each time we go through the loop. On our 40 × 60 dataset, this means we are performing the same calculation 2400 times.</p></li>
</ol>
<p>Here’s how we can improve it:</p>
<ol style="list-style-type: decimal">
<li><p>We can pick better colors, and use a <a href="reference.html#colormap">colormap</a></p></li>
<li><p>Instead of checking if values are exactly equal to the mean, we can check if they are close to it.</p></li>
<li><p>We can calculate the mean once, before the loop, and use that value over and over. The cost of computing the mean is much more than retrieving its value from memory.</p></li>
</ol>
<p>Our modified code looks like this:</p>
<pre><code>[height, width] = size(patient_data);
heatmap = zeros(height, width);
center = mean(patient_data(:));
for y = 1:height
for x = 1:width
if patient_data(y, x) > 1.9*center
heatmap(y, x) = 1;
elseif patient_data(y, x) < 0.9*center
heatmap(y, x) = -1;
else
heatmap(y, x) = 0;
end
end
end
imagesc(heatmap);
colorbar();
colormap winter
axis equal</code></pre>
<div>
<img src="img/04-cond_2.png" style="height:350px">
</div>
<p>This is better, but we might want to widen the band around the mean that gets that color.</p>
<p>But to do that, we’ll have to go back to our code and change some numerical values (<code>1.9</code> and <code>0.9</code> in the code above). This is almost certainly a bad idea. Let’s write a function to make things easier:</p>
<pre><code>% make_heatmap.m
function heatmap = make_heatmap(data, low_band, high_band)
% Make a 3-colored heatmap from
% a 2D array of data.
heatmap = zeros(height, width);
center = mean(patient_data(:));
for y = 1:height
for x = 1:width
if patient_data(y, x) > high_band*center
heatmap(y, x) = 1;
elseif patient_data(y, x) < low_band*center
heatmap(y, x) = -1;
else
heatmap(y, x) = 0;
end
end
end
end</code></pre>
<p>To test this function, we can run it with the settings we just used:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">heatmap = make_heatmap(patient_data, <span class="fl">0.9</span>, <span class="fl">1.1</span>);
imagesc(heatmap);
colorbar();
colormap winter
axis equal</code></pre>
<div>
<img src="img/04-cond_2.png" style="height:350px">
</div>
<p>That seems right, so let’s try wider bands:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">heatmap = make_heatmap(patient_data, <span class="fl">0.8</span>, <span class="fl">1.2</span>);
imagesc(heatmap);
colorbar();
colormap winter
axis equal</code></pre>
<div>
<img src="img/04-cond_3.png" style="height:350px">
</div>
<pre class="sourceCode matlab"><code class="sourceCode matlab">heatmap = make_heatmap(patient_data, <span class="fl">0.6</span>, <span class="fl">1.4</span>);
imagesc(heatmap);
colorbar();
colormap winter
axis equal</code></pre>
<div>
<img src="img/04-cond_4.png" style="height:350px">
</div>
<div id="design-choice" class="challenge panel panel-success">
<div class="panel-heading">
<h2><span class="glyphicon glyphicon-pencil"></span>Design choice</h2>
</div>
<div class="panel-body">
<ol style="list-style-type: decimal">
<li>Why does the <code>make_heatmap</code> function return an array instead of displaying it immediately? Do you think this is a good design choice?</li>
</ol>
</div>
</div>
<p>Before we are ready to publish our <code>make_heatmap</code> function, we need to be sure of its correctness. To do that, we need to learn how to <em>test</em> our code, and that will be the subject of our next lesson.</p>
</div>
</div>
</article>
<div class="footer">
<a class="label swc-blue-bg" href="http://software-carpentry.org">Software Carpentry</a>
<a class="label swc-blue-bg" href="https://github.com/swcarpentry/matlab-novice-inflammation">Source</a>
<a class="label swc-blue-bg" href="mailto:[email protected]">Contact</a>
<a class="label swc-blue-bg" href="LICENSE.html">License</a>
</div>
</div>
<!-- Javascript placed at the end of the document so the pages load faster -->
<script src="http://software-carpentry.org/v5/js/jquery-1.9.1.min.js"></script>
<script src="css/bootstrap/bootstrap-js/bootstrap.js"></script>
</body>
</html>