Search code examples
pythongradientfillcontourf

Gradient 2D plot using contourf


I did a test code brigging something I saw on stack on different topic, and try to assemble it to make what I need : a filled curve with gradient.

After validate this test code I will make a subplot (4 plots for 4 weeks) with the same min/max for all plot (it's a power consumption).

My code :

from matplotlib import pyplot as plt
import numpy as np

# random x
x = range(100)

# smooth random y
y = 0
result = []
for _ in x:
    result.append(y)
    y += np.random.normal(loc=0, scale=1)#, size=len(x))
y = result    
y = list(map(abs, y))


# creation of z for contour
z1 = min(y)
z3 = max(y)/(len(x)+1)
z2 = max(y)-z3
z = [[z] * len(x) for z in np.arange(z1,z2,z3)]


num_bars = len(x)  # more bars = smoother gradient

# plt.contourf(x, y, z, num_bars, cmap='greys')
plt.contourf(x, y, z, num_bars, cmap='cool', levels=101)

background_color = 'w'
plt.fill_between(
    x, 
    y, 
    y2=max(y), 
    color=background_color
    )

But everytime I make the code run, the result display a different gradient scale, that is not smooth neither even straight right. AND sometime the code is in error : TypeError: Length of y (100) must match number of rows in z (101)

enter image description here enter image description here

I'm on it since too many time, turning around, and can't figure where I'm wrong...


Solution

  • I finally find something particularly cool, how to :

    • have both filled gradient curves in a different color (thanks to JohanC in this topic)
    • use x axis with datetime (thanks to Ffisegydd in this topic)

    Here the code :

    import numpy as np
    import matplotlib.pyplot as plt
    import pandas as pd
    import matplotlib.dates as mdates
    
    
    
    
    np.random.seed(2022)
    
    st_date = '2022-11-01 00:00:00'
    st_date = pd.to_datetime(st_date)
    en_date = st_date + pd.DateOffset(days=7)
    x = pd.date_range(start=st_date,end=en_date,freq='30min')
    x = mdates.date2num(x)
    
    y = np.random.normal(0.01, 1, len(x)).cumsum()
    
    fig, ax = plt.subplots(figsize=(18, 5))
    ax.plot(x, y, color='grey')
    
    
    ########################
    # positives fill
    #######################
    
    grad1 = ax.imshow(
        np.linspace(0, 1, 256).reshape(-1, 1),
        cmap='Blues',
        vmin=-0.5,
        aspect='auto',
        extent=[x.min(), x.max(), 0, y.max()],
        # extent=[x[0], x[1], 0, y.max()],
        origin='lower'
        )
    
    poly_pos = ax.fill_between(x, y.min(), y, alpha=0.1)
    
    grad1.set_clip_path(
        poly_pos.get_paths()[0],
        transform=ax.transData
        )
    
    poly_pos.remove()
    
    ########################
    # negatives fill
    #######################
    
    grad2 = ax.imshow(
        np.linspace(0, 1, 256).reshape(-1, 1),
        cmap='Reds',
        vmin=-0.5,
        aspect='auto',
        extent=[x.min(), x.max(), y.min(), 0],
        origin='upper'
        )
    
    poly_neg = ax.fill_between(x, y, y.max(), alpha=0.1)
    
    grad2.set_clip_path(
        poly_neg.get_paths()[0],
        transform=ax.transData
        )
    
    poly_neg.remove()
    
    
    ########################
    # decorations and formatting plot
    ########################
    
    ax.xaxis_date()
    date_format = mdates.DateFormatter('%d-%b %H:%M')
    ax.xaxis.set_major_formatter(date_format)
    fig.autofmt_xdate()
    ax.grid(True)
    

    enter image description here