Skip to content

Commit 494ec09

Browse files
authored
Merge branch 'gh-pages' into remove-dictionaries-from-errors
2 parents de98838 + 5a5feae commit 494ec09

File tree

14 files changed

+105
-54
lines changed

14 files changed

+105
-54
lines changed

.github/workflows/template.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,14 @@ jobs:
127127
id: check-rmd
128128
working-directory: lesson
129129
run: |
130-
echo "::set-output name=count::$(shopt -s nullglob; files=($(find . -iname '*.Rmd')); echo ${#files[@]})"
130+
echo "count=$(shopt -s nullglob; files=($(find . -iname '*.Rmd')); echo ${#files[@]})" >> $GITHUB_OUTPUT
131131
132132
- name: Set up R
133133
if: steps.check-rmd.outputs.count != 0
134-
uses: r-lib/actions/setup-r@master
134+
uses: r-lib/actions/setup-r@v2
135135
with:
136-
r-version: 'release'
136+
use-public-rspm: true
137+
install-r: false
137138

138139
- name: Install needed packages
139140
if: steps.check-rmd.outputs.count != 0

.github/workflows/website.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,14 @@ jobs:
4444
- name: Look for R-markdown files
4545
id: check-rmd
4646
run: |
47-
echo "::set-output name=count::$(shopt -s nullglob; files=($(find . -iname '*.Rmd')); echo ${#files[@]})"
47+
echo "count=$(shopt -s nullglob; files=($(find . -iname '*.Rmd')); echo ${#files[@]})" >> $GITHUB_OUTPUT
4848
4949
- name: Set up R
5050
if: steps.check-rmd.outputs.count != 0
51-
uses: r-lib/actions/setup-r@master
51+
uses: r-lib/actions/setup-r@v2
5252
with:
53-
r-version: 'release'
53+
use-public-rspm: true
54+
install-r: false
5455

5556
- name: Restore R Cache
5657
if: steps.check-rmd.outputs.count != 0

_episodes/01-intro.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ keypoints:
1414
- "Use `variable = value` to assign a value to a variable in order to record it in memory."
1515
- "Variables are created on demand whenever a value is assigned to them."
1616
- "Use `print(something)` to display the value of `something`."
17+
- "Use `# some kind of explanation` to add comments to programs."
1718
- "Built-in functions are always available to use."
19+
1820
---
1921

2022
## Variables
@@ -42,7 +44,7 @@ weight_kg = 60
4244
{: .language-python}
4345

4446
From now on, whenever we use `weight_kg`, Python will substitute the value we assigned to
45-
it. In layman's terms, **a variable is a name for a value**.
47+
it. In layperson's terms, **a variable is a name for a value**.
4648

4749
In Python, variable names:
4850

