Skip to content

Comments

Adds denselines recipe#5461

Open
ConnectedSystems wants to merge 3 commits intoMakieOrg:masterfrom
ConnectedSystems:feat/densitylines
Open

Adds denselines recipe#5461
ConnectedSystems wants to merge 3 commits intoMakieOrg:masterfrom
ConnectedSystems:feat/densitylines

Conversation

@ConnectedSystems
Copy link
Contributor

@ConnectedSystems ConnectedSystems commented Dec 7, 2025

Description

Adds a new DenseLines recipe for visualizing time series data as a density line chart, based on the Moritz and Fisher method. It calculates and displays the density of overlapping lines, providing insights into data distribution over time.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Checklist

  • Added an entry in CHANGELOG.md (for new features and breaking changes)
  • Added or changed relevant sections in the documentation
  • Added unit tests for new algorithms, conversion methods, etc.
  • Added reference image tests for new plotting functions, recipes, visual options, etc.

@github-project-automation github-project-automation bot moved this to Work in progress in PR review Dec 7, 2025
@ConnectedSystems
Copy link
Contributor Author

ConnectedSystems commented Dec 7, 2025

Denselines implements the method of Moritz and Fisher (2018).
It plots, in essence, heatmaps of time series.

Closes #5427

@jkrumbiegel suggested that this be part of the Datashader recipe, however I opted to keep this separate at least for now.

Usage example:

using Dates
using Statistics

using WGLMakie

function example_data()
    n_times = 100
    n_series = 10000
    t = range(0, 2π, n_times)

    time_series = zeros(n_times, n_series)

    # First group: constant frequency sine wave
    for i in 1:div(n_series, 2)
        time_series[:, i] = 2.0 .+ sin.(t) .+ 0.1 .* randn(n_times)
    end

    # Second group: increasing frequency and amplitude
    for i in (div(n_series, 2)+1):n_series
        freq = range(1, 10, n_times)
        amp = range(0.5, 2, n_times)
        time_series[:, i] = 1.0 .+ amp .* sin.(freq .* t) .+ 0.1 .* randn(n_times)
    end

    return time_series
end

f, ax, sp = denselines(example_data(); bins_x=200, bins_y=100, colormap=:plasma)
ax.xlabel = "Time"
ax.ylabel = "Value"
image

Comment on lines 88 to 94
pixels = bresenham_line(x1, y1, x2, y2)

for (px, py) in pixels
if 1 <= px <= bins_x && 1 <= py <= bins_y
dense_mat[px, py] = 1.0
end
end
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't we also skip the pixels vector here and directly write to the matrix? That should save quite a bit of GC time I'd imagine.

Similarly, compute_normalized_density could probably clear and write to a matrix created once in denseline_data instead of creating a new one for every set of points

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think it might make sense to reuse the datashader infra for this if at all possible

Copy link
Contributor Author

@ConnectedSystems ConnectedSystems Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made changes for performance.

@jkrumbiegel , is a barrier function all you have in mind when you say "reuse the datashader infra"?

EDIT to add before and after:

Before: 1.797718 seconds (2.06 M allocations: 1.673 GiB, 29.31% gc time)

After: 0.377040 seconds (60.37 k allocations: 3.668 MiB)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Work in progress

Development

Successfully merging this pull request may close these issues.

3 participants