0

I am trying to do a risk matrix using Python that integrate severity and probability, I already tried using heatmaps, and it is maybe the closest kind of graph I have found so far, but I think it does not represent the basic structure of a risk matrix. The next image shows the kind of matrix graph I want. I would appreciate any recommendation: library, link...whatever to be able to graph a risk matrix.

enter image description here

This is the data I am trying to locate inside the risk matrix:

|---------------------|------------------|----------------------|
|      Component      |        KPI       |     Classification             
|---------------------|------------------|----------------------|
|          12         |         34       |    High Criticality
|---------------------|------------------|----------------------|
          Start                 38            High Criticality
|---------------------|------------------|----------------------|
         Fusela                 45            Low Criticality
|---------------------|------------------|----------------------|
          Hyd                   50           Medium Criticality
|---------------------|------------------|----------------------|
          Damp                  51           Medium Criticality
|---------------------|------------------|----------------------|
         Turbine                62           High Criticality
|---------------------|------------------|----------------------|
        Intercon                65          Medium Criticality
|---------------------|------------------|----------------------|
       Main Rotor               90           High Criticality
|---------------------|------------------|----------------------|
         AM-19                  93            High Criticality
|---------------------|------------------|----------------------|
      Main Trans                98            High Criticality
|---------------------|------------------|----------------------|

And this is the code I already implemented using heatmap:

import matplotlib.pyplot as plt

data = data.sort_values(by = 'KPI', ascending = False)
x = 1
for element in list(data['Componente']):
    data['Componente'] = data['Componente'].str.replace(str(element),'{}. 
{}'.format(str(x),element))
    x = x + 1
data['Clasificación'] = data['Clasificación'].str.replace('Criticidad 
Alta','1. Criticidad Alta').str.replace('Criticidad Media','2. Criticidad 
Media').str.replace('Criticidad Baja', '3. Criticidad Baja')
result = data.pivot(index='Componente',columns= 'Clasificacion', values = 
'KPI')
sb.heatmap(result, annot= True ,cmap='RdYlGn' ,fmt=".1f", vmax=100)
plt.figtext(.5,.9,'RESULTADO MATRIZ RIESGO', fontsize=14, ha='center')
plt.show()

The output I woudl like is something like the next imager:

enter image description here

Zephyr
  • 11,891
  • 53
  • 45
  • 80
Andres
  • 59
  • 2
  • 11

2 Answers2

2

Here is an idea with plt.imshow, and plt.annotate:

# function to make risk matrix
def make_risk_matrix(shape=3,levels=3):
    matrix = np.zeros((shape, shape))
    for level in range(levels):
        matrix[np.triu_indices(shape, level)] += 1
    return matrix

# specify bin borders and respective positions
likelihood_bins = [100,86,60]
positions = [0,1,2]

for position, likelihood in zip(positions, likelihood_bins):
    df.loc[df['KPI']<=likelihood, 'y'] = position

# generate x-positions from classification column
df['x'] = df['Classification'].replace({'High':2, 'Medium':1, 'Low':0})

# default offset for x -position
x_offset = -.4

# generate risk matrix and display as image
risk_matrix = make_risk_matrix()
plt.imshow(risk_matrix, cmap='RdYlGn_r')

# write individual components on it
# as some components will end up in hte same bin,
# caculate y-offset on the fly
for group in df.groupby(['x', 'y']):
    y_offset = -.3
    for ix, row in group[1].iterrows():

        plt.annotate(
            row['Component'], 
            xy=(
                row['x']+x_offset,
                row['y']+y_offset
                )
            )
        y_offset +=.15 # update y_offset

plt.xlabel('Consequence')
plt.ylabel('Likelihood')

enter image description here

df that i used was this one:

    Component   KPI Classification
1   12          34  High
2   Start       38  High
3   Fusela      45  Low
4   Hyd         50  Medium
[...]
warped
  • 8,947
  • 3
  • 22
  • 49
1

I had the same need and came up with the solution below (leveraging ideas/code snippets from Matplotlib Subplots -- Get Rid of Tick Labels Altogether).

The plots are built from the ground up using figs and multiple axes, so it should be easy to modify/update as needed.

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd


fig = plt.figure()
plt.subplots_adjust(wspace=0, hspace=0)
plt.xticks([])
plt.yticks([])
plt.xlim(0, 5)
plt.ylim(0, 5)
plt.xlabel('Consequence')
plt.ylabel('Likelihood')
plt.title('Example of Risk Matrix Plot')

#This example is for a 5 * 5 matrix
nrows=5
ncols=5
axes = [fig.add_subplot(nrows, ncols, r * ncols + c + 1) for r in range(0, nrows) for c in range(0, ncols) ]

# remove the x and y ticks
for ax in axes:
    ax.set_xticks([])
    ax.set_yticks([])
    ax.set_xlim(0,5)
    ax.set_ylim(0,5)

#Add background colors
#This has been done manually for more fine-grained control
#Run the loop below to identify the indice of the axes

#Identify the index of the axes
#for i in range(len(axes)):
#    axes[i].text(0,0, i)

green = [10, 15, 16, 20 , 21] #Green boxes
yellow = [0, 5, 6, 11, 17, 22, 23] #yellow boxes
orange = [1 , 2, 7, 12, 13, 18, 19, 24] # orange boxes
red = [3, 4, 8, 9, 14] #red boxes

for _ in green:
    axes[_].set_facecolor('green')

for _ in yellow:
    axes[_].set_facecolor('yellow')

for _ in orange:
    axes[_].set_facecolor('orange')

for _ in red:
    axes[_].set_facecolor('red')


#Add labels to the Green boxes
axes[10].text(0.1,0.8, '4')
axes[15].text(0.1,0.8, '2')
axes[20].text(0.1,0.8, '1')
axes[16].text(0.1,0.8, '5')
axes[21].text(0.1,0.8, '3')


#Add labels to the Yellow boxes
axes[0].text(0.1,0.8, '11')
axes[5].text(0.1,0.8, '7')
axes[6].text(0.1,0.8, '12')
axes[11].text(0.1,0.8, '8')
axes[17].text(0.1,0.8, '9')
axes[22].text(0.1,0.8, '6')
axes[23].text(0.1,0.8, '10')

#Add lables to the Orange boxes
axes[1].text(0.1,0.8, '16')
axes[2].text(0.1,0.8, '20')
axes[7].text(0.1,0.8, '17')
axes[12].text(0.1,0.8, '13')
axes[13].text(0.1,0.8, '18')
axes[18].text(0.1,0.8, '14')
axes[19].text(0.1,0.8, '19')
axes[24].text(0.1,0.8, '15')

#Add lables to the Red Boxes
axes[3].text(0.1,0.8, '23')
axes[8].text(0.1,0.8, '21')
axes[4].text(0.1,0.8, '25')
axes[9].text(0.1,0.8, '24')
axes[14].text(0.1,0.8, '22')

#Plot some data

for _ in range(len(axes)):
        axes[_].plot(np.random.uniform(2, 4, 5), np.random.uniform(2,4,5), '.')
plt.show()

Sample Output Image

RiveN
  • 2,595
  • 11
  • 13
  • 26