e-hulten / july

A small library for creating pretty heatmaps of daily data.
MIT License
209 stars 26 forks source link

Sliding calendar #21

Closed Bouni closed 2 years ago

Bouni commented 2 years ago

Hi, is it possible to get a sliding calendar like the on I see on my github profile?

It looks like the ones that come out of july always start at January and end in December while the github calendar starts at today - 356 days and today is the last square in the calendar.

e-hulten commented 2 years ago

Hi, sorry for the late response. Your issue completely slipped under my radar.

When using the heatmap function, the plot starts and and ends on the dates passed as the dates argument. That means the plot is not necessarily showing data for January through December, as shown in the plots below:

import numpy as np
import matplotlib.pyplot as plt
import july
from july.utils import date_range

# Create and plot data with more than 365 days.
dates_long = date_range("2020-01-01", "2021-12-31")
data_long = np.random.randint(0, 14, len(dates_long))
print(f"This plot shows {len(dates_long)} days:")
july.heatmap(dates_long, data_long, title='Github Activity', cmap="github", fontsize=8)
plt.show()

# Create and plot data with less than 365 days.
dates_short = date_range("2021-04-01", "2021-12-31")
data_short = np.random.randint(0, 14, len(dates_short))
print(f"This plot shows {len(dates_short)} days:")
july.heatmap(dates_short, data_short, title='Github Activity', cmap="github")
plt.show()

image

To get a "GitHub style" plot with a sliding calendar, we simply need to pass 365 days to the heatmap function. If you have more than 365 data points, that means passing the 365 last ones. If you have less than 365 data points, that means zero padding the data until you have 365 days. There is no function doing this for you in july, but here is a quick example of what such a function could look like for your reference:

from dateutil.relativedelta import relativedelta

def plot_365_days(dates, data):
    if len(dates) < 365:
        # If original data is less than 365 days, pad with zeros to get a full year of data.
        first_date = dates[-1] + relativedelta(days=-364)
        # Full year of dates.
        dates = date_range(first_date, dates[-1])
        # Array of zeros, same length as `dates`.
        padded_data = np.zeros_like(dates)
        # Fill in original data.
        padded_data[-len(data):] = data
        data = padded_data

    july.heatmap(dates[-365:], data[-365:], title='Github Activity', cmap="github")

plot_365_days(dates_long, data_long)  # More than 365 data points.
plot_365_days(dates_short, data_short)  # Less than 365 data points. 

image

I'll strongly consider adding a proper version of this function to the next release, thanks for giving me the idea!

Bouni commented 2 years ago

No problem, thanks for the detailed explanation!