Jeff W.
Jeff W.

Reputation: 355

Adding error bars to Matplotlib-generated graph of Pandas dataframe creates invalid legend

I am trying to graph a Pandas dataframe using Matplotlib. The dataframe contains four data columns composed of natural numbers, and an index of integers. I would like to produce a single plot with line graphs for each of the four columns, as well as error bars for each point. In addition, I would like to produce a legend providing labels for each of the four graphed lines.

Graphing the lines and legend without error bars works fine. When I introduce error bars, however, the legend becomes invalid -- the colours it uses no longer correspond to the appropriate lines. If you compare a graph with error bars and a graph without, the legend and the shapes/positions of the curves remain exactly the same. The colours of the curves get switched about, however, so that though the same four colours are used, they now correspond to different curves, meaning that the legend now assigns the wrong label to each curve.

My graphing code is thus:

def plot_normalized(agged, show_errorbars, filename):
  combined = {}
  # "agged" is a dictionary containing Pandas dataframes. Each dataframe
  # contains both a CPS_norm_mean and CPS_norm_std column. By running the code
  # below, the single dataframe "combined" is created, which has integer
  # indices and a column for each of the four CPS_norm_mean columns contained
  # in agged's four dataframes.
  for k in agged:
    combined[k] = agged[k]['CPS_norm_mean']
  combined = pandas.DataFrame(combined)

  plt.figure()
  combined.plot()

  if show_errorbars:
    for k in agged:
      plt.errorbar(
        x=agged[k].index,
        y=agged[k]['CPS_norm_mean'],
        yerr=agged[k]['CPS_norm_std']
      )

  plt.xlabel('Time')
  plt.ylabel('CPS/Absorbency')
  plt.title('CPS/Absorbency vs. Time')
  plt.savefig(filename)

The full 100-line script is available on GitHub. To run, download both graph.py and lux.csv, then run "python2 graph.py". It will generate two PNG files in your working directory -- one graph with error bars and one without.

The graphs are thus:

Observe that the graph without error bars is properly labelled; note that the graph with error bars is improperly labelled, as though the legend is identical, the line graphs' changed colours mean that each legend entry now refers to a different (wrong) curve.

Thanks for any help you can provide. I've spent a number of extremely aggravating hours bashing my head against the wall, and I suspect that I'm making a stupid beginner's mistake. For what it's worth, I've tried with the Matplotlib development tree, version 1.2.0, and 1.1.0, and all three have exhibited identical behaviour.

Upvotes: 3

Views: 2661

Answers (1)

arynaq
arynaq

Reputation: 6870

I am new to programming and python in general but I managed to throw together a dirty fix, the legends are now correct, the colors are not.

def plot_normalized(agged, show_errorbars, filename):
  combined = {}
  for k in agged:
    combined[k] = agged[k]['CPS_norm_mean']
  combined = pandas.DataFrame(combined)

  ax=combined.plot()

  if show_errorbars:
    for k in agged:
      plt.errorbar(
        x=agged[k].index,
        y=agged[k]['CPS_norm_mean'],
        yerr=agged[k]['CPS_norm_std'],
        label = k #added
      )

  if show_errorbars: #try this, dirty fix
   labels, handles = ax.get_legend_handles_labels()
   N = len(handles)/2
   plt.legend(labels[:N], handles[N:])

  #Why does the fix work?:
  #labels, handles = ax.get_legend_handles_labels()
  #print handles
  #out:
  #[u'Blank', u'H9A', u'Q180K', u'Wildtype', 'Q180K', 'H9A', 'Wildtype', 'Blank']
  #Right half has correct order, these are the labels from label=k above in errorplot



  plt.xlabel('Time')
  plt.ylabel('CPS/Absorbency')
  plt.title('CPS/Absorbency vs. Time')
  plt.savefig(filename)

Produces: No error plot Error plot

Upvotes: 2

Related Questions