Visualization in Python

To visualize the metrics API response, you can use the following Python code in a Jupyter notebook.

Classification Metrics

This example demonstrates how to plot both a confusion matrix and the performance metrics (e.g., accuracy, F1, precision, recall, specificity) over time.

First, we need to fetch the metrics for a specific time window.

import requests

url = f"http://{CLUSTER_IP}/metrics-server/api/v1/metrics/pipeline/classification"

params = {
    'namespace': 'seldon',
    'pipelineName': 'iris-model-pipeline',
    'modelName': 'iris-model',
    'startTime': '2025-02-25T11:51:22Z',
    'endTime': '2025-02-25T11:53:22Z',
    'interval': '10s'
}

response = requests.get(url, params=params)
Expand to see an example of metrics API response
{
  "metrics": [
    {
      "accuracy": 0,
      "confusionMatrix": {
        "categories": [
          "Setosa",
          "Versicolor",
          "Virginica"
        ],
        "computedConfusionValues": [
          {
            "falseNegativeCount": 10,
            "falsePositiveCount": 0,
            "trueNegativeCount": 0,
            "truePositiveCount": 0
          },
          {
            "falseNegativeCount": 0,
            "falsePositiveCount": 10,
            "trueNegativeCount": 0,
            "truePositiveCount": 0
          },
          {
            "falseNegativeCount": 0,
            "falsePositiveCount": 0,
            "trueNegativeCount": 10,
            "truePositiveCount": 0
          }
        ],
        "values": [
          0,
          10,
          0,
          0,
          0,
          0,
          0,
          0,
          0
        ]
      },
      "endTime": "2025-02-25T11:51:32Z",
      "f1": 0,
      "precision": 0,
      "recall": 0,
      "specificity": 0.5
    },
    {
      "accuracy": 0,
      "confusionMatrix": {
        "categories": [
          "Setosa",
          "Versicolor",
          "Virginica"
        ],
        "computedConfusionValues": [
          {
            "falseNegativeCount": 16,
            "falsePositiveCount": 0,
            "trueNegativeCount": 0,
            "truePositiveCount": 0
          },
          {
            "falseNegativeCount": 0,
            "falsePositiveCount": 16,
            "trueNegativeCount": 0,
            "truePositiveCount": 0
          },
          {
            "falseNegativeCount": 0,
            "falsePositiveCount": 0,
            "trueNegativeCount": 16,
            "truePositiveCount": 0
          }
        ],
        "values": [
          0,
          16,
          0,
          0,
          0,
          0,
          0,
          0,
          0
        ]
      },
      "endTime": "2025-02-25T11:51:42Z",
      "f1": 0,
      "precision": 0,
      "recall": 0,
      "specificity": 0.5
    },
    {
      "accuracy": 0.4375,
      "confusionMatrix": {
        "categories": [
          "Setosa",
          "Versicolor",
          "Virginica"
        ],
        "computedConfusionValues": [
          {
            "falseNegativeCount": 9,
            "falsePositiveCount": 0,
            "trueNegativeCount": 7,
            "truePositiveCount": 0
          },
          {
            "falseNegativeCount": 0,
            "falsePositiveCount": 9,
            "trueNegativeCount": 0,
            "truePositiveCount": 7
          },
          {
            "falseNegativeCount": 0,
            "falsePositiveCount": 0,
            "trueNegativeCount": 16,
            "truePositiveCount": 0
          }
        ],
        "values": [
          0,
          9,
          0,
          0,
          7,
          0,
          0,
          0,
          0
        ]
      },
      "endTime": "2025-02-25T11:51:52Z",
      "f1": 0.46666667,
      "precision": 0.4375,
      "recall": 0.5,
      "specificity": 0.6666667
    },
    {
      "accuracy": 0.125,
      "confusionMatrix": {
        "categories": [
          "Setosa",
          "Versicolor",
          "Virginica"
        ],
        "computedConfusionValues": [
          {
            "falseNegativeCount": 2,
            "falsePositiveCount": 0,
            "trueNegativeCount": 6,
            "truePositiveCount": 0
          },
          {
            "falseNegativeCount": 0,
            "falsePositiveCount": 7,
            "trueNegativeCount": 0,
            "truePositiveCount": 1
          },
          {
            "falseNegativeCount": 5,
            "falsePositiveCount": 0,
            "trueNegativeCount": 3,
            "truePositiveCount": 0
          }
        ],
        "values": [
          0,
          2,
          0,
          0,
          1,
          0,
          0,
          5,
          0
        ]
      },
      "endTime": "2025-02-25T11:52:02Z",
      "f1": 0.18181819,
      "precision": 0.125,
      "recall": 0.33333334,
      "specificity": 0.6666667
    },
    {
      "accuracy": -1,
      "confusionMatrix": {
        "categories": [],
        "computedConfusionValues": [],
        "values": []
      },
      "endTime": "2025-02-25T11:52:12Z",
      "f1": -1,
      "precision": -1,
      "recall": -1,
      "specificity": -1
    },
    {
      "accuracy": -1,
      "confusionMatrix": {
        "categories": [],
        "computedConfusionValues": [],
        "values": []
      },
      "endTime": "2025-02-25T11:52:22Z",
      "f1": -1,
      "precision": -1,
      "recall": -1,
      "specificity": -1
    },
    {
      "accuracy": -1,
      "confusionMatrix": {
        "categories": [],
        "computedConfusionValues": [],
        "values": []
      },
      "endTime": "2025-02-25T11:52:32Z",
      "f1": -1,
      "precision": -1,
      "recall": -1,
      "specificity": -1
    },
    {
      "accuracy": -1,
      "confusionMatrix": {
        "categories": [],
        "computedConfusionValues": [],
        "values": []
      },
      "endTime": "2025-02-25T11:52:42Z",
      "f1": -1,
      "precision": -1,
      "recall": -1,
      "specificity": -1
    },
    {
      "accuracy": -1,
      "confusionMatrix": {
        "categories": [],
        "computedConfusionValues": [],
        "values": []
      },
      "endTime": "2025-02-25T11:52:52Z",
      "f1": -1,
      "precision": -1,
      "recall": -1,
      "specificity": -1
    },
    {
      "accuracy": -1,
      "confusionMatrix": {
        "categories": [],
        "computedConfusionValues": [],
        "values": []
      },
      "endTime": "2025-02-25T11:53:02Z",
      "f1": -1,
      "precision": -1,
      "recall": -1,
      "specificity": -1
    },
    {
      "accuracy": -1,
      "confusionMatrix": {
        "categories": [],
        "computedConfusionValues": [],
        "values": []
      },
      "endTime": "2025-02-25T11:53:12Z",
      "f1": -1,
      "precision": -1,
      "recall": -1,
      "specificity": -1
    },
    {
      "accuracy": -1,
      "confusionMatrix": {
        "categories": [],
        "computedConfusionValues": [],
        "values": []
      },
      "endTime": "2025-02-25T11:53:22Z",
      "f1": -1,
      "precision": -1,
      "recall": -1,
      "specificity": -1
    }
  ]
}

