mle#

  • Series: lecture-python.myst

  • File: lectures/mle.md

  • Audit date: 2026-08-26

  • Corpus snapshot: e25fdf2345

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

  • Overall score: 7.2 / 10

  • Priority: HIGH

Score breakdown#

Category

Score

One-line note

Writing

4.5/10

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

Math

3/10

qe-math-002 ×19; qe-math-004 ×106; qe-math-009 ×5.

Code

7.5/10

qe-code-001 ×7.

JAX

out of scope

JAX rules target lecture-jax.

Figures

5.5/10

qe-fig-005 ×6; qe-fig-006 ×4; qe-fig-001 ×7, +1 more.

References

10/10

no mechanical violations detected.

Links

10/10

no mechanical violations detected.

Admonitions

10/10

no mechanical violations detected.

Issues#

Critical#

None found.

High severity#

  • [qe-code-001] (reviewer) — Follow PEP8 unless closer to mathematical notation. Count: 7. Lines: 66, 433, 500, 550, 611, 631, 785. Example: the name logL is bound to four different functions - 403 (scalar quadratic), 497 (logL(\u03b2, model) for the Poisson), 611 (scalar quadratic again) and 946 (the Probit) - and G_logL / H_logL are rebound twice, at 515-516 and 953-954. newton_raphson (536-565) then reads G_logL and H_logL out of module globals at 550 instead of taking them as arguments, so the Probit call at 1027 silently depends on 953-954 having run, and the Poisson call at 583 stops working once they have. \u03b2 is likewise three different objects: a coefficient vector at 221 and 577, a jnp.linspace grid at 416 and 627, and a float loop variable at 631, where the loop shadows the grid the previous line just plotted. Two imports are dead - gammaln (66) and Axes3D (73, the 3-D axes come from add_subplot(projection="3d") at 302) - and gammaln is exactly what 500 needs: jnp.log(factorial(y)) overflows for the sample it is written for, while gammaln(y + 1) is the stable form and is already imported. 498 binds y = model.y and 500 then uses both model.y and y in one expression. 404 and 612 space the exponentiation (-((\u03b2 - 10) ** 2)). 433 puts two calls on one line as a tuple, ax1.grid(), ax2.grid(). And 785-786 uses a backslash continuation inside a string literal, so the printed table title reads “Explaining the Number of Billionaires” followed by twenty-four spaces before “in 2008”.

  • [qe-fig-001] — Do not set figure size unless necessary. Count: 7. Lines: 130, 180, 232, 301, 418, 628, 827. Example: figsize=.

  • [qe-fig-005] — Descriptive figure names for cross-referencing. Count: 6. Lines: 127, 175, 217, 415, 624, 800. Example: code-cell figure without mystnb figure metadata.

  • [qe-math-002] — Use \top for transpose notation. Count: 19. Lines: 207, 465, 871, 900, 901, 918, 919, 926, 927, 929, …. Example: apostrophe transpose i'.

  • [qe-math-004] — Do not use bold face for matrices or vectors. Count: 106. Lines: 195, 202, 207, 212, 215, 257, 264, 267, 276, 278, …. Example: \mathbf.

  • [qe-math-009] (reviewer) — Choose simplicity in mathematical notation. Count: 5. Lines: 288, 398, 424, 469, 686. Example: the likelihood is written both ways, and one of them is wrong: \mathcal{L}(\boldsymbol{\beta}) at 280, 339, 351, 594, 898, 916 and 926, but \mathcal{L(\beta)} - argument inside the calligraphic group, so the parenthesis and the \(\beta\) render calligraphic too - at 398, 424, 427, 439, 464, 465 and 643. In the same axis labels, log is typed as three italic letters rather than \log (424 $log \mathcal{L(\beta)}$, 427 \frac{dlog \mathcal{L(\beta)}}{d \beta}, 643), where 351, 398, 439, 594, 898 and 916 use \log. The Poisson mean is \(\mu\) in every display (115, 202, 208, 318, 363) and in the code (123, 235, 292, 499), but 288 calls it \(\lambda\) once - “If \(y_i\) follows a Poisson distribution with \(\lambda = 7\)” - immediately above a cell that passes \u03bc=7. 686-689 typesets four multi-word prose labels as italic math with manual spacers, ${number\ of\ billionaires}_i$, $\log{GDP\ per\ capita}_i$, $\log{population}_i$, ${years\ in\ GATT}_i$, where \text{...} is both shorter and correct. And 469 writes < tol in math mode, rendering the tolerance as the product of three variables.

