Skip to content
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

Basic plotting support #464

Merged
merged 25 commits into from
Sep 13, 2024
Merged

Basic plotting support #464

merged 25 commits into from
Sep 13, 2024

Conversation

sharkdp
Copy link
Owner

@sharkdp sharkdp commented Jun 11, 2024

This is a draft for adding basic plotting support to Numbat. Contrary to the first attempt in #147, we now implement the basic data preprocessing step in Numbat itself, using the new struct and List data types. We can now also implement this as a normal function, instead of having to introduce a new command.

We can now write something like:

let A0 = 3 cm
let ω = 20 Hz
let λ = 1.5 s

fn amplitude(t) = A0 exp(-t/λ) cos(ω t)

line_plot(amplitude, 0 s, 8 s) |>
  xlabel("Time") |>
  ylabel("Amplitude") |>
  show

and get the following plot. The xlabel/ylabel calls are optional. Note how the units here are inferred and added to the axis descriptions:

image

Similarly, we can create a simple bar chart:

let names = ["Iron", "Copper", "Silver", "Gold", "Platinum"]
let elements = map(element, names)

fn density(e: ChemicalElement) -> MassDensity = e.density

let densities = map(density, elements)

bar_chart(densities) |>
  xlabels(names) |>
  value_label("Density") |>
  show

image

Open points:

  • This would be much more powerful if we had Generic structs #452. This would allow us to rewrite line_plot(amplitude, 0 s, 8 s) into line_plot(amplitude) |> xlim(0 s, 8 s), for example. It would also allow us to implement ylim(…, …). We could also store the function inside the LinePlot struct instead of storing all the data points. We would only convert to data points before passing it to the FFI
  • It would be nice to be able to move the |> reverse-apply operators in the example above to the next line. To have it look like
    line_plot(amplitude, 0 s, 8 s)
      |> xlabel("Time")
      |> ylabel("Amplitude")
      |> show
    

@eminence
Copy link
Contributor

For CLI usage, I wonder if we could hook it up to ratatui's canvas or chart widgets. This probably wouldn't be all that useful in practice, but might be fun anyway :)

@sharkdp
Copy link
Owner Author

sharkdp commented Jun 15, 2024

For CLI usage, I wonder if we could hook it up to ratatui's canvas or chart widgets. This probably wouldn't be all that useful in practice, but might be fun anyway :)

Cool. Hadn't heard about ratatui. But yeah, I think a file-based backend and maybe an actual GUI backend might be more useful options.

@sharkdp
Copy link
Owner Author

sharkdp commented Jul 1, 2024

This changeset now uses plotly, which allows us to open interactive plots in the browser. This is also much lighter in terms of dependencies (but see plotly/plotly.rs#176). And it would also integrate with the web version naturally.

image

@sharkdp sharkdp mentioned this pull request Aug 5, 2024
@sharkdp
Copy link
Owner Author

sharkdp commented Aug 5, 2024

@irevoire I had a new idea for the API. It's a bit hacky, but could be enough for a first draft. I'm now using your enhanced reverse-apply operator to add optional arguments like xlabel and ylabel. Please see the updated PR description for details, and let me know what you think, if you're interested.

@irevoire
Copy link
Contributor

irevoire commented Aug 5, 2024

Oh yes we can now do a builder pattern quite easily!
IMO, that's nicer than having a « Default » implementation for the structure 🤔

On another subject, I’m really starting to think that we may need scopes earlier than I expected:

Plot.line_plot(amplitude, 0 s, 8 s)
  |> Plot.xlabel("Time")
  |> Plot.ylabel("Amplitude")
  |> Plot.show

Something like that seems easier to understand, and it will free up a lot of symbols for the users.

@sharkdp
Copy link
Owner Author

sharkdp commented Aug 8, 2024

On another subject, I’m really starting to think that we may need scopes earlier than I expected:

Yes, absolutely. It's a problem already since I can't reuse things like ylabel for other plot types. I added a slightly hacky solution for now in order to be able to have just one show function for both line plots and bar plots. The latter of which are now also supported (see PR description).

@sharkdp sharkdp linked an issue Aug 8, 2024 that may be closed by this pull request
@sharkdp sharkdp changed the title Very basic plotting support Basic plotting support Aug 8, 2024
@sharkdp sharkdp marked this pull request as ready for review August 8, 2024 17:57
@sharkdp
Copy link
Owner Author

sharkdp commented Aug 9, 2024

This is now in a state where it could be merged. A drawback is that the binary size increases by about 4 MiB, which is probably mostly due to plotly/plotly.rs#176

@sharkdp sharkdp merged commit b175c31 into master Sep 13, 2024
15 checks passed
@sharkdp sharkdp deleted the plotting branch September 13, 2024 20:18
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.

Plot functionality
3 participants