How to create a colour gradient in each graph bar python?

156 Views Asked by At

This is the current output of my code at the moment.

I have this the code:

azul das barras

rb=0
gb=80
bb=141
color_bar=rgb_to_hex(rb, gb, bb)

laranja da linha

rl=237
gl=125
bl=49

color_line=rgb_to_hex(rl, gl, bl)

cor branco

rbra=255
gbra=255
bbra=255

color_text_table=rgb_to_hex(rbra,gbra,bbra)

#Tamanho da letra e espessura linha fontsize=18 linewidth=5

#configuração tamanho da imagem plt.rcParams['figure.figsize']=(20,10)

#Alterar cor da frame do gráfico ou outros parametros plt.rc('axes',edgecolor=color_bar, lw=linewidth-2.5)

#Criar Gráfico de Barras

width_bar=0.35
lim_sup=grouped\['OID_MEM_ID'\].max()+(grouped\['OID_MEM_ID'\].max()-grouped\['OID_MEM_ID'\].min())\*2
ax = grouped.plot('Mês Ano','OID_MEM_ID',
color=color_bar,
kind ='bar',
fontsize=fontsize+2,
legend=False,
width=width_bar,
ylim=(0,lim_sup))

#Retirar valores y do gráfico ax.axes.yaxis.set_ticklabels([])

I expect this output

What can I do to improve my solution?

1

There are 1 best solutions below

0
Ori Yarden PhD On BEST ANSWER

We can use a combination of matplotlib's mcolors, Polygon, and Bbox to put together a gradient bar plot:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from matplotlib.patches import Polygon
from matplotlib.transforms import Bbox

y = np.random.random(4)*0.01
fill_color = [0, 0.25, 0.75]
box_width = 0.35
box_floor = 0
box_alpha = 1.0
ax = plt.subplot(1, 1, 1)
rgb = mcolors.colorConverter.to_rgb(fill_color)
box_color = np.zeros((100, 1, 4), dtype=float)
box_color[:, :, :3] = rgb
box_color[:, :, -1] = np.linspace(0, box_alpha, 100)[:, None]
for _x in range(len(y)):
    im = ax.imshow(box_color, aspect='auto', extent=[_x - box_width + 1, _x + box_width + 1, box_floor, y[_x]], origin='lower')
    xy = np.vstack([[_x - box_width + 1, box_floor], Bbox.from_bounds(_x - box_width + 1, _x + box_width + 1, box_floor, y[_x]), [_x + box_width + 1, box_floor], [_x - box_width + 1, box_floor]])
    clip_path = Polygon(xy, facecolor='none', edgecolor='none', closed=True)
    ax.add_patch(clip_path)
    im.set_clip_path(clip_path)
ax.plot(np.arange(1, len(y) + 1, 1), y[::-1], ls='-', lw=1.5, color=[1, 0.5, 0])
ax.set_ylim(0, y.max() + (0.1*y.max()))
ax.set_xlim(-box_width, len(y) + box_width + 1)
plt.show()

Outputs:

enter image description here

However, it doesn't scale out well beyond the tenths place (0.1) so for data with larger numbers we'd have to use something like rescale_y and also adjust the yticklabels:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from matplotlib.patches import Polygon
from matplotlib.transforms import Bbox

_y = np.random.random(4)*1000
def rescale_y(_y, _exp=0):
    while max(_y) > 0.1:
        _exp += 1
        _y*=0.1
    return _y, _exp

y, _exp = rescale_y(_y)
fill_color = [0, 0.25, 0.75]
box_width = 0.35
box_floor = 0
box_alpha = 1.0
ax = plt.subplot(1, 1, 1)
rgb = mcolors.colorConverter.to_rgb(fill_color)
box_color = np.zeros((100, 1, 4), dtype=float)
box_color[:, :, :3] = rgb
box_color[:, :, -1] = np.linspace(0, box_alpha, 100)[:, None]
for _x in range(len(y)):
    im = ax.imshow(box_color, aspect='auto', extent=[_x - box_width + 1, _x + box_width + 1, box_floor, y[_x]], origin='lower')
    xy = np.vstack([[_x - box_width + 1, box_floor], Bbox.from_bounds(_x - box_width + 1, _x + box_width + 1, box_floor, y[_x]), [_x + box_width + 1, box_floor], [_x - box_width + 1, box_floor]])
    clip_path = Polygon(xy, facecolor='none', edgecolor='none', closed=True)
    ax.add_patch(clip_path)
    im.set_clip_path(clip_path)
ax.plot(np.arange(1, len(y) + 1, 1), y[::-1], ls='-', lw=1.5, color=[1, 0.5, 0])
ax.set_ylim(0, y.max() + (0.1*y.max()))
if _exp:
    ax.set_yticklabels([str(round(_tick*(10**(_exp)))) for _tick in ax.get_yticks()])
ax.set_xlim(-box_width, len(y) + box_width + 1)
plt.show()

Outputs:

enter image description here

Otherwise...

enter image description here

If we want different fill_colors for each bar in our plot:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from matplotlib.patches import Polygon
from matplotlib.transforms import Bbox

_y = np.random.random(4)*1000
def rescale_y(_y, _exp=0):
    while max(_y) > 0.1:
        _exp += 1
        _y*=0.1
    return _y, _exp

y, _exp = rescale_y(_y)
fill_colors = [[0, 0.25, 0.75], [1, 0, 0], [0, 1, 0], [1, 1, 0]]
box_width = 0.35
box_floor = 0
box_alpha = 1.0
ax = plt.subplot(1, 1, 1)
for _x in range(len(y)):
    rgb = mcolors.colorConverter.to_rgb(fill_colors[_x])
    box_color = np.zeros((100, 1, 4), dtype=float)
    box_color[:, :, :3] = rgb
    box_color[:, :, -1] = np.linspace(0, box_alpha, 100)[:, None]
    im = ax.imshow(box_color, aspect='auto', extent=[_x - box_width + 1, _x + box_width + 1, box_floor, y[_x]], origin='lower')
    xy = np.vstack([[_x - box_width + 1, box_floor], Bbox.from_bounds(_x - box_width + 1, _x + box_width + 1, box_floor, y[_x]), [_x + box_width + 1, box_floor], [_x - box_width + 1, box_floor]])
    clip_path = Polygon(xy, facecolor='none', edgecolor='none', closed=True)
    ax.add_patch(clip_path)
    im.set_clip_path(clip_path)
ax.plot(np.arange(1, len(y) + 1, 1), y[::-1], ls='-', lw=1.5, color=[1, 0.5, 0])
ax.set_ylim(0, y.max() + (0.1*y.max()))
if _exp:
    ax.set_yticklabels([str(round(_tick*(10**(_exp)))) for _tick in ax.get_yticks()])
ax.set_xlim(-box_width, len(y) + box_width + 1)
plt.show()

Outputs:

enter image description here