From 98bc39c5eaf3a4bed0e48ab1b2296d37637bd459 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Tue, 4 Aug 2026 09:04:37 +1000 Subject: [PATCH 1/4] FIX: lecture cleanup batch from the 2026-08 tracker audit - polars: guard Exercise 1 first/last with drop_nulls (#589) - polars, pandas: migrate the remaining legacy np.random.* call sites to the Generator API (#552) - pandas_panel: use direct raw.githubusercontent.com data URLs (#580) - python_by_example: include the rng line in the white-noise snippet and correct the line count (#552) - functions, scipy: define rng in the cells that use it (#552) - numpy: document DiscreteRV's seed parameter (#552) and reference qe.Timer() instead of the retired tic/toc wording (#594) - about_py: standardize on PyTorch (#552) - autodiff: split the PRNG key once into three single-use keys (#529) Co-Authored-By: Claude Fable 5 --- lectures/about_py.md | 4 ++-- lectures/autodiff.md | 6 +++--- lectures/functions.md | 6 ++++++ lectures/numpy.md | 8 ++++++-- lectures/pandas.md | 3 ++- lectures/pandas_panel.md | 6 +++--- lectures/polars.md | 19 ++++++++++--------- lectures/python_by_example.md | 9 ++++++--- lectures/scipy.md | 1 + 9 files changed, 39 insertions(+), 23 deletions(-) diff --git a/lectures/about_py.md b/lectures/about_py.md index e719c6b3..9dd8851c 100644 --- a/lectures/about_py.md +++ b/lectures/about_py.md @@ -139,7 +139,7 @@ popularity of a single Python deep learning library ```{figure} /_static/lecture_specific/about_py/pytorch_vs_matlab.png ``` -Pytorch is just one of several Python libraries for deep learning and AI. +PyTorch is just one of several Python libraries for deep learning and AI. @@ -349,7 +349,7 @@ We will discuss the details later in the lecture series, where we cover NumPy in While NumPy is still the king of array processing in Python, there are now important competitors. -Libraries such as [JAX](https://github.com/jax-ml/jax), [Pytorch](https://pytorch.org/), and [CuPy](https://cupy.dev/) also have +Libraries such as [JAX](https://github.com/jax-ml/jax), [PyTorch](https://pytorch.org/), and [CuPy](https://cupy.dev/) also have built in array types and array operations that can be very fast and efficient. In fact these libraries are better at exploiting parallelization and fast hardware, as diff --git a/lectures/autodiff.md b/lectures/autodiff.md index 843c8a8d..99c4e8f8 100644 --- a/lectures/autodiff.md +++ b/lectures/autodiff.md @@ -350,11 +350,11 @@ Let's generate some simulated data: ```{code-cell} ipython3 n = 100 key = jax.random.key(1234) -x = jax.random.uniform(key, (n,)) +key, x_key, ϵ_key = jax.random.split(key, 3) +x = jax.random.uniform(x_key, (n,)) α, β, σ = 0.5, 1.0, 0.1 # Set the true intercept and slope. -key, subkey = jax.random.split(key) -ϵ = jax.random.normal(subkey, (n,)) +ϵ = jax.random.normal(ϵ_key, (n,)) y = α * x + β + σ * ϵ ``` diff --git a/lectures/functions.md b/lectures/functions.md index cdc88059..3de5f4b5 100644 --- a/lectures/functions.md +++ b/lectures/functions.md @@ -305,6 +305,8 @@ This is accomplished in the next program (funcloopprog)= ```{code-cell} python3 +rng = np.random.default_rng() + def generate_data(n): ϵ_values = [] for i in range(n): @@ -334,6 +336,8 @@ This is achieved in the next piece of code. (funcloopprog2)= ```{code-cell} python3 +rng = np.random.default_rng() + def generate_data(n, generator_type): ϵ_values = [] for i in range(n): @@ -366,6 +370,8 @@ To understand this, consider the following version. (test_program_6)= ```{code-cell} python3 +rng = np.random.default_rng() + def generate_data(n, generator_type): ϵ_values = [] for i in range(n): diff --git a/lectures/numpy.md b/lectures/numpy.md index 653ca0fc..624063de 100644 --- a/lectures/numpy.md +++ b/lectures/numpy.md @@ -1252,7 +1252,11 @@ class DiscreteRV: def __init__(self, q, seed=None): """ The argument q is a NumPy array, or array like, nonnegative and sums - to 1 + to 1. + + The argument seed sets the seed for the underlying random number + generator; with the default seed=None, draws are not reproducible + across runs. """ self.q = q self.Q = cumsum(q) @@ -1431,7 +1435,7 @@ print(A) **Part2**: Move on to replicate the result of the following broadcasting operation. Meanwhile, compare the speeds of broadcasting and the `for` loop you implement. -For this part of the exercise you can use the `tic`/`toc` functions from the `quantecon` library to time the execution. +For this part of the exercise you can use the `qe.Timer()` context manager from the `quantecon` library to time the execution. Let's make sure this library is installed. diff --git a/lectures/pandas.md b/lectures/pandas.md index 8218f44d..d61c3f74 100644 --- a/lectures/pandas.md +++ b/lectures/pandas.md @@ -89,7 +89,8 @@ Let's start with Series. We begin by creating a series of four random observations ```{code-cell} ipython3 -s = pd.Series(np.random.randn(4), name='daily returns') +rng = np.random.default_rng() +s = pd.Series(rng.standard_normal(4), name='daily returns') s ``` diff --git a/lectures/pandas_panel.md b/lectures/pandas_panel.md index 922f42a2..383220d3 100644 --- a/lectures/pandas_panel.md +++ b/lectures/pandas_panel.md @@ -77,7 +77,7 @@ countries and assign it to `realwage`. The dataset can be accessed with the following link: ```{code-cell} ipython3 -url1 = 'https://github.com/QuantEcon/data-lectures/raw/main/lectures/realwage.csv' +url1 = 'https://raw.githubusercontent.com/QuantEcon/data-lectures/main/lectures/realwage.csv' ``` ```{code-cell} ipython3 @@ -197,7 +197,7 @@ function. The dataset can be accessed with the following link: ```{code-cell} ipython3 -url2 = 'https://github.com/QuantEcon/data-lectures/raw/main/lectures/countries.csv' +url2 = 'https://raw.githubusercontent.com/QuantEcon/data-lectures/main/lectures/countries.csv' ``` ```{code-cell} ipython3 @@ -506,7 +506,7 @@ in Europe by age and sex from [Eurostat](https://ec.europa.eu/eurostat/data/data The dataset can be accessed with the following link: ```{code-cell} ipython3 -url3 = 'https://github.com/QuantEcon/data-lectures/raw/main/lectures/employ.csv' +url3 = 'https://raw.githubusercontent.com/QuantEcon/data-lectures/main/lectures/employ.csv' ``` Reading in the CSV file returns a panel dataset in long format. Use `.pivot_table()` to construct diff --git a/lectures/polars.md b/lectures/polars.md index 1285e065..1ab33a05 100644 --- a/lectures/polars.md +++ b/lectures/polars.md @@ -78,7 +78,8 @@ Let's start with Series. We begin by creating a series of four random observations ```{code-cell} ipython3 -s = pl.Series(name='daily returns', values=np.random.randn(4)) +rng = np.random.default_rng() +s = pl.Series(name='daily returns', values=rng.standard_normal(4)) s ``` @@ -114,7 +115,7 @@ For example, to associate ticker symbols with returns: ```{code-cell} ipython3 df = pl.DataFrame({ 'company': ['AMZN', 'AAPL', 'MSFT', 'GOOG'], - 'daily returns': np.random.randn(4) + 'daily returns': rng.standard_normal(4) }) df ``` @@ -463,13 +464,13 @@ a grouped weighted average. ```{code-cell} ipython3 n = 5_000_000 -np.random.seed(42) +rng = np.random.default_rng(42) -groups = np.random.choice(['A', 'B', 'C', 'D'], n) -values = np.random.randn(n) -weights = np.random.rand(n) -extra1 = np.random.randn(n) -extra2 = np.random.randn(n) +groups = rng.choice(['A', 'B', 'C', 'D'], n) +values = rng.standard_normal(n) +weights = rng.random(n) +extra1 = rng.standard_normal(n) +extra2 = rng.standard_normal(n) big_pd = pd.DataFrame({ 'group': groups, 'value': values, @@ -685,7 +686,7 @@ Calculate percentage changes using Polars expressions: ```{code-cell} ipython3 price_change = ticker.select([ - ((pl.col(tick).last() / pl.col(tick).first() - 1) * 100) + ((pl.col(tick).drop_nulls().last() / pl.col(tick).drop_nulls().first() - 1) * 100) .alias(tick) for tick in ticker_list.keys() ]).transpose( diff --git a/lectures/python_by_example.md b/lectures/python_by_example.md index f89c87f5..b3717dfa 100644 --- a/lectures/python_by_example.md +++ b/lectures/python_by_example.md @@ -173,19 +173,22 @@ Then it's harder for readers to know where `sqrt` came from, should they wish to ### Random Draws -Returning to our program that plots white noise, the remaining three lines +Returning to our program that plots white noise, the remaining four lines after the import statements are ```{code-cell} ipython +rng = np.random.default_rng() ϵ_values = rng.standard_normal(100) plt.plot(ϵ_values) plt.show() ``` -The first line generates 100 (quasi) independent standard normals and stores +The first line creates a random number generator `rng`. + +The second line generates 100 (quasi) independent standard normals and stores them in `ϵ_values`. -The next two lines genererate the plot. +The last two lines generate the plot. We can and will look at various ways to configure and improve this plot below. diff --git a/lectures/scipy.md b/lectures/scipy.md index fec66033..cf0c34c4 100644 --- a/lectures/scipy.md +++ b/lectures/scipy.md @@ -189,6 +189,7 @@ For example, `scipy.stats.linregress` implements simple linear regression ```{code-cell} python3 from scipy.stats import linregress +rng = np.random.default_rng() x = rng.standard_normal(200) y = 2 * x + 0.1 * rng.standard_normal(200) gradient, intercept, r_value, p_value, std_err = linregress(x, y) From b13f58b09bdf8ccd2875db4055a78008a40dcda1 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Wed, 5 Aug 2026 12:45:53 +1000 Subject: [PATCH 2/4] FIX: drop redundant rng re-creation in functions.md The three added `rng = np.random.default_rng()` lines re-bound a generator that is already created at the top of the "Random Draws" section and carried forward through notebook state, so they changed nothing at execution time while adding boilerplate to cells whose subject is function structure. Reverts functions.md to match main. Co-Authored-By: Claude Opus 5 (1M context) --- lectures/functions.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lectures/functions.md b/lectures/functions.md index 3de5f4b5..cdc88059 100644 --- a/lectures/functions.md +++ b/lectures/functions.md @@ -305,8 +305,6 @@ This is accomplished in the next program (funcloopprog)= ```{code-cell} python3 -rng = np.random.default_rng() - def generate_data(n): ϵ_values = [] for i in range(n): @@ -336,8 +334,6 @@ This is achieved in the next piece of code. (funcloopprog2)= ```{code-cell} python3 -rng = np.random.default_rng() - def generate_data(n, generator_type): ϵ_values = [] for i in range(n): @@ -370,8 +366,6 @@ To understand this, consider the following version. (test_program_6)= ```{code-cell} python3 -rng = np.random.default_rng() - def generate_data(n, generator_type): ϵ_values = [] for i in range(n): From 0b077ba1df9ed993adf628792aefa667ee38fc93 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Wed, 5 Aug 2026 12:55:57 +1000 Subject: [PATCH 3/4] FIX: tidy remaining rng bindings in scipy.md and polars.md scipy.md: drop the redundant rng re-creation before the linregress example, which re-bound a generator already created earlier in the lecture. Reverts scipy.md to match main. polars.md: rename the seeded benchmark generator to bench_rng so it no longer shadows the unseeded rng created in the Series section with one carrying different semantics. Co-Authored-By: Claude Opus 5 (1M context) --- lectures/polars.md | 12 ++++++------ lectures/scipy.md | 1 - 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/lectures/polars.md b/lectures/polars.md index 1ab33a05..a81f3733 100644 --- a/lectures/polars.md +++ b/lectures/polars.md @@ -464,13 +464,13 @@ a grouped weighted average. ```{code-cell} ipython3 n = 5_000_000 -rng = np.random.default_rng(42) +bench_rng = np.random.default_rng(42) -groups = rng.choice(['A', 'B', 'C', 'D'], n) -values = rng.standard_normal(n) -weights = rng.random(n) -extra1 = rng.standard_normal(n) -extra2 = rng.standard_normal(n) +groups = bench_rng.choice(['A', 'B', 'C', 'D'], n) +values = bench_rng.standard_normal(n) +weights = bench_rng.random(n) +extra1 = bench_rng.standard_normal(n) +extra2 = bench_rng.standard_normal(n) big_pd = pd.DataFrame({ 'group': groups, 'value': values, diff --git a/lectures/scipy.md b/lectures/scipy.md index cf0c34c4..fec66033 100644 --- a/lectures/scipy.md +++ b/lectures/scipy.md @@ -189,7 +189,6 @@ For example, `scipy.stats.linregress` implements simple linear regression ```{code-cell} python3 from scipy.stats import linregress -rng = np.random.default_rng() x = rng.standard_normal(200) y = 2 * x + 0.1 * rng.standard_normal(200) gradient, intercept, r_value, p_value, std_err = linregress(x, y) From 9af710cea15283af0841b9f4aae28ae228498a15 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Wed, 5 Aug 2026 13:00:51 +1000 Subject: [PATCH 4/4] Revert the bench_rng rename in polars.md Reusing the rng name for the seeded benchmark generator is fine: the rename was a readability preference with no effect on output, and nothing downstream of the benchmark cell reads rng. The seed is unchanged, so the benchmark data stays reproducible. Co-Authored-By: Claude Opus 5 (1M context) --- lectures/polars.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lectures/polars.md b/lectures/polars.md index a81f3733..1ab33a05 100644 --- a/lectures/polars.md +++ b/lectures/polars.md @@ -464,13 +464,13 @@ a grouped weighted average. ```{code-cell} ipython3 n = 5_000_000 -bench_rng = np.random.default_rng(42) +rng = np.random.default_rng(42) -groups = bench_rng.choice(['A', 'B', 'C', 'D'], n) -values = bench_rng.standard_normal(n) -weights = bench_rng.random(n) -extra1 = bench_rng.standard_normal(n) -extra2 = bench_rng.standard_normal(n) +groups = rng.choice(['A', 'B', 'C', 'D'], n) +values = rng.standard_normal(n) +weights = rng.random(n) +extra1 = rng.standard_normal(n) +extra2 = rng.standard_normal(n) big_pd = pd.DataFrame({ 'group': groups, 'value': values,