Next, we can build a confusion matrix for the selected time bucket. For more details on time bucketing, refer to the "Calculating Metrics" page.

%matplotlib inline

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

json_data = response.json()

# Choosing a time bucket
time_bucket_index = 0
confusion_matrix_data = json_data['metrics'][time_bucket_index]['confusionMatrix']

# Extracting the confusion matrix values
categories = confusion_matrix_data['categories']
values = confusion_matrix_data['values']

# Reshape the values to form the confusion matrix
matrix_size = len(categories)
confusion_matrix = np.array(values).reshape((matrix_size, matrix_size))

# Creating a pandas dataframe for better visualization
df_cm = pd.DataFrame(confusion_matrix, index=categories, columns=categories)

# Plotting the confusion matrix
plt.figure(figsize=(8, 6))
sns.heatmap(df_cm, annot=True, fmt='g', cmap='Blues')
plt.title('Confusion Matrix')
plt.xlabel('Predicted Labels')
plt.ylabel('Actual Labels')
plt.show()
Example of a Confusion matrix using test data

Finally, we can plot the Accuracy, Precision, Recall, F1, and Specificity metrics over time.

%matplotlib inline

import matplotlib.pyplot as plt
import pandas as pd

df = pd.DataFrame(json_data['metrics'])

# Plotting the data
plt.figure(figsize=(10, 6))

for metric in ['accuracy', 'f1', 'precision', 'recall', 'specificity']:
    df_filtered = df[df[metric] != -1]
    plt.plot(df_filtered['endTime'], df_filtered[metric], label=metric)

plt.xlabel('Time')
plt.ylabel('Metrics')
plt.title('Model Performance Metrics Over Time')
plt.legend()
plt.grid(True)
plt.xticks(rotation=45)
plt.tight_layout()

plt.show()
Example of a Classification Metrics plot using test data

Regression Metrics

This example demonstrates how to plot such performance metrics as Mean Absolute Error, Mean Squared Error, Root Mean Squared Error over time.

Let's fetch the metrics over specific time window first.

import requests

url = f"http://{CLUSTER_IP}/metrics-server/api/v1/metrics/pipeline/regression"

params = {
    'namespace': 'seldon',
    'pipelineName': 'house-value-pipeline',
    'modelName': 'house-value-model',
    'startTime': '2025-02-25T16:02:28Z',
    'endTime': '2025-02-25T16:04:28Z',
    'interval': '5s'
}

