Skip to content

Linear Regression proof of concept #123

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

scrouthtv
Copy link

Hi!

Thanks for all your work on the cetz and related project, I love it!

For my past work, I have been experimenting with linear regression plotting.
Herein, I want to propose to you a proof of concept:

image

The x marks plot a simulated measurement error (see utility function in my example).
The dashed trend line is automatically fitted by cetz-plot for least square error.

#import "@preview/cetz:0.3.4"
#import "@local/cetz-plot:0.1.2": plot

// utility function to simulate a measurement error
// f [function float => float]
// samples [array of float]
// error [float]: default 1.0
// seed [int]: default 42
#let measor(f, samples, error: 1.0, seed: 42) = {
	import "@preview/suiji:0.3.0": gen-rng-f, uniform-f

	let (_, errors) = uniform-f(gen-rng-f(seed), high: error, size: samples.len())
	let out = (0,) * samples.len()

	for i in range(samples.len()) {
		let x = samples.at(i)
		let y = f(x) + errors.at(i) - error / 2.0
		out.at(i) = (x, y)
	}

	return out
}

// utility function to range on float values
#let rangef(start, end, step: 1.0) = range(int(start / step), int(end / step) + 1).map(x => x * step)

// =================================================================================

// For our examples, we have the "true" model and a measurement error.
// To these mesaurements, a model is fitted:
#let examples = (
	(f: x => 8 * x, error: 10, proto: "linear", color: blue),
	(f: x => 2 * calc.pow(x * 2, 2) - 50 * x - 2, error: 50, proto: x => (1, x, calc.pow(x, 2)), color: red),
	(f: x => 0.2 * calc.exp(x), error: 1000, proto: x => (1, x, calc.exp(x)), color: green),
	(f: x => 0, error: 10, proto: x => (1, calc.sin(x), calc.cos(x)), color: fuchsia)
)

#let samples = rangef(1, 10.1, step: 0.5)

#for ex in examples {
figure(cetz.canvas({
plot.plot(size: (15, 4), {
	// Simulate measurement samples:
	let data = measor(ex.f, samples, error: ex.error)
	// Plot measurement samples:
	plot.add(data, mark: "x", style: (stroke: none), mark-style: (stroke: ex.color))
	// Fit and plot trend line:
	plot.add-trend(data, ex.proto, style: (stroke: (paint: ex.color, dash: "dashed")))
})
}))
}

(The fourth example is simply random data where I attempt to fit a sine function.)

With this PR you find the code that I used for this proof of concept.

The main implementation challenge is that this needs some matrix operations (transpose, inverse, multiply). I don't think they are builtin to typst and neither is there a package for these.
Therefore, I have added a very basic implementation using the Gaussian algorithm.
In a real application, I think this would rather be done using wasm, as this implementation already exceeds 1 sec of compilation time on my machine...

With these matrix operations available, the rest is pretty straight-forward.

Let me know whether you would be interested in this feature for cetz-plot and I will continue working on this code, write docs and tests etc.!

@johannes-wolf johannes-wolf self-requested a review April 14, 2025 14:34
@johannes-wolf
Copy link
Member

Let me know whether you would be interested in this feature for cetz-plot and I will continue working on this code, write docs and tests etc.!

Sorry for answering that late, did not notice the PR until now.

Yes, I am interested in that feature!

I started a huge refactoring for cetz-plot that fixes some long-standing problems and allows features like shared axes etc. Problem is, that I do not really have time to complete that PR(s) and/or work on cetz-plot in general. And since there is an alternative, lilaq, motivation is pretty low. So I do not really know… merging features into the current state? Waiting for the refactoring?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants