'How to make Matplotlib table cell colors work with invisible cell edges?

I am trying to create a table with matplotlib that has some columns that should be visually joined by not sharing borders, and also be colored. Now I can do one or the other, but I can't seem to do both as the setting border edges invisible screws up the colors.

The way it should look with the colors: Colors correct

The way it should look with the edges: Edges correct

The way it now looks when I combine both: Edges and colors appliedThis is my example code producing the borked result. You can create the correct colors by commenting out the for loop above plt.show(). If I set all edges to BTLR (= full edges) the colors are fine again.

It would also work for me if I could set the lines from the first image as bolder than the others, but I did not find a way to do that either.

Any help would be appreciated. Seems like a bug in matplotlib to me? Is there a workaround? Maybe I need to do it in plotly or some other tool, but I have never used plotly before and there seems to be a lot less uncomplicated help around for it as compared to matplotlib...

import math
import matplotlib
import matplotlib.pyplot as plt
matplotlib.use('TkAgg')

dpi_value = 100
picture_size_x = 1200
picture_size_y = 600

fig, axs = plt.subplots(figsize=(picture_size_x / dpi_value, picture_size_y / dpi_value), dpi=dpi_value)
axs.axis('tight')
axs.axis('off')
data = [[  66386,  174296,   75131,  577908,   32015, 49682, 76314],
        [  58230,  381139,   78045,   99308,  160454, 74674, 64311],
        [  89135,   80552,  152558,  497981,  603535, 75312, 25687],
        [  78415,   81858,  150656,  193263,   69638, 14566, 48753],
        [ 139361,  331509,  343164,  781380,   52269, 54378, 64745]]


cellcolours_array = [['green', 'green', 'green', 'orange', 'orange', 'orange', 'cyan'],
        ['green', 'green', 'green', 'orange', 'orange', 'orange', 'cyan'],
        ['green', 'green', 'green', 'orange', 'orange', 'orange', 'cyan'],
        ['green', 'green', 'green', 'orange', 'orange', 'orange', 'cyan'],
        ['green', 'green', 'green', 'orange', 'orange', 'orange', 'cyan']]

columns = ('Freeze', 'Wind', 'Fire', 'Flood', 'Quake', 'Drought', 'Hail')
rows = ['%d year' % x for x in (100, 50, 20, 10, 5)]

the_table = plt.table(cellText=data,
                      cellColours=cellcolours_array,
                      rowLabels=rows,
                      colLabels=columns,
                      cellLoc='center',
                      bbox=[0.0, 0.0, 1.0, 1.0]
                      )

# this here doesnt work right
for row_idx in range(len(data)+1):
    for col_idx in range(len(data[0])):
        edges = 'horizontal'
        if col_idx == 0:
            edges = 'BTL'
        if (col_idx+1) % 3 == 0:
            edges = 'BTR'
            ccolor = 'silver'
        if (col_idx+1) == len(data[0]):
            edges = 'BTR'
        the_table[row_idx, col_idx].visible_edges = edges
plt.show()

I tried experimenting with color, facecolor, fill and other things to no avail. I know I can set linewidth to 0 globally, but that won't help me as I need some lines...



Solution 1:[1]

After some deep thinking, I came up with a bandaid solution that works:

I am now creating two tables, one with the edges visible like I want and transparent cells, and one with the colored cells and no edges. Both with the same bbox, the edges-table on top with the higher zorder value. Et voila, I get the desired result:

all correct now

Here is the code:

import math
import matplotlib
import matplotlib.pyplot as plt
matplotlib.use('TkAgg')

dpi_value = 100
picture_size_x = 1200
picture_size_y = 600

fig, axs = plt.subplots(figsize=(picture_size_x / dpi_value, picture_size_y / dpi_value), dpi=dpi_value)
axs.axis('tight')
axs.axis('off')

data = [[  66386,  174296,   75131,  577908,   32015, 49682, 76314],
        [  58230,  381139,   78045,   99308,  160454, 74674, 64311],
        [  89135,   80552,  152558,  497981,  603535, 75312, 25687],
        [  78415,   81858,  150656,  193263,   69638, 14566, 48753],
        [ 139361,  331509,  343164,  781380,   52269, 54378, 64745]]

columns = ('Freeze', 'Wind', 'Fire', 'Flood', 'Quake', 'Drought', 'Hail')
rows = ['%d year' % x for x in (100, 50, 20, 10, 5)]

edges_table = plt.table(cellText=data,
                        rowLabels=rows,
                        colLabels=columns,
                        cellLoc='center',
                        bbox=[0.0, 0.0, 1.0, 1.0],
                        zorder=10
                        )

for row_idx in range(len(data)+1):
    for col_idx in range(len(data[0])):
        edges = 'horizontal'
        if col_idx == 0:
            edges = 'BTL'
        if (col_idx+1) % 3 == 0:
            edges = 'BTR'
        if (col_idx+1) == len(data[0]):
            edges = 'BTR'
        edges_table[row_idx, col_idx].visible_edges = edges

cellcolours_array = [['green', 'green', 'green', 'orange', 'orange', 'orange', 'cyan'],
        ['green', 'green', 'green', 'orange', 'orange', 'orange', 'cyan'],
        ['green', 'green', 'green', 'orange', 'orange', 'orange', 'cyan'],
        ['green', 'green', 'green', 'orange', 'orange', 'orange', 'cyan'],
        ['green', 'green', 'green', 'orange', 'orange', 'orange', 'cyan']]

colors_table = plt.table(cellText=data,
                         cellColours=cellcolours_array,
                         rowLabels=rows,
                         colLabels=columns,
                         cellLoc='center',
                         bbox=[0.0, 0.0, 1.0, 1.0],
                         zorder=1
                         )

for child in edges_table.get_children():
    child.set(facecolor=(1.0, 1.0, 1.0, 0.0))
    child.set_text_props(color=(1.0, 1.0, 1.0, 0.0))
for child in colors_table.get_children():
    child.set(linewidth=0)
plt.show()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Krusenstern