brunorosilva / plotly-calplot

The easiest and best looking Calendar Heatmap you'll find, made with Plotly.
https://pypi.org/project/plotly-calplot/
104 stars 10 forks source link

option to plot all months/years in a single row #15

Closed FrancescSunol closed 1 year ago

FrancescSunol commented 1 year ago

It would be great to have an option to plot all months (even if they are form different years) in a single row, or maybe is currently possible to do it?

brunorosilva commented 1 year ago

Hi, @FrancescSunol

This is currently not supported natively. I'll give it some thought.

You mean something like this, right? image

FrancescSunol commented 1 year ago

yes exactly like this

brunorosilva commented 1 year ago

This is currently impossible :( I'm trying to create an easy-to-use solution, but no luck so far. I'll reach out when I figure it out.

FrancescSunol commented 1 year ago

ok many thanks!

brunorosilva commented 1 year ago

Hi, @FrancescSunol

I figured out a dumb way to do this. Plotly should have a better implementation of this in their utils package.

If this is a recurrent problem I'll update the pkg to support it natively or even write to Plotly myself.

import copy
from plotly.subplots import make_subplots

def transform_calplot_to_columns(calplot):
    # we'll be updating the figure, so lets just copy it so we won't alter the original one
    single_row_calplot = copy.deepcopy(calplot)

    # getting the amount of years in the plot
    years_amount = len(
        [
            layout_key
            for layout_key in single_row_calplot.to_dict()["layout"].keys()
            if layout_key.startswith("xaxis")
        ]
    )

    # we'll use this as our template to find the correct distances between subplots
    dummy_subplots = make_subplots(rows=1, cols=years_amount)
    for dummy_layout_key in dummy_subplots.to_dict()["layout"]:
        if dummy_layout_key.startswith("xaxis"):
            # we're updating our xaxis domain to match a column subplot
            single_row_calplot["layout"][dummy_layout_key].update(
                domain=list(dummy_subplots["layout"][dummy_layout_key]["domain"])
            )
        if dummy_layout_key.startswith("yaxis"):
            # we're updating our yaxis domain to match a column subplot
            single_row_calplot["layout"][dummy_layout_key].update(domain=[0.0, 1.0])

    # we also have to fix the year titles, since those differ when we go from rows to columns
    for i, ann in enumerate(single_row_calplot["layout"]["annotations"]):
        # getting the offset with the first subplot
        mid = (
            single_row_calplot["layout"]["xaxis"]["domain"][1]
            - single_row_calplot["layout"]["xaxis"]["domain"][0]
        ) / 2
        # now we update each year title axis.
        # plotly names the first axis as xaxis, the second one as xaxis2 and so on,
        # this is ugly code, but we'll make it do. They should really change the first
        # one to xaxis1 or start at 0.
        if i != 0:
            ann.update(
                y=1.0, x=single_row_calplot["layout"][f"xaxis{i+1}"]["domain"][0] + mid
            )
        if i == 0:
            ann.update(
                y=1.0, x=single_row_calplot["layout"]["xaxis"]["domain"][0] + mid
            )
    return single_row_calplot

Example

image

This looks kinda silly

image

I recommend you to increase the plot width before the treatment

image image

brunorosilva commented 1 year ago

Hey @FrancescSunol did this solution solve your problem? I'll be releasing a prettier solution in the start of the year like a parameter or a different function as a whole (something like single_row_calplot(df)).

FrancescSunol commented 1 year ago

Hi @brunorosilva sorry for the late reply, for the moment I left that one-line calplot aside, but I will try your new solution for sure!

brunorosilva commented 1 year ago

@FrancescSunol Hi, just updating that this will be available as a native option in the next release.

fig1 = calplot(
    dummy_df,
    x="ds",
    y="value",
    years_as_columns=True
)

image

Thanks so much for the idea.

FrancescSunol commented 1 year ago

Great!! many thanks @brunorosilva !