Plotting shaded uncertainty region in line plot in matplotlib when data has NaNs

patapouf_ai picture patapouf_ai · Mar 28, 2017 · Viewed 19.5k times · Source

I would like a plot which looks like this: plot with uncertainty

I am trying to do this with matplotlib:

fig, ax = plt.subplots()

with sns.axes_style("darkgrid"):
    for i in range(5):
        ax.plot(means.ix[i][list(range(3,104))], label=means.ix[i]["label"])
        ax.fill_between(means.ix[i][list(range(3,104))]-stds.ix[i][list(range(3,104))], means.ix[i][list(range(3,104))]+stds.ix[i][list(range(3,104))])
    ax.legend()

I want the shaded region to be the same colour as the line in the centre. But right now, my problem is that means has some NaNs and fill_between does not accept that. I get the error

TypeError: ufunc 'isfinite' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''

Any ideas on how I could achieve what I want? The solution doesn't need to use matplotlib as long as it can plot my series of points with their uncertainties for multiple series.

Answer

patapouf_ai picture patapouf_ai · Mar 28, 2017

Ok. So one of the problem was that the dtype of my data was object and not float and this caused fill_between to fail when it looked to see if the numbers were finite. I finally managed to do it by (a) converting to float and then (b) to solve the problem of the matching colours for uncertainty and line, to use a colour palette. So I have:

import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
fig, ax = plt.subplots()
clrs = sns.color_palette("husl", 5)
with sns.axes_style("darkgrid"):
    epochs = list(range(101))
    for i in range(5):
        meanst = np.array(means.ix[i].values[3:-1], dtype=np.float64)
        sdt = np.array(stds.ix[i].values[3:-1], dtype=np.float64)
        ax.plot(epochs, meanst, label=means.ix[i]["label"], c=clrs[i])
        ax.fill_between(epochs, meanst-sdt, meanst+sdt ,alpha=0.3, facecolor=clrs[i])
    ax.legend()
    ax.set_yscale('log')

which gave me the following result: enter image description here