simple_linear_regression#
Series: lecture-python-intro
File:
lectures/simple_linear_regression.mdAudit date: 2026-08-26
Corpus snapshot:
a12d17c0efCategories 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 |
|
Math |
8.5/10 |
|
Code |
7.5/10 |
|
JAX |
out of scope |
JAX rules target |
Figures |
6.5/10 |
|
References |
N/A |
no citations in this lecture. |
Links |
10/10 |
no mechanical violations detected. |
Admonitions |
9.5/10 |
|
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, anddf.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. Alsonp.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 callingplt.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. Andpow(df['X'], 2)at 356 and 544 spells exponentiation as a function call where the rule’s own preferred form isdf['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 asy_i = \alpha + \beta x_i + \epsilon_i, i = 1,2,...,N, with...instead of\ldotsand 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 drawplt.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 methodslr-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} htmliframe 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 namednumanddenand then divides their sums, and 364 implements{eq}`eq:optimal-alpha`directly asy_bar - β * x_bar- so a reader can point at each symbol in the formula and find it in the cell.slr-ex1is 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 theNaNrows, 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=Trueand saying what changed - “a linear model fits the shape of the data more closely” (517) - before applyingnp.log10and re-estimating. That is the honest order: see the curvature, then transform.Every one of the seven body figures carries mystnb
captionandnamemetadata (63, 95, 117, 135, 167, 216, 237, 374), which is better than most files in this series manage.
Recommended actions#
Fix the objective in the two error sections: 207 and 228 sum absolute residuals while 177-186 defines the cost as the sum of squared residuals, so both figures plot a function whose minimum is not the OLS solution that 220 and 241 mark on them. Square the residuals, or say explicitly that these two figures use the absolute-value criterion - which is what
slr-ex2(569-577) poses as the alternative method.Correct the two derivative displays at 252 and 294:
\frac{\partial C}{\partial \alpha}[\sum (y_i - \alpha - \beta x_i)^2]reads as the derivative of \(C\) applied to \(C\), since 186 already defines \(C\) as that sum. Write\frac{\partial C}{\partial \alpha} = \sum_{i=1}^{N} -2(y_i - \alpha - \beta x_i)and let 258 be the first-order condition.Fix the sign at 324: expanding 318 gives \(+\beta \bar x x_i\), not \(-\beta \bar x x_i\), and 330 already uses the correct sign, so as it stands the reader sees the error appear and disappear between two consecutive lines.
Remove the forward dependence at 195-199: the section computes error curves from hardcoded
β_optimal = 64.38andα_optimal = -14.72taken from a section that has not run yet. Either move 191-242 after “Calculating optimal values” and use the computedα,β, or state the two numbers as a preview and add an assertion later that the derivation reproduces them. While there,[the next section](slr:optimal-values)at 195 is a markdown link pointing at a MyST label and should be{ref}the next section slr:optimal-values`` - it is the shape that matches neither link rule and so is invisible to the checker.Write the sample means as \(\bar y\) and \(\bar x\) rather than \(\bar{y_i}\) and \(\bar{x_i}\) (285, 288, 318, 324, 330, 336): a bar over a subscripted symbol denotes the mean of one observation, and these are the two named results
{eq}`eq:optimal-alpha`and{eq}`eq:optimal-beta`. Settle \(\hat{y_i}\) (80) against \(\hat{y}_i\) (151) at the same time.Answer the two questions the solution skips:
slr-ex1asks six things (393-398) and the solution labels Q2, Q3, Q4 and Q5 only - Q1 (the prior prediction) and Q6 (“interpret the coefficients and write a summary sentence”) are unanswered, and Q6 is the one that carries the economics. Then giveslr-ex2a solution: it currently ends the lecture with an unanswered question, and the absolute-value objective it asks about is already computed at 207 and 228.Collapse the three guess-and-iterate figures (91, 113, 131) into one set of axes with three labelled lines - the cells are verbatim copies apart from the value of \(\beta\) - and replace the two one-dimensional error slices (212, 233) with a contour plot of \(C(\alpha, \beta)\), which is the object 186 defines.
Make the exercise-1 data step reproducible without the third-party iframe at 407-409: the
{raw} htmlembed will not render in the PDF build and the download link at 411 is offered only as a fallback. Also remove or recompute the vintage-specific numbers stated in prose - “from 62156 to 12445” (455), “the Year 1543” (470), “the most recent year in the dataset 2018” (494) - which are read from a live URL.Use
data['log_gdppc']at 557 rather thandf['log_gdppc']: the assignment mixes the two frames and only works because the indices happen to align after thereset_index(drop=True)at 497, and the neighbouring cell at 544 already usesdata.Bold the four definitions and italicise the three emphases: residuals (146), ordinary least squares (177), cost function (183), line of best fit (30); minimizes (177), only (572), absolute values (574).
Sweep the code style: the nineteen missing spaces after commas (51, 52, 99, 100, 121, 122, 139, 140, 171, 172, 206, 227, 344, 356, 381, 382, 544, 561, 562), the trailing whitespace at 67-70, the six output-suppressing semicolons (220, 241, 383, 489, 501, 514), the eight over-long lines (416 at 122 characters, 514 at 127, 501 at 117, 466 at 109, plus 433, 489, 543, 563), and
pow(df['X'], 2)in favour ofdf['X']**2at 356 and 544.Finish the mechanical items: mystnb
caption/namemetadata on the five solution figures (482, 488, 500, 513, 556),:class: dropdownon the{solution-start}at 402 (qe-admon-002),\cdotor juxtaposition instead ofN*\alphaat 276 (qe-math-012 (proposed)), the three over-long captions at 91, 113 and 131, the three double spaces at 195, 396 and 527, and the thirteen prose lines with trailing whitespace (30, 37-43, 75, 144, 177, 279, 459, 572, 574). The threeqe-writing-004hits at 424 and 463 are “Our World in Data” and “Life Expectancy Data” - the first is the proper name of the source and should be left alone; the second is a real over-capitalisation.