3

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:

  • Correct graph (with no error bars):
  • Incorrect graph (with error bars):

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.

4
  • 1
    Can you up the pngs to somewhere (and show it here?) not on a PC, easier to get an answer as well. Commented Nov 25, 2012 at 21:27
  • Sorry -- I meant to include the graphs, but forgot to do so. I've now edited my post to include them. Unfortunately, I don't have a sufficiently high reputation to add images or post more than two hyperlinks, so you'll have to copy and paste the links yourself. I've also included them below: Correct graph (without error bars): i.imgur.com/XE3oY.png Incorrect graph (with error bars): i.imgur.com/OQVQL.png Commented Nov 25, 2012 at 21:37
  • Hmm it seems to me the order is reversed in the second graph (in how legends appear). Have you tried calling the if-test before combined.plot? Commented Nov 25, 2012 at 22:10
  • Thanks for your suggestion. Moving the "if" statement before the combined.plot() call results in an output identical to the correct graph -- though the legend is correct, no error bars are shown, even when show_errorbars is True. Commented Nov 25, 2012 at 22:25

1 Answer 1

2

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

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you kindly! Your solution works wonderfully. As you said, the colours aren't consistent between the graph with error bars and the graph without, but this is a trivial matter to me. As long as the legend accurately labels the graph, I'm quite satisfied. Thanks again!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.