@@ -209,8 +211,12 @@ weight in kilograms is now: 65.0
209211
> ~~~
210212
> {: .output}
211213
>
212-
> ![Value of 65.0 with weight_kg label stuck on it, and value of 143.0 with weight_lb label
213-
stuck on it](../fig/python-sticky-note-variables-02.svg)
214+
> Everything in a line of code following the '#' symbol is a
215+
> [comment]({{ page.root }}/reference/#comment) that is ignored by Python.
216+
> Comments allow programmers to leave explanatory notes for other
217+
> programmers or their future selves.
218+
>
219+
> ![Value of 65.0 with weight_kg label stuck on it, and value of 143.0 with weight_lb label stuck on it](../fig/python-sticky-note-variables-02.svg)
214220
>
215221
> Similar to above, the expression `2.2 * weight_kg` is evaluated to `143.0`,
216222
> and then this value is assigned to the variable `weight_lb` (i.e. the sticky

_episodes/02-numpy.md

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ keypoints:
1717
- "Use `array[x, y]` to select a single element from a 2D array."
1818
- "Array indices start at 0, not 1."
1919
- "Use `low:high` to specify a `slice` that includes the indices from `low` to `high-1`."
20-
- "Use `# some kind of explanation` to add comments to programs."
2120
- "Use `numpy.mean(array)`, `numpy.max(array)`, and `numpy.min(array)` to calculate simple statistics."
2221
- "Use `numpy.mean(array, axis=0)` or `numpy.mean(array, axis=1)` to calculate statistics across the specified axis."
2322
---
@@ -32,7 +31,7 @@ that can be called upon when needed.
3231

3332
To begin processing the clinical trial inflammation data, we need to load it into Python.
3433
We can do that using a library called
35-
[NumPy](http://docs.scipy.org/doc/numpy/ "NumPy Documentation"), which stands for Numerical Python.
34+
[NumPy](https://numpy.org/doc/stable "NumPy Documentation"), which stands for Numerical Python.
3635
In general, you should use this library when you want to do fancy things with lots of numbers,
3736
especially if you have matrices or arrays. To tell Python that we'd like to start using NumPy,
3837
we need to [import]({{ page.root }}/reference.html#import) it:
@@ -70,9 +69,8 @@ The expression `numpy.loadtxt(...)` is a
7069
[function call]({{ page.root }}/reference.html#function-call)
7170
that asks Python to run the [function]({{ page.root }}/reference.html#function) `loadtxt` which
7271
belongs to the `numpy` library.
73-
This [dotted notation]({{ page.root }}/reference.html#dotted-notation)
74-
is used everywhere in Python: the thing that appears before the dot contains the thing that
75-
appears after.
72+
The dot notation in Python is used most of all as an object attribute/property specifier or for invoking its method. `object.property` will give you the object.property value,
73+
`object_name.method()` will invoke on object_name method.
7674

7775
As an example, John Smith is the John that belongs to the Smith family.
7876
We could use the dot notation to write his name `smith.john`,
@@ -205,16 +203,16 @@ first value in data: 0.0
205203
{: .output}
206204
207205
~~~
208-
print('middle value in data:', data[30, 20])
206+
print('middle value in data:', data[29, 19])
209207
~~~
210208
{: .language-python}
211209
212210
~~~
213-
middle value in data: 13.0
211+
middle value in data: 16.0
214212
~~~
215213
{: .output}
216214
217-
The expression `data[30, 20]` accesses the element at row 30, column 20. While this expression may
215+
The expression `data[29, 19]` accesses the element at row 30, column 20. While this expression may
218216
not surprise you,
219217
`data[0, 0]` might.
220218
Programming languages like Fortran, MATLAB and R start counting at 1
@@ -411,11 +409,6 @@ maximum inflammation for patient 0: 18.0
411409
~~~
412410
{: .output}
413411
414-
Everything in a line of code following the '#' symbol is a
415-
[comment]({{ page.root }}/reference.html#comment) that is ignored by Python.
416-
Comments allow programmers to leave explanatory notes for other
417-
programmers or their future selves.
418-
419412
We don't actually need to store the row in a variable of its own.
420413
Instead, we can combine the selection and the function call:
421414

_episodes/04-lists.md

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -166,57 +166,80 @@ does not.
166166
> ## Nested Lists
167167
> Since a list can contain any Python variables, it can even contain other lists.
168168
>
169-
> For example, we could represent the products in the shelves of a small grocery shop:
169+
> For example, you could represent the products on the shelves of a small grocery shop
170+
> as a nested list called `veg`:
171+
>
172+
> ![`veg` is represented as a shelf full of produce. There are three rows of vegetables
173+
> on the shelf, and each row contains three baskets of vegetables. We can label
174+
> each basket according to the type of vegetable it contains, so the top row
175+
> contains (from left to right) lettuce, lettuce, and peppers.](../fig/04_groceries_veg.png)
176+
>
177+
> To store the contents of the shelf in a nested list, you write it this way:
170178
>
171179
> ~~~
172-
> x = [['pepper', 'zucchini', 'onion'],
173-
> ['cabbage', 'lettuce', 'garlic'],
174-
> ['apple', 'pear', 'banana']]
180+
> veg = [['lettuce', 'lettuce', 'peppers', 'zucchini'],
181+
> ['lettuce', 'lettuce', 'peppers', 'zucchini'],
182+
> ['lettuce', 'cilantro', 'peppers', 'zucchini']]
175183
> ~~~
176184
> {: .language-python}
177185
>
178-
> Here is a visual example of how indexing a list of lists `x` works:
186+
> Here are some visual examples of how indexing a list of lists `veg` works. First,
187+
> you can reference each row on the shelf as a separate list. For example, `veg[2]`
188+
> represents the bottom row, which is a list of the baskets in that row.
179189
>
180-
> [![x is represented as a pepper shaker containing several packets of pepper. [x[0]] is represented
181-
> as a pepper shaker containing a single packet of pepper. x[0] is represented as a single packet of
182-
> pepper. x[0][0] is represented as single grain of pepper. Adapted
183-
> from @hadleywickham.](../fig/indexing_lists_python.png)][hadleywickham-tweet]
190+
> ![`veg` is now shown as a list of three rows, with `veg[0]` representing the top row of
191+
> three baskets, `veg[1]` representing the second row, and `veg[2]` representing the bottom row.](../fig/04_groceries_veg0.png)
184192
>
185-
> Using the previously declared list `x`, these would be the results of the
186-
> index operations shown in the image:
193+
> Index operations using the image would work like this:
187194
>
188195
> ~~~
189-
> print([x[0]])
196+
> print(veg[2])
190197
> ~~~
191198
> {: .language-python}
192199
>
193200
> ~~~
194-
> [['pepper', 'zucchini', 'onion']]
201+
> ['lettuce', 'cilantro', 'peppers', 'zucchini']
195202
> ~~~
196203
> {: .output}
197204
>
198205
> ~~~
199-
> print(x[0])
206+
> print(veg[0])
207+
> ~~~
208+
> {: .language-python}
209+
>
210+
> ~~~
211+
> ['lettuce', 'lettuce', 'peppers', 'zucchini']
212+
> ~~~
213+
> {: .output}
214+
>
215+
> To reference a specific basket on a specific shelf, you use two indexes. The first
216+
> index represents the row (from top to bottom) and the second index represents
217+
> the specific basket (from left to right).
218+
> ![`veg` is now shown as a two-dimensional grid, with each basket labeled according to
219+
> its index in the nested list. The first index is the row number and the second
220+
> index is the basket number, so `veg[1][3]` represents the basket on the far right
221+
> side of the second row (basket 4 on row 2): zucchini](../fig/04_groceries_veg00.png)
222+
>
223+
> ~~~
224+
> print(veg[0][0])
200225
> ~~~
201226
> {: .language-python}
202227
>
203228
> ~~~
204-
> ['pepper', 'zucchini', 'onion']
229+
> 'lettuce'
205230
> ~~~
206231
> {: .output}
207232
>
208233
> ~~~
209-
> print(x[0][0])
234+
> print(veg[1][2])
210235
> ~~~
211236
> {: .language-python}
212237
>
213238
> ~~~
214-
> 'pepper'
239+
> 'peppers'
215240
> ~~~
216241
> {: .output}
217242
>
218-
> Thanks to [Hadley Wickham][hadleywickham-tweet]
219-
> for the image above.
220243
{: .callout}
221244
222245
> ## Heterogeneous Lists

_episodes/05-loop.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -307,8 +307,8 @@ so we should always use it when we can.
307307
> Given the following loop:
308308
> ~~~
309309
> word = 'oxygen'
310-
> for char in word:
311-
> print(char)
310+
> for letter in word:
311+
> print(letter)
312312
> ~~~
313313
> {: .language-python}
314314
>

_episodes/08-func.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,17 @@ Let's start by defining a function `fahr_to_celsius` that converts temperatures
5050
from Fahrenheit to Celsius:
5151

5252
~~~
53+
def explicit_fahr_to_celsius(temp):
54+
# Assign the converted value to a variable
55+
converted = ((temp - 32) * (5/9))
56+
# Return the value of the new variable
57+
return converted
58+
5359
def fahr_to_celsius(temp):
60+
# Return converted value more efficiently using the return
61+
# function without creating a new variable. This code does
62+
# the same thing as the previous function but it is more explicit
63+
# in explaining how the return command works.
5464
return ((temp - 32) * (5/9))
5565
~~~
5666
{: .language-python}

_episodes/09-errors.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,12 @@ hopefully the custom error message is informative enough to help you figure out
195195
> > `7` is not the right index to use with `messages`.
196196
> {: .solution}
197197
{: .challenge}
198+
=======
199+
> ## Better errors on newer Pythons
200+
>
201+
> Newer versions of Python have improved error printouts. If you are debugging errors, it is often
202+
> helpful to use the latest Python version, even if you support older versions of Python.
203+
{: .callout}
198204
199205
## Syntax Errors
200206
@@ -442,7 +448,9 @@ you will receive a `FileNotFoundError` telling you so.
442448
If you attempt to write to a file that was opened read-only, Python 3
443449
returns an `UnsupportedOperationError`.
444450
More generally, problems with input and output manifest as
445-
`IOError`s or `OSError`s, depending on the version of Python you use.
451+
`OSError`s, which may show up as a more specific subclass; you can see
452+
[the list in the Python docs](https://docs.python.org/3/library/exceptions.html#os-exceptions).
453+
They all have a unique UNIX `errno`, which is you can see in the error message.
446454
447455
~~~
448456
file_handle = open('myfile.txt', 'r')

_episodes/10-defensive.md

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,10 @@ def normalize_rectangle(rect):
130130
dx = x1 - x0
131131
dy = y1 - y0
132132
if dx > dy:
133-
scaled = float(dx) / dy
133+
scaled = dx / dy
134134
upper_x, upper_y = 1.0, scaled
135135
else:
136-
scaled = float(dx) / dy
136+
scaled = dx / dy
137137
upper_x, upper_y = scaled, 1.0
138138
139139
assert 0 < upper_x <= 1.0, 'Calculated upper X coordinate invalid'
@@ -304,7 +304,15 @@ Its advocates believe it produces better code faster because:
304304
rather than to find errors.
305305
2. Writing tests helps programmers figure out what the function is actually supposed to do.
306306

307-
Here are three test functions for `range_overlap`:
307+
We start by defining an empty function `range_overlap`:
308+
309+
~~~
310+
def range_overlap(ranges):
311+
pass
312+
~~~
313+
{: .language-python}
314+
315+
Here are three test statements for `range_overlap`:
308316

309317
~~~
310318
assert range_overlap([ (0.0, 1.0) ]) == (0.0, 1.0)
@@ -326,10 +334,9 @@ AssertionError:
326334
{: .error}
327335

328336
The error is actually reassuring:
329-
we haven't written `range_overlap` yet,
330-
so if the tests passed,
331-
it would be a sign that someone else had
332-
and that we were accidentally using their function.
337+
we haven't implemented any logic into `range_overlap` yet,
338+
so if the tests passed, it would indicate that we've written
339+
an entirely ineffective test.
333340

334341
And as a bonus of writing these tests,
335342
we've implicitly defined what our input and output look like:
@@ -534,7 +541,7 @@ This violates another important rule of programming:
534541
> > * The first assertion checks that the input sequence `values` is not empty.
535542
> > An empty sequence such as `[]` will make it fail.
536543
> > * The second assertion checks that each value in the list can be turned into an integer.
537-
> > Input such as `[1, 2,'c', 3]` will make it fail.
544+
> > Input such as `[1, 2, 'c', 3]` will make it fail.
538545
> > * The third assertion checks that the total of the list is greater than 0.
539546
> > Input such as `[-10, 2, 3]` will make it fail.
540547
> {: .solution}

_episodes/11-debugging.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ not more.
268268
> for patient in patients:
269269
> weight, height = patients[0]
270270
> bmi = calculate_bmi(height, weight)
271-
> print("Patient's BMI is: %f" % bmi)
271+
> print("Patient's BMI is:", bmi)
272272
> ~~~
273273
> {: .language-python}
274274
>

0 commit comments

Comments
 (0)