Generating Repeatable Noisy Data

Synthetic data lets us test a fitting method against a known underlying relation. This lesson builds paired measurements from a linear signal and seeded normal noise, then checks the properties that should remain true across valid runs.

The project needs measurements before it can fit a line. Rather than begin with an unknown real dataset, we will generate synthetic data: artificial data created from a process whose important properties are known.

Knowing the process gives us an answer against which the fitting method can be checked.

Separate the signal from the noise

For each input value xix_i, generate one measurement:

yi=mtruexi+εiy_i = m_{\text{true}}x_i + \varepsilon_i

Read this as: “y sub i equals the true slope times x sub i, plus noise epsilon sub i.”

The two parts have different roles:

  • mtruexim_{\text{true}}x_i is the signal, the systematic relation we want the fitting method to recover;
  • εi\varepsilon_i is noise, variation added to one measurement.

Suppose the true slope is 2.0, the input is x = 2.0, and the generated noise is -0.3. The clean signal and noisy measurement are:

clean signal = 2.0 * 2.0 = 4.0
measurement  = 4.0 + (-0.3) = 3.7

The measurement does not need to lie exactly on the line. The underlying relation is still present, but the program must estimate it from several imperfect measurements.

Use a simple line through the origin

This project uses:

y=mxy = mx

There is no intercept term. When x is zero, the clean value of y is also zero.

A more general line has the form y=mx+by = mx + b, where bb is the intercept. Learning both slope and intercept would require searching over two unknown values. Keeping the intercept fixed at zero lets this project concentrate on one complete fitting workflow before adding another parameter.

Choose input coordinates deliberately

Create evenly spaced input coordinates with np.linspace:

The first coordinate is 0.0, the last is 5.0, and config.point_count determines how many values appear between them. For point_count=6, the result is:

[0. 1. 2. 3. 4. 5.]

The coordinate range affects how visible a slope difference becomes. For example, slopes 2.0 and 2.1 differ by only 0.1 at x=1, but by 0.5 at x=5. The chosen range is therefore part of the data-generating process, not an arbitrary display detail.

Draw noise from a normal distribution

Create a NumPy generator from the configured seed:

rng = np.random.default_rng(config.seed)

Then draw one noise value for each input:

The normal distribution is centred at loc=0.0, so positive and negative noise balance around zero over many draws. The scale argument is the standard deviation. It controls the typical size of the variation.

The scale is not a strict maximum. With noise_scale=0.25, NumPy can generate a value whose magnitude is greater than 0.25; such values become less common as their distance from zero increases.

The noise array has the same shape as x. NumPy can therefore add one noise value to each clean signal value:

Make one run reproducible

A seed establishes the generator's initial state. Creating a new generator with the same seed and making the same draws in the same order reproduces the same noise:

This prints True.

Two consecutive draws from one generator are normally different because the first draw advances its state:

This normally prints False. Repeatability depends on the seed and the sequence of generator operations, not on the seed alone.

Implement the data-generating function

The function below validates the configuration, creates the input coordinates, draws noise, and returns paired arrays. Change the seed or noise scale and compare the clean and measured values.

Generate repeatable noisy measurements

The function creates a new generator from the configured seed. Calling it twice with the same configuration therefore reproduces the same x and y arrays.

Ready to run.

The expression y - clean_y recovers the noise that was added during data generation. We can inspect it here because synthetic data gives us both the clean signal and the noisy measurements.

Test guarantees rather than one printed array

The exact measurements depend on the seed and generator behavior. More durable checks target the contract of make_data:

For repeatability under the same environment and operation order:

Do not require the sample noise to have an average of exactly zero. A zero-centred distribution describes the generating process over many draws; one small sample can have a positive or negative average.

Synthetic data is a controlled test, not proof of realism

Synthetic data is useful because:

  • the true slope is known;
  • the amount and kind of noise are controlled;
  • mistaken shapes and formulas are easier to diagnose;
  • a run can be reproduced from its configuration.

It also has important limits. Success on these measurements shows that the fitting code works for this constructed process. It does not show that real measurements follow a line, have normally distributed noise, pass through the origin, or contain no other sources of error.

Exercise: Identify the non-random signal

In the equation yi=mtruexi+εiy_i = m_{\text{true}}x_i + \varepsilon_i, which expression is the non-random signal?

Choose the signal

Select one choice, then check.

HintFind the clean relation

The signal is the value that would remain if the generated noise were zero.

SolutionThe signal is the true line

The signal is mtruexim_{\text{true}}x_i. The term εi\varepsilon_i is the generated noise, and their sum is the observed measurement.

Exercise: Compute one noisy measurement

The true slope is 2.0, the input is x = 3.0, and the generated noise is -0.4. What measured y value is produced?

Answer it first, then check.

HintCompute the clean signal first

Calculate 2.0 * 3.0, then add -0.4.

SolutionAdd noise to the clean value

The clean value is 2.0 * 3.0 = 6.0. The measurement is 6.0 + (-0.4) = 5.6.

Exercise: Interpret the normal noise scale

What does noise_scale=0.25 mean in rng.normal(loc=0.0, scale=0.25, size=6)?

Choose the meaning of scale

Select one choice, then check.

HintDistinguish spread from bounds

A normal distribution is not limited to a closed interval.

SolutionScale controls the distribution's spread

scale=0.25 sets the standard deviation to 0.25. Individual values may have magnitude greater than 0.25, and a small sample need not have any exact predetermined average.

Exercise: Reason about two generator draws

What should you normally expect from this program?

Choose the expected relation

Select one choice, then check.

HintTrack generator state

The first call changes the generator's current state before the second call.

SolutionSuccessive draws use successive states

The arrays normally differ. The seed establishes the initial state, and each draw advances that state. Recreating a generator with the same seed would restart the sequence.

Exercise: Complete repeatable data generation

Complete make_data so it creates four evenly spaced inputs from 0.0 through 3.0, draws one normal noise value per input, and returns paired x and y arrays. The program must print:

shape: (4,)
paired: True
repeatable: True

Generate paired data from a seeded process

Ready to run.

HintUse one noise value for each x value

Create x with np.linspace(0.0, 3.0, num=4). Set the noise draw's size to x.size.

SolutionBuild the signal and add seeded noise

np.linspace creates the four coordinates. The generator draws a noise array with the same number of values, and elementwise addition produces the paired measurements. Recreating the generator inside the function makes a repeated call with the same arguments reproduce the arrays.

Preserve the data-generating process

A synthetic dataset is identified by more than the returned arrays. Preserve:

coordinate rule
true slope
intercept assumption
noise distribution
noise scale
seed
number and order of generator draws

Together, these choices explain where the measurements came from and make the controlled test reproducible. The fitting stage can now use x and y without knowing the true slope directly.