Medium severity#

  • [qe-fig-006] — Lowercase axis labels. Count: 4. Lines: 184, 185, 829, 830. Example: axis label Number of billionaires in 2008.

  • [qe-fig-008] — Use lw=2 for line charts. Count: 3. Lines: 136, 239, 635. Example: plot() without lw=.

  • [qe-writing-001] — Use one sentence per paragraph. Count: 1. Lines: 740. Example: 2 sentences in one paragraph.

  • [qe-writing-002] (reviewer) — Keep writing clear, concise, and valuable. Count: 4. Lines: 282, 324, 510, 905. Example: 324-326 tells the reader something false about the central object of the lecture: “The likelihood function is the same as the joint pmf, but treats the parameter \(\boldsymbol{\beta}\) as a random variable and takes the observations \((y_i, \mathbf{x}_i)\) as given.” In maximum likelihood \(\boldsymbol{\beta}\) is the argument of the function, not a random variable - treating it as random is the Bayesian reading, which this lecture is not doing. 905 and 911 make a second terminology error twice over: “the derivative of a cumulative probability distribution is its marginal distribution” and “\(\phi\) is the marginal normal distribution” - \(\phi\) is the density, and “marginal” means something else. 282 writes “Assume we have some data \(y_i = \{y_1, y_2\}\)”, setting a subscripted scalar equal to a two-element set, where \(y_i\) is a scalar observation everywhere else in the file. And 510 is ungrammatical: “(The documentation also states that when matrices that are near-square, jax.jacfwd probably has an edge over jax.jacrev.)”.

  • [qe-writing-003] (reviewer) — Maintain logical flow. Count: 4. Lines: 262, 503, 543, 606. Example: the parameter count contradicts itself three ways. 262 says the conditional distribution “contains 4 (\(k = 4\)) parameters”; 267-272 displays \(\boldsymbol{\beta}\) as \((\beta_0, \beta_1, \beta_2, \beta_3)\), which is four entries and therefore \(k = 3\) under the convention set at 208 (\(\mu_i = \exp(\beta_0 + \beta_1 x_{i1} + \ldots + \beta_k x_{ik})\)); and the simulation immediately below uses five, \u03b2 = jnp.array([0.26, 0.18, 0.25, -0.1, -0.22]) at 221 with five-element \(\mathbf{x}_i\) at 225-228. The Treisman specification the section is building towards has four (const plus the three regressors listed at 686-689, matching reg1 at 704), so 221 is the odd one out and 262’s “\(k = 4\)” is off by one either way. Next, 503 says “To find the gradient of the poisson_logL” - there is no poisson_logL anywhere in the file; the function is logL (497). Third, the printed header at 543 labels the parameter column "\u03b8" while everything around it - 536, 551-553, 556, 563 - calls the parameter \u03b2, so the algorithm’s own output uses a symbol the lecture never defines. Fourth, 606-607 says “The iterative process can be visualized in the following diagram, where the maximum is found at \(\beta = 10\)”, but the iterative process the reader has just watched (583) fitted a three-parameter Poisson model, and the diagram at 624-646 draws tangents to the toy quadratic redefined at 611 - two unrelated objects joined by “the following”.

  • [qe-writing-004] — Avoid unnecessary capitalization in narrative text. Count: 2. Lines: 844. Example: mid-sentence ‘Likelihood’.

  • [qe-writing-005] (reviewer) — Use bold for definitions, italic for emphasis. Count: 2. Lines: 88, 904. Example: only five emphasis spans exist in the file and two of them are the wrong weight. 88 introduces a term in italic where the rule asks for bold - “we need to make an assumption as to which parametric class of distributions is generating the data” - and it is a definition, since 92 then explains what such a class is. 904 does the reverse, bolding a named theorem that is being used rather than defined: “Using the fundamental theorem of calculus, the derivative of a cumulative probability distribution …”. The other three are right: maximum likelihood estimates (99) and Poisson regression (197) are definitions in bold, maximum (444) is emphasis in italic, and Forbes’ (158) is a publication title.

  • [qe-writing-007] (reviewer) — Use visual elements to enhance understanding. Count: 2. Lines: 557, 624. Example: the Newton-Raphson section makes three claims about the iteration path - 586-587 (“convergence in only 7 iterations”), 589 (“with each iteration, the log-likelihood value increased”) and 594-598 (“the increase in \(\log \mathcal{L}(\boldsymbol{\beta}_{(k)})\) becomes smaller with each iteration … because the gradient is approaching 0”) - and all three are left for the reader to read off the printed table produced at 543-558. The seven log-likelihood values are already in hand; plotting them against \(k\) makes all three claims visible at once and is two lines. Instead the figure that does appear, at 624-646, illustrates a different function (the scalar quadratic redefined at 611) with tangent lines at \(\beta = 7, 8.5, 9.5, 10\) - a good picture of why Newton-Raphson works, but not of the run just performed, which is what 606 says it shows.

