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

Log support #207

Merged
merged 13 commits into from
May 14, 2023
Merged

Log support #207

merged 13 commits into from
May 14, 2023

Conversation

jonasvdd
Copy link
Member

@jonasvdd jonasvdd commented May 7, 2023

Partially addresses #206
Closes #190

Specifically, log-axis their zoom functionality will be supported.

TODO:

  • tests for log axis and update sizes

@jonasvdd
Copy link
Member Author

jonasvdd commented May 7, 2023

Code ⬇️ implementation which projects LTTB its buckets to equidistant log buckets.

import numpy as np
from plotly_resampler.aggregation.aggregation_interface import DataPointSelector
from typing import Union


class LogLTTB(DataPointSelector):
    @staticmethod
    def _argmax_area(prev_x, prev_y, avg_next_x, avg_next_y, x_bucket, y_bucket) -> int:
        """Vectorized triangular area argmax computation.

        Parameters
        ----------
        prev_x : float
            The previous selected point is x value.
        prev_y : float
            The previous selected point its y value.
        avg_next_x : float
            The x mean of the next bucket
        avg_next_y : float
            The y mean of the next bucket
        x_bucket : np.ndarray
            All x values in the bucket
        y_bucket : np.ndarray
            All y values in the bucket

        Returns
        -------
        int
            The index of the point with the largest triangular area.
        """
        return np.abs(
            x_bucket * (prev_y - avg_next_y)
            + y_bucket * (avg_next_x - prev_x)
            + (prev_x * avg_next_y - avg_next_x * prev_y)
        ).argmax()

    def _arg_downsample(
        self, x: Union[np.ndarray, None], y: np.ndarray, n_out: int, **kwargs
    ) -> np.ndarray:
        """TODO complete docs"""
        # We need a valid x array to determing the x-range
        assert x is not None, "x cannot be None for this downsampler"

        # the log function to use
        lf = np.log1p

        offset = np.unique(
            np.searchsorted(
                x, np.exp(np.linspace(lf(x[0]), lf(x[-1]), n_out + 1)).astype(np.int64)
            )
        )

        # Construct the output array
        sampled_x = np.empty(len(offset) + 1, dtype="int64")
        sampled_x[0] = 0
        sampled_x[-1] = x.shape[0] - 1

        # Convert x & y to int if it is boolean
        if x.dtype == np.bool_:
            x = x.astype(np.int8)
        if y.dtype == np.bool_:
            y = y.astype(np.int8)

        a = 0
        for i in range(len(offset) - 2):
            a = (
                self._argmax_area(
                    prev_x=x[a],
                    prev_y=y[a],
                    avg_next_x=np.mean(x[offset[i + 1] : offset[i + 2]]),
                    avg_next_y=y[offset[i + 1] : offset[i + 2]].mean(),
                    x_bucket=x[offset[i] : offset[i + 1]],
                    y_bucket=y[offset[i] : offset[i + 1]],
                )
                + offset[i]
            )
            sampled_x[i + 1] = a

        # ------------ EDGE CASE ------------
        # next-average of last bucket = last point
        sampled_x[-2] = (
            self._argmax_area(
                prev_x=x[a],
                prev_y=y[a],
                avg_next_x=x[-1],  # last point
                avg_next_y=y[-1],
                x_bucket=x[offset[-2] : offset[-1]],
                y_bucket=y[offset[-2] : offset[-1]],
            )
            + offset[-2]
        )
        return sampled_x

Code ⬇️ example which creates a log plot:

from plotly_resampler.aggregation import NoGapHandler

n = 100_000
y = np.sin(np.arange(n) / 2_000) + np.random.randn(n) / 10

fr = FigureResampler(False)
fr.add_trace(
    go.Scattergl(mode="lines+markers", marker_color=np.abs(y) / np.max(np.abs(y))),
    hf_x=np.arange(n),
    hf_y=y,
    downsampler=LogLTTB(),
    gap_handler=NoGapHandler(),
)
fr.update_xaxes(type="log")
fr.update_layout(template='plotly_white', title='log axis demo')

image

@jonasvdd jonasvdd mentioned this pull request May 7, 2023
@codecov-commenter
Copy link

codecov-commenter commented May 7, 2023

Codecov Report

Merging #207 (68ab1a9) into main (a14331e) will increase coverage by 0.01%.
The diff coverage is 100.00%.

❗ Your organization is not using the GitHub App Integration. As a result you may experience degraded service beginning May 15th. Please install the Github App Integration for your organization. Read more.

@@            Coverage Diff             @@
##             main     #207      +/-   ##
==========================================
+ Coverage   97.15%   97.16%   +0.01%     
==========================================
  Files          13       13              
  Lines         985      989       +4     
==========================================
+ Hits          957      961       +4     
  Misses         28       28              
Impacted Files Coverage Δ
..._resampler/aggregation/plotly_aggregator_parser.py 94.56% <100.00%> (+0.12%) ⬆️
...ler/figure_resampler/figure_resampler_interface.py 100.00% <100.00%> (ø)

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

jonasvdd and others added 5 commits May 7, 2023 19:53
* 💪 add tests for #208

* 🙏 fix for #208
* 💪 add tests for #208

* 🙏 fix for #208

* ✨ tests for #210

* 💪 code-fix for #210

* 🔧 tests for setting hf_x dynamically for #210

* 🔥 fix for setting hf_series x to a tz-aware pd.Series

* 🖊️ review

* 🔍 review code

* 🔍 fix helper method

* 🙏

---------

Co-authored-by: jvdd <boebievdd@gmail.com>
@jvdd
Copy link
Member

jvdd commented May 14, 2023

LGTM!

@jonasvdd jonasvdd merged commit 4157229 into main May 14, 2023
16 checks passed
@jvdd jvdd deleted the log_support branch June 18, 2023 09:22
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.

Add data-vis repo link in readme / docs
4 participants