forked from swcarpentry/matlab-novice-inflammation
-
Notifications
You must be signed in to change notification settings - Fork 0
/
03-loops.html
297 lines (274 loc) · 17.3 KB
/
03-loops.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
<!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">Repeating With Loops</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>Explain what a for loop does.</li>
<li>Correctly write for loops that repeat simple commands.</li>
<li>Trace changes to a loop variable as the loops runs.</li>
<li>Use a for loop to process multiple files</li>
</ul>
</div>
</div>
<p>Recall that we have to do this analysis for every one of our dozen datasets. And we need a better way than typing out commands for each one, because we’ll find ourselves writing a lot of duplicate code. Remember, code that is repeated in two or more places will eventually be wrong in at least one. Also, if we make changes in the way we analyze our datasets, we have to introduce that change in every copy of our code. To avoid all of this repetition, we have to teach MATLAB to repeat our commands, and to do <em>that</em>, we have to learn how to write <em>loops</em>.</p>
<p>Suppose we want to print each character in the word “lead” on a line of its own. One way is to use four <code>disp</code> statements:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">word = <span class="st">'lead'</span>;
disp(word(<span class="fl">1</span>));
disp(word(<span class="fl">2</span>));
disp(word(<span class="fl">3</span>));
disp(word(<span class="fl">4</span>));</code></pre>
<pre class="output"><code>l
e
a
d</code></pre>
<p>But this is a bad approach for two reasons:</p>
<ol style="list-style-type: decimal">
<li><p>It doesn’t scale: if we want to print the characters in a string that’s hundreds of letters long, we’d be better off typing them in.</p></li>
<li><p>It’s fragile: if we change <code>word</code> to a longer string, it only prints part of the data, and if we change it to a shorter one, it produces an error, because we’re asking for characters that don’t exist.</p></li>
</ol>
<pre class="sourceCode matlab"><code class="sourceCode matlab">word = <span class="st">'tin'</span>;
disp(word(<span class="fl">1</span>));
disp(word(<span class="fl">2</span>));
disp(word(<span class="fl">3</span>));
disp(word(<span class="fl">4</span>));</code></pre>
<pre class="error"><code>error: A(I): index out of bounds; value 4 out of bound 3</code></pre>
<p>There’s a better approach:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">word = <span class="st">'lead'</span>;
for letter = <span class="fl">1</span>:<span class="fl">4</span>
disp(word(letter))
end</code></pre>
<pre class="output"><code>l
e
a
d</code></pre>
<p>This improved version uses a <a href="reference.html#for-loop">for loop</a> to repeat an operation—in this case, printing to the screen—once for each element in an array.</p>
<p>The general form of a for loop is:</p>
<pre><code>for variable = collection
do things with variable
end</code></pre>
<p>The for loop executes the commands in the <a href="reference.html#loop-body">loop body</a> for every value in the array <code>collection</code>. This value is called the <a href="reference.html#loop-variable">loop variable</a>, and we can call it whatever we like. In our example, we gave it the name <code>letter</code>.</p>
<p>We have to terminate the loop body with the <code>end</code> keyword, and we can have as many commands as we like in the loop body. But, we have to remember that they will all be repeated as many times as there are values in <code>collection</code>.</p>
<p>Our for loop has made our code more scalable, and less fragile. There’s still one little thing about it that should bother us. For our loop to deal appropriately with shorter or longer words, we have to change the first line of our loop by hand:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">word = <span class="st">'tin'</span>;
for letter = <span class="fl">1</span>:<span class="fl">3</span>
disp(word(letter));
end</code></pre>
<pre class="output"><code>t
i
n</code></pre>
<p>Although this works, it’s not the best way to write our loop:</p>
<ul>
<li><p>We might update <code>word</code> and forget to modify the loop to reflect that change.</p></li>
<li><p>We might make a mistake while counting the number of letters in <code>word</code>.</p></li>
</ul>
<p>Fortunately, MATLAB provides us with a convenient function to write a better loop:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">word = <span class="st">'aluminium'</span>;
for letter = <span class="fl">1</span>:length(word)
disp(word(letter));
end</code></pre>
<pre class="output"><code>a
l
u
m
i
n
i
u
m</code></pre>
<p>This is much more robust code, as it can deal identically with words of arbitrary length. Here’s another loop that repeatedly updates the variable <code>len</code>:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">len = <span class="fl">0</span>
for vowel = <span class="st">'aeiou'</span>
len = len + <span class="fl">1</span>;
end
disp([<span class="st">'Number of vowels: '</span>, num2str(len)])</code></pre>
<p>It’s worth tracing the execution of this little program step by step. Since there are five characters in “aeiou”, the loop body will be executed five times. When we enter the loop, <code>len</code> is zero - the value assigned to it beforehand. The first time through, the loop body adds 1 to the old value of <code>len</code>, producing 1, and updates <code>len</code> to refer to that new value. The next time around, <code>vowel</code> is <code>e</code>, and <code>len</code> is 1, so <code>len</code> is updated to 2. After three more updates, <code>len</code> is 5; since there’s nothing left in <code>aeiou</code> for MATLAB to process, the loop finishes and the <code>disp</code> statement tells us our final answer.</p>
<p>Note that a loop variable is just a variable that’s being used to record progress in a loop. It still exists after the loop is over, and we can re-use variables previously defined as loop variables as well:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">disp(vowel)</code></pre>
<pre class="output"><code>u</code></pre>
<div id="performing-exponentiation" class="challenge panel panel-success">
<div class="panel-heading">
<h2><span class="glyphicon glyphicon-pencil"></span>Performing Exponentiation</h2>
</div>
<div class="panel-body">
<p>MATLAB uses the caret (<code>^</code>) to perform exponentiation:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">disp(<span class="fl">5</span>^<span class="fl">3</span>)</code></pre>
<pre class="output"><code>125</code></pre>
<p>You can also use a loop to perform exponentiation. Remember that <code>b^x</code> is just <code>b*b*b*</code>… <code>x</code> times.</p>
<p>Let a variable <code>b</code> be the base of the number and <code>x</code> the exponent. Write a loop to compute <code>b^x</code>. Check your result for <code>b = 4</code> and <code>x = 5</code>.</p>
</div>
</div>
<div id="incrementing-with-loops" class="challenge panel panel-success">
<div class="panel-heading">
<h2><span class="glyphicon glyphicon-pencil"></span>Incrementing with Loops</h2>
</div>
<div class="panel-body">
<p>Write a loop that spells the word “aluminum,” adding one letter at a time:</p>
<pre class="output"><code>a
al
alu
alum
alumi
alumin
aluminu
aluminum</code></pre>
</div>
</div>
<div id="looping-in-reverse" class="challenge panel panel-success">
<div class="panel-heading">
<h2><span class="glyphicon glyphicon-pencil"></span>Looping in Reverse</h2>
</div>
<div class="panel-body">
<p>In Matlab, the colon operator (<code>:</code>) accepts a <a href="reference.>%20html#stride">stride</a> or skip argument between the start and stop:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">disp(<span class="fl">1</span>:<span class="fl">3</span>:<span class="fl">11</span>)</code></pre>
<pre class="output"><code>1 4 7 10</code></pre>
<pre class="sourceCode matlab"><code class="sourceCode matlab">disp(<span class="fl">11</span>:-<span class="fl">3</span>:<span class="fl">1</span>)</code></pre>
<pre class="output"><code>11 8 5 2</code></pre>
<p>Using this, write a loop to print the letters of “aluminum” in reverse order, one letter per line.</p>
<pre class="output"><code>m
u
n
i
m
u
l
a</code></pre>
</div>
</div>
<p>We now have almost everything we need to process multiple data files with our <code>analyze</code> script. You’ll notice that our data files are named <code>inflammation-01.csv</code>, <code>inflammation-02.csv</code>, etc. Let’s write a loop that tries to print the names of each one of our files:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">for idx = <span class="fl">1</span>:<span class="fl">12</span>
file_name = sprintf(<span class="st">'inflammation-%d.csv'</span>, idx);
disp(file_name);
end</code></pre>
<pre class="output"><code>inflammation-1.csv
inflammation-2.csv
inflammation-3.csv
inflammation-4.csv
inflammation-5.csv
inflammation-6.csv
inflammation-7.csv
inflammation-8.csv
inflammation-9.csv
inflammation-10.csv
inflammation-11.csv
inflammation-12.csv</code></pre>
<p>This is close, but not quite right. The <code>sprintf</code> function is useful when we want to generate MATLAB strings based on a <em>template</em>. In our case, that template is the string <code>inflammation-%d.csv</code>. <code>sprintf</code> generates a new string from our template by replacing the <code>%d</code> with the data referred to by our second argument, <code>i</code>.</p>
<p>Again, let’s trace the execution of our loop: in the beginning of our loop, <code>i</code> starts by referring to the value 1. So, when MATLAB executes the command</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">file_name = sprintf(<span class="st">'inflammation-%d.csv'</span>, idx);</code></pre>
<p>it substitutes the <code>%d</code> in the template <code>inflammation-%d.csv</code>, with the value of <code>i</code>, i.e., 1. The resulting string is <code>inflammation-1.csv</code>, which is assigned to the variable <code>file_name</code>. The <code>disp</code> command prints that string to screen. The second time around, <code>sprintf</code> generates the string <code>inflammation-2.csv</code>, which is assigned to the variable <code>file_name</code>, and printed to screen. And so on, till <code>i</code> finally refers to the value 12.</p>
<p>Notice that there’s a mistake. Our files are actually named <code>inflammation-01.csv</code>, <code>inflammation-02.csv</code>, etc. To get it right, we have to modify our template:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">for idx = <span class="fl">1</span>:<span class="fl">12</span>
file_name = sprintf(<span class="st">'inflammation-%02d.csv'</span>, idx);
disp(file_name);
end</code></pre>
<pre class="output"><code>inflammation-01.csv
inflammation-02.csv
inflammation-03.csv
inflammation-04.csv
inflammation-05.csv
inflammation-06.csv
inflammation-07.csv
inflammation-08.csv
inflammation-09.csv
inflammation-10.csv
inflammation-11.csv
inflammation-12.csv</code></pre>
<p>We’ve replaced <code>%d</code> in our earlier template with <code>%02d</code>. With this, we’re specifying that we want our data to be displayed with a minimum width of 2 characters, and that we want to <em>pad</em> with 0 for data that isn’t at least 2 digits long.</p>
<p>We’re now ready to modify <code>analyze.m</code> to process multiple data files:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab"><span class="co">% script analyze.m</span>
for idx = <span class="fl">1</span>:<span class="fl">3</span>
<span class="co">% Generate strings for file and image names:</span>
file_name = sprintf(<span class="st">'inflammation-%02d.csv'</span>, idx);
img_name = sprintf (<span class="st">'patient_data-%02d.png'</span>, idx);
patient_data = csvread(file_name);
ave_inflammation = mean(patient_data, <span class="fl">1</span>);
figure()
subplot(<span class="fl">2</span>, <span class="fl">2</span>, <span class="fl">1</span>);
plot(ave_inflammation);
ylabel(<span class="st">'average'</span>)
subplot(<span class="fl">2</span>, <span class="fl">2</span>, <span class="fl">2</span>);
plot(max(patient_data, [], <span class="fl">1</span>));
ylabel(<span class="st">'max'</span>)
subplot(<span class="fl">2</span>, <span class="fl">2</span>, <span class="fl">3</span>);
plot(min(patient_data, [], <span class="fl">1</span>));
ylabel(<span class="st">'min'</span>)
print(<span class="st">'-dpng'</span>, img_name);
close();
end</code></pre>
<p>Remember that to run our script, we simply type in its name in the command line:</p>
<p><img src="img/02-loop_1.png" style="width:500px; height:400px"></p>
<p><img src="img/02-loop_2.png" style="width:500px; height:400px"></p>
<p><img src="img/02-loop_3.png" style="width:500px; height:400px"></p>
<p>Sure enough, the maxima of these data sets show exactly the same ramp as the first, and their minima show the same staircase structure.</p>
<div id="another-way-to-analyze-multiple-files" class="challenge panel panel-success">
<div class="panel-heading">
<h2><span class="glyphicon glyphicon-pencil"></span>Another Way to Analyze Multiple Files</h2>
</div>
<div class="panel-body">
<p>In cases where our file names don’t follow such a regular pattern, we might want to process all files that end with a given extension, say <code>.csv</code>. At the command line we could get this list of files by using a <a href="reference.html#wildcard">wildcard</a>:</p>
<pre class="sourceCode bash"><code class="sourceCode bash"><span class="kw">ls</span> *.csv</code></pre>
<p>Thankfully, Matlab also has <code>ls</code>, though it returns a single long string:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">filestr = ls(<span class="st">'*.csv'</span>)</code></pre>
<pre class="output"><code>inflammation-01.csv inflammation-04.csv inflammation-07.csv inflammation-10.csv
inflammation-02.csv inflammation-05.csv inflammation-08.csv inflammation-11.csv
inflammation-03.csv inflammation-06.csv inflammation-09.csv inflammation-12.csv</code></pre>
<p>To turn this string into an array we can loop over (actually, a <a href="http://www.mathworks.com/help/matlab/cell-arrays.html">Cell Array</a>), we need to “split” the string at each occurrence of whitespace:</p>
<pre class="sourceCode matlab"><code class="sourceCode matlab">file_string = ls(<span class="st">'*.csv'</span>);
file_list = strsplit(file_string)</code></pre>
<pre class="output"><code>file_list =
Columns 1 through 3
'inflammation-01.csv' 'inflammation-04.csv' 'inflammation-07.csv'
Columns 4 through 6
'inflammation-10.csv' 'inflammation-02.csv' 'inflammation-05.csv'
Columns 7 through 9
'inflammation-08.csv' 'inflammation-11.csv' 'inflammation-03.csv'
Columns 10 through 13
'inflammation-06.csv' 'inflammation-09.csv' 'inflammation-12.> csv' ''</code></pre>
<p>Using this trick, rewrite the <code>analyze</code> script to analyze all <code>csv</code> files in the current directory. Be careful of the empty string <code>''</code> at the end of <code>file_list</code>!</p>
</div>
</div>
</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>