simple_linear_regression#

  • Series: lecture-python-intro

  • File: lectures/simple_linear_regression.md

  • Audit date: 2026-08-26

  • Corpus snapshot: a12d17c0ef

  • Categories audited: writing, math, code, figures, links, admonitions (JAX out of scope)

  • Overall score: 7.8 / 10

  • Priority: LOW

Score breakdown#

Category

Score

One-line note

Writing

4.5/10

qe-writing-004 ×3; qe-writing-005 ×4; qe-writing-003 ×4, +3 more.

Math

8.5/10

qe-math-012 (proposed) ×1; qe-math-009 ×3.

Code

7.5/10

qe-code-001 ×5.

JAX

out of scope

JAX rules target lecture-jax.

Figures

6.5/10

qe-fig-005 ×5; qe-fig-004 ×3.

References

N/A

no citations in this lecture.

Links

10/10

no mechanical violations detected.

Admonitions

9.5/10

qe-admon-002 ×1.

Issues#

Critical#

None found.

High severity#

  • [qe-code-001] (reviewer) — Follow PEP8 unless closer to mathematical notation. Count: 5. Lines: 51, 67, 99, 220, 501. Example: the space after a comma is missing at nineteen sites and the file is inconsistent about it within single lines: y = [2000,1000,1500,2500,500,900,1100,1500,1800, 250] (51) puts one in only before the last element, and df.plot(x='X',y='Y', kind='scatter', ax=ax) (99, and identically at 100, 121, 122, 139, 140, 171, 172, 381, 382, 561, 562) omits it after the first argument and supplies it after the rest. Also np.arange(20,100,0.5) (206), np.arange(-500,500,5) (227), df[['X','Y']] (344), pow(df['X'],2) (356, 544). 67-70 carry trailing whitespace on four consecutive keyword-argument lines. Six statements are terminated with a semicolon to suppress Jupyter output (220, 241, 383, 489, 501, 514), which is E703 - assigning to _ or calling plt.show() does the same job. Eight lines exceed 79 characters, four of them past 100: 416 at 122, 514 at 127, 501 at 117, 466 at 109. And pow(df['X'], 2) at 356 and 544 spells exponentiation as a function call where the rule’s own preferred form is df['X']**2.

  • [qe-fig-005] — Descriptive figure names for cross-referencing. Count: 5. Lines: 482, 488, 500, 513, 556. Example: code-cell figure without mystnb figure metadata.

Medium severity#

  • [qe-fig-004] — Caption formatting conventions. Count: 3. Lines: 91, 113, 131. Example: caption of 7 words.

  • [qe-math-009] (reviewer) — Choose simplicity in mathematical notation. Count: 3. Lines: 25, 80, 285. Example: the sample means are written \(\bar{y_i}\) and \(\bar{x_i}\) - a bar over a subscripted symbol, which reads as the mean of the \(i\)-th observation rather than the mean over \(i\) - at 285, 288, 318, 324, 330 and 336, i.e. in {eq}`eq:optimal-alpha` and {eq}`eq:optimal-beta`, the two results the lecture exists to produce, and in the two {eq} references at 396 and 527. The hat is placed inconsistently in the same way and only two displays apart: \(\hat{y_i}\) at 80 puts it over the whole subscripted symbol, \(\hat{y}_i\) at 151 puts it over the letter alone. And 25 crams the index range into the model display as y_i = \alpha + \beta x_i + \epsilon_i, i = 1,2,...,N, with ... instead of \ldots and no separator before the range.

  • [qe-writing-002] (reviewer) — Keep writing clear, concise, and valuable. Count: 3. Lines: 75, 106, 459. Example: three paragraphs begin in lower case as if continuing a sentence interrupted by a figure - “as you can see the data suggests that more ice-cream is typically sold on hotter days” (75), “now substituting for \(\alpha\)” and its neighbours in the derivation (315, 321, 327, 333, and 261, 267, 273, 279 above it) - which is a defensible running-commentary style for a derivation but reads as a fragment when a figure or a display intervenes. 106-142 says the same thing three times in three copied cells (the guess-and-iterate figures). And the exercise-1 solution carries four sentences of generic advice that state a principle without applying it: “It is often a good idea to at first import a few lines of data from a csv” (426), “Sometimes it can be useful to rename your columns” (438), “It is always a good idea to spend a bit of time understanding what data you actually have” (459), “For example, you may want to explore this data to see if there is consistent reporting” (461) - of which 459 and 461 add nothing that 461-489 does not then demonstrate directly.

  • [qe-writing-003] (reviewer) — Maintain logical flow. Count: 4. Lines: 195, 207, 252, 324. Example: the two “how does error change” figures plot the wrong objective. 177-187 defines the cost function as the sum of squared residuals, \(C = \sum (y_i - \alpha - \beta x_i)^2\), and says OLS minimizes it; the cells at 205-207 and 226-228 then compute abs((α_optimal + β * df['X']) - df['Y']).sum() - the sum of absolute residuals - and 220 and 241 draw plt.axvline(β_optimal) / plt.axvline(α_optimal), the OLS minimisers, on top of a curve that OLS does not minimise. The absolute-value objective is precisely the alternative method slr-ex2 (569-577) asks the reader to consider as a different estimator, so the section that motivates OLS is quietly plotting its competitor. Second, that whole section (191-242) needs an answer it has not derived: 195 says “we know from the next section the optimal values … are” and hardcodes β_optimal = 64.38, α_optimal = -14.72 (198-199), which the next section then recomputes at 357-365 and never compares against. Third, the two central derivative displays are malformed: 252 writes \frac{\partial C}{\partial \alpha}[\sum_{i=1}^{N}{(y_i - \alpha - \beta x_i)^2}], and since \(C\) is that sum (186), the expression reads as the derivative of \(C\) applied to \(C\) rather than \(\partial C/\partial\alpha = \ldots\); 294 repeats it for \(\beta\). Fourth, 324 loses a sign: expanding 318 gives \(x_i y_i - \bar y x_i + \beta \bar x x_i - \beta x_i^2\), and 324 prints \(- \beta \bar{x_i} x_i\); the next line, 330, silently restores the correct sign by writing \(+\beta \sum(\bar{x_i} x_i - x_i^2)\), so a reader following the algebra step by step is stopped between the two.

  • [qe-writing-004] — Avoid unnecessary capitalization in narrative text. Count: 3. Lines: 424, 463. Example: mid-sentence ‘Data’.

  • [qe-writing-005] (reviewer) — Use bold for definitions, italic for emphasis. Count: 4. Lines: 146, 177, 183, 572. Example: bold is used three times for emphasis and never for a definition, and italic is not used anywhere in the file. 177 bolds a verb - “chooses \(\alpha\) and \(\beta\) in such a way that minimizes the sum of the squared residuals” - which is emphasis and belongs in italic; 572 bolds “is not the only way” and 574 bolds “the sum of the absolute values”, both emphasis. Meanwhile four terms are defined with no emphasis at all: “which we will call the residuals” (146), “Ordinary Least Squares (OLS)” in the same sentence as the bolded verb (177), “Let’s call this a cost function” (183), and “line of best fit”, which appears in scare quotes rather than bold at 30, 77, 106 and 368. The result is that a reader scanning for bold finds the three words the lecture wants stressed and none of the four terms it wants remembered.

  • [qe-writing-007] (reviewer) — Use visual elements to enhance understanding. Count: 3. Lines: 113, 212, 407. Example: the guess-and-iterate sequence draws three separate figures for three values of \(\beta\) (91, 113, 131) from three verbatim copies of the same four-line cell, with captions “Scatter plot with a line of fit”, “… #2” and “… #3” - so the progression the reader is meant to see happens across three page-widths instead of on one set of axes, where three labelled lines and a legend would show the whole search at once. The two error figures (212, 233) each take a one-dimensional slice through an objective the lecture has just written as a function of two parameters (186); a contour plot of \(C(\alpha, \beta)\) would show the joint minimum and would also make visible the thing the slices leave implicit - that each is taken holding the other parameter at its optimum. And 407-409 embeds an {raw} html iframe pointing at an interactive chart on ourworldindata.org, so the figure the exercise’s data step depends on is a live third-party page: it will not render in the PDF build, it cannot be reproduced from the lecture’s own code, and the download link two lines later (411) is the reproducible path that is offered only “if you get stuck”. The five figures in the solution (482, 488, 500, 513, 556) carry no mystnb metadata and no captions.

  • [qe-writing-008] — Remove excessive whitespace between words. Count: 3. Lines: 195, 396, 527. Example: 2 spaces.