Low severity#

None found.

Strengths#

  • The case convention for densities and distributions (proposed qe-math-015 (proposed)) is right everywhere it appears: lowercase \(f\) for the Poisson pmf (115, 202, 286, 318, 353-360, 870) and for the conditional pmf plotted at 147 and 251, uppercase \(\Phi\) for the normal CDF against lowercase \(\phi\) for its density in the probit exercise (871, 875, 900-901, 908, 916-932), and \(\Phi(s)\) differentiated to \(\phi(s)\) at 908 - the one place where getting the case wrong would break the derivation.

  • The lecture earns its numerical method rather than asserting it: 371-381 derives the Poisson log-likelihood in four displayed steps and then states plainly that “no analytical solution exists to the above problem”, which is what motivates Newton-Raphson at 383-479, and 342-345 justifies working with the log at all by naming the reason (monotone transformation) and giving a one-line example the reader can differentiate mentally.

  • The Newton-Raphson update is introduced with a picture of its own logic before any code: 395-399 posits a scalar log-likelihood, 401-413 differentiates it with jax.grad and jax.vmap (each linked to its own documentation page), 415-435 plots the function above its derivative on shared axes, and 438-446 reads the maximum off the pair and adds the second-order check.

  • The JAX machinery is explained where it is used, not assumed: 407-409 says what jax.grad and jax.vmap do, and 505-512 quotes the JAX documentation on the jacfwd / jacrev choice and gives the reason for picking jacfwd for the Hessian - so a reader who has not met forward-mode differentiation is not stopped.

  • The empirical section replicates a real published result and shows its work: three nested specifications defined explicitly as column lists (704-724), estimated with the paper’s own robust standard errors (cov_type="HC0", 735, 772), collected into one table with summary_col and a fixed regressor_order (776-783), and then turned into the paper’s actual finding - the residual bar chart at 800-831 that puts Russia at the top, with the magnitude stated at 834-835.

  • Both exercises are properly scaffolded and both solutions are complete: mle_ex1 asks for the log-likelihood, gradient and Hessian of the probit model and the solution derives all three (895-933) before writing the class, and mle_ex2 supplies \(\mathbf{X}\), \(y\) and \(\boldsymbol{\beta}_{(0)}\) as displayed matrices (968-993) and asks for verification against statsmodels (995-999) - and 1002-1004 warns that the hand-rolled algorithm is sensitive to starting values, which is the honest caveat.

  • Every code-cell display uses bmatrix (267-272, 970-976, 979-985, 988-992) - qe-math-003 measures zero - and the two equation labels that exist are both used: poissonreg (200) is cited at 684.

  • qe-code-003 and all five admonition rules measure zero: the single non-Anaconda dependency is installed in the first code cell with :tags: [hide-output] (33-37), the GPU note is factored out into {include} _admonition/gpu.md (24), and both solutions carry :class: dropdown.