response = requests.get(url, params=params)
Expand to see an example of metrics API response
{
  "metrics": [
    {
      "endTime": "2025-02-25T16:02:33Z",
      "meanAbsoluteError": 3.5947158,
      "meanSquaredError": 12.921982,
      "rootMeanSquaredError": 3.5947158
    },
    {
      "endTime": "2025-02-25T16:02:38Z",
      "meanAbsoluteError": 3.5947158,
      "meanSquaredError": 12.921982,
      "rootMeanSquaredError": 3.5947158
    },
    {
      "endTime": "2025-02-25T16:02:43Z",
      "meanAbsoluteError": 3.5947158,
      "meanSquaredError": 12.921982,
      "rootMeanSquaredError": 3.5947158
    },
    {
      "endTime": "2025-02-25T16:02:48Z",
      "meanAbsoluteError": 1.9672309,
      "meanSquaredError": 5.618767,
      "rootMeanSquaredError": 2.3703938
    },
    {
      "endTime": "2025-02-25T16:02:53Z",
      "meanAbsoluteError": 1.8137174,
      "meanSquaredError": 4.2309737,
      "rootMeanSquaredError": 2.0569332
    },
    {
      "endTime": "2025-02-25T16:02:58Z",
      "meanAbsoluteError": 1.7597755,
      "meanSquaredError": 6.0277104,
      "rootMeanSquaredError": 2.4551396
    },
    {
      "endTime": "2025-02-25T16:03:03Z",
      "meanAbsoluteError": 2.8212993,
      "meanSquaredError": 14.908364,
      "rootMeanSquaredError": 3.861135
    },
    {
      "endTime": "2025-02-25T16:03:08Z",
      "meanAbsoluteError": -1,
      "meanSquaredError": -1,
      "rootMeanSquaredError": -1
    },
    {
      "endTime": "2025-02-25T16:03:13Z",
      "meanAbsoluteError": -1,
      "meanSquaredError": -1,
      "rootMeanSquaredError": -1
    },
    {
      "endTime": "2025-02-25T16:03:18Z",
      "meanAbsoluteError": -1,
      "meanSquaredError": -1,
      "rootMeanSquaredError": -1
    },
    {
      "endTime": "2025-02-25T16:03:23Z",
      "meanAbsoluteError": -1,
      "meanSquaredError": -1,
      "rootMeanSquaredError": -1
    },
    {
      "endTime": "2025-02-25T16:03:28Z",
      "meanAbsoluteError": -1,
      "meanSquaredError": -1,
      "rootMeanSquaredError": -1
    },
    {
      "endTime": "2025-02-25T16:03:33Z",
      "meanAbsoluteError": -1,
      "meanSquaredError": -1,
      "rootMeanSquaredError": -1
    },
    {
      "endTime": "2025-02-25T16:03:38Z",
      "meanAbsoluteError": -1,
      "meanSquaredError": -1,
      "rootMeanSquaredError": -1
    },
    {
      "endTime": "2025-02-25T16:03:43Z",
      "meanAbsoluteError": -1,
      "meanSquaredError": -1,
      "rootMeanSquaredError": -1
    },
    {
      "endTime": "2025-02-25T16:03:48Z",
      "meanAbsoluteError": -1,
      "meanSquaredError": -1,
      "rootMeanSquaredError": -1
    },
    {
      "endTime": "2025-02-25T16:03:53Z",
      "meanAbsoluteError": -1,
      "meanSquaredError": -1,
      "rootMeanSquaredError": -1
    },
    {
      "endTime": "2025-02-25T16:03:58Z",
      "meanAbsoluteError": -1,
      "meanSquaredError": -1,
      "rootMeanSquaredError": -1
    },
    {
      "endTime": "2025-02-25T16:04:03Z",
      "meanAbsoluteError": -1,
      "meanSquaredError": -1,
      "rootMeanSquaredError": -1
    },
    {
      "endTime": "2025-02-25T16:04:08Z",
      "meanAbsoluteError": -1,
      "meanSquaredError": -1,
      "rootMeanSquaredError": -1
    },
    {
      "endTime": "2025-02-25T16:04:13Z",
      "meanAbsoluteError": -1,
      "meanSquaredError": -1,
      "rootMeanSquaredError": -1
    },
    {
      "endTime": "2025-02-25T16:04:18Z",
      "meanAbsoluteError": -1,
      "meanSquaredError": -1,
      "rootMeanSquaredError": -1
    },
    {
      "endTime": "2025-02-25T16:04:23Z",
      "meanAbsoluteError": -1,
      "meanSquaredError": -1,
      "rootMeanSquaredError": -1
    },
    {
      "endTime": "2025-02-25T16:04:28Z",
      "meanAbsoluteError": -1,
      "meanSquaredError": -1,
      "rootMeanSquaredError": -1
    }
  ]
}

Finally, we can plot the Mean Absolute Error, Mean Squared Error, Root Mean Squared Error over time.

%matplotlib inline

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

json_data = response.json()
df = pd.DataFrame(json_data ['metrics'])

plt.figure(figsize=(10, 6))

for metric in ['meanAbsoluteError', 'meanSquaredError', 'rootMeanSquaredError']:
    filtered = df[df[metric] != -1]
    plt.plot(filtered['endTime'], filtered[metric], label=metric)

plt.xlabel('Time')
plt.ylabel('Metrics')
plt.title('Model Performance Metrics Over Time')
plt.legend()
plt.grid(True)
plt.xticks(rotation=45)
plt.tight_layout()

plt.show()
Example of a Regression Metrics plot using test data

Last updated

Was this helpful?