Low severity#

  • [qe-admon-002] — Use dropdown class for solutions. Count: 1. Lines: 402. Example: {solution-start} without :class: dropdown.

  • [qe-math-012 (proposed)] — Multiplication via \cdot or juxtaposition, never *. Count: 1. Lines: 276. Example: * as multiplication.

Strengths#

  • The lecture teaches OLS by making the reader feel the problem first: 83-142 guesses \(\beta = 10\), then 100, then 65, each time plotting the fit and saying what is wrong with it, and only at 144 turns to “formalizing this guessing process by thinking of this problem as an optimization problem”. For an introductory audience meeting least squares for the first time, that ordering is worth the space it takes.

  • The residual plot at 163-175 is the right figure at the right moment: plt.vlines(df['X'], df['Y_hat'], df['Y'], color='r') draws each \(\hat e_i\) as a red segment between the observation and the line, so the quantity the cost function squares is visible before the cost function is written down at 186 - and the same figure is redrawn on the fitted line at 383 so the reader can compare.

  • The derivation from the first-order conditions to {eq}`eq:optimal-alpha` and {eq}`eq:optimal-beta` (249-337) is done in twelve one-step displays, each with a sentence saying what was done (“we can remove the constant \(-2\) from the summation by dividing both sides by \(-2\)”, “the middle term is a straightforward sum from \(i=1,...N\) by a constant \(\alpha\)”, “this can be split into two summations”) - the granularity a first course needs, and the two results are then labelled and cited from the exercise at 396 and 527.

  • The algebra and the code are matched line for line: 355-357 implements the numerator and denominator of {eq}`eq:optimal-beta` as two DataFrame columns named num and den and then divides their sums, and 364 implements {eq}`eq:optimal-alpha` directly as y_bar - β * x_bar - so a reader can point at each symbol in the formula and find it in the cell.

  • slr-ex1 is a real end-to-end exercise rather than a drill: it asks for a prediction before the data (393), sends the reader to a named public source, and the solution then spends 424-492 on the data work that actually matters - noticing the NaN rows, discovering that only Great Britain reports 1543, plotting the count of reporting countries per year (489), and concluding that “if you are doing cross-sectional comparisons then more recent data will include a wider set of countries”.

  • The log transform at 509-521 is motivated by looking at the untransformed scatter first (501), stating the two patterns it shows (506-507), then replotting with logx=True and saying what changed - “a linear model fits the shape of the data more closely” (517) - before applying np.log10 and re-estimating. That is the honest order: see the curvature, then transform.

  • Every one of the seven body figures carries mystnb caption and name metadata (63, 95, 117, 135, 167, 216, 237, 374), which is better than most files in this series manage.