Repository: pandas-dev/pandas

Issue:
test_bar_log fights back with matplotlib 1.3.0
```
======================================================================
FAIL: test_bar_log (pandas.tests.test_graphics.TestDataFramePlots)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/yoh/deb/gits/pkg-exppsy/pandas/pandas/tests/test_graphics.py", line 568, in test_bar_log
    self.assertEqual(ax.yaxis.get_ticklocs()[0], 1.0)
AssertionError: 0.10000000000000001 != 1.0

----------------------------------------------------------------------
Ran 1 test in 0.341s
```

needs to be fixed up for debian where mpl now 1.3.0



Discussion:
@yarikoptic side note - aren't there a _bunch_ of failing tests for nipy's pandas builds?

@jtratner -- yes unfortunately there are 
http://nipy.bic.berkeley.edu/waterfall?category=pandas
and I would appreciate if they get addressed.  ATM I worry about this particular one to stabilize released pandas 0.12 building on Debian

@yarikoptic or @cpcloud any chance there's a vagrant box already set up to make it easier to debug these locally?

@yarikoptic Forgive me, but can you give a 2-second overview of how to navigate all of that stuff?

@jtratner  for this particular bug I guess you would not find any "stock" vagrant box since Debian unstable is rolling too much (updated twice a day) for anyone to care... may be at some point I would reincarnate my veewee setup to furnish those.  But you could get ANY Debian based vagrant box (thus stock too) and debootstrap a complete Debian into a subdirectory within... see e.g. my elderly post http://neuro.debian.net/blog/2011/2011-12-12_schroot_fslview.html

For sparc-specific bugs -- you would need a sparc box and vagrant alone would not be sufficient.

@cpcloud buildbot's views are indeed a bit archaic and require "get used to". I prefer waterfall for a quick look
http://nipy.bic.berkeley.edu/waterfall?category=pandas
there on top it better be green ;)   those are builders names, defined as combinations of project, python, architecture etc in their names, e.g.  pandas-py2.x-sid-sparc   is pandas with python2 (whatever is default there), Debian sid, sparc architecture.  Thus you have "builders" for sid (unstable) and wheezy (stable Debian), python2 and python3 (concrete versions differ between sid and wheezy -- look inside logs ;) ).

if any builder "red" -- look inside,e.g. go to
http://nipy.bic.berkeley.edu/builders/pandas-py2.x-sid-sparc
there you would see all failed/succeeded builds.  For failed, go to the failed one e.g. 
http://nipy.bic.berkeley.edu/builders/pandas-py2.x-sid-sparc/builds/87
and you will see what succeded (in green) and what failed (again in red).  Step 9 here (`shell_4 'xvfb-run --auto-servernum ...`) is the one running tests and it is in red -- just click on its stdio link to get to entire dump of output.

You could also get there straight from waterfall for recent builds -- for older ones you might need to scroll down to get to that failed step in red.

I hope this helps

@cpcloud I actually find it easier to just go to this page - http://nipy.bic.berkeley.edu/builders and search for 'pandas' - then it's basically just the same thing as Travis, just need to click on the red blocks until it shows you a list of test cases.

so what about the issue itself? ;)

@yarikoptic forgot to say: thanks, your explanation was very helpful...

re this bug....i'll take a look...i don't think this is just happening on sparc

@cpcloud I never said it happens just on sparc ;)  it  is matplotlib 1.3.0 compatibility issue

@y-p 

wondering if u know the reason for this:

```
In [1]: paste
        p1 = Series([200, 500]).plot(log=True, kind='bar')
## -- End pasted text --

In [2]: p1.yaxis.get_ticklocs()
Out[2]: array([     0.1,      1. ,     10. ,    100. ,   1000. ,  10000. ])
```

note the extra 0.1 and 10000, here's the plot

![i-can-haz-lawg-plawt](https://f.cloud.github.com/assets/417981/1135598/42e84d86-1c19-11e3-9f7f-c0598bf0ac20.png)

only 1, 10, 100, and 1000 in the plot....should i report to matplotlib?

opened an issue over at matplotlib

https://github.com/matplotlib/matplotlib/issues/2419

> commit 4c0f3f30f805f53f91f22f26212d366c59d2ae48
> Author: Daniel Hyams <dhyams@gitdev.(none)>
> Date:   Sat Jan 19 19:14:03 2013 -0500
> 
>    Modifications to MultipleLocator and LogLocator, so that the locators will
>    give locations one past what they really need to.  This is necessary, because
>    in situations where round off error matters, we want the locator to offer
>    more that it really thinks it needs to.  The clipping of ticks still takes
>    place in the Axis class, so it's perfectly fine to add more locs than necessary.
> 
>    In fact, matplotlib relies on the clipping behavior in the Axis class already
>    to not draw ticks outside of the limits of the axis.

I'll update the tests for different versions of MPL


Resolving patch:
diff --git a/pandas/tools/plotting.py b/pandas/tools/plotting.py
--- a/pandas/tools/plotting.py
+++ b/pandas/tools/plotting.py
@@ -4,6 +4,7 @@
 import warnings
 import re
 from contextlib import contextmanager
+from distutils.version import LooseVersion
 
 import numpy as np
 
@@ -1452,7 +1453,13 @@ def f(ax, x, y, w, start=None, log=self.log, **kwds):
 
     def _make_plot(self):
         import matplotlib as mpl
+
+        # mpl decided to make their version string unicode across all Python
+        # versions for mpl >= 1.3 so we have to call str here for python 2
+        mpl_le_1_2_1 = str(mpl.__version__) <= LooseVersion('1.2.1')
+
         colors = self._get_colors()
+        ncolors = len(colors)
         rects = []
         labels = []
 
@@ -1466,19 +1473,18 @@ def _make_plot(self):
             ax = self._get_ax(i)
             label = com.pprint_thing(label)
             kwds = self.kwds.copy()
-            kwds['color'] = colors[i % len(colors)]
+            kwds['color'] = colors[i % ncolors]
 
-            start =0
+            start = 0
             if self.log:
                 start = 1
                 if any(y < 1):
                     # GH3254
-                    start = 0 if mpl.__version__ == "1.2.1" else None
+                    start = 0 if mpl_le_1_2_1 else None
 
             if self.subplots:
                 rect = bar_f(ax, self.ax_pos, y,  self.bar_width,
-                             start = start,
-                             **kwds)
+                             start=start, **kwds)
                 ax.set_title(label)
             elif self.stacked:
                 mask = y > 0
@@ -1489,8 +1495,7 @@ def _make_plot(self):
                 neg_prior = neg_prior + np.where(mask, 0, y)
             else:
                 rect = bar_f(ax, self.ax_pos + i * 0.75 / K, y, 0.75 / K,
-                             start = start,
-                              label=label, **kwds)
+                             start=start, label=label, **kwds)
             rects.append(rect)
             if self.mark_right:
                 labels.append(self._get_marked_label(label, i))


Test patch:
diff --git a/pandas/tests/test_graphics.py b/pandas/tests/test_graphics.py
--- a/pandas/tests/test_graphics.py
+++ b/pandas/tests/test_graphics.py
@@ -2,6 +2,7 @@
 import os
 import string
 import unittest
+from distutils.version import LooseVersion
 
 from datetime import datetime, date, timedelta
 
@@ -34,8 +35,9 @@ def setUpClass(cls):
         try:
             import matplotlib as mpl
             mpl.use('Agg', warn=False)
+            cls.mpl_le_1_2_1 = str(mpl.__version__) <= LooseVersion('1.2.1')
         except ImportError:
-            raise nose.SkipTest
+            raise nose.SkipTest("matplotlib not installed")
 
     def setUp(self):
         self.ts = tm.makeTimeSeries()
@@ -161,6 +163,16 @@ def test_bar_linewidth(self):
             for r in ax.patches:
                 self.assert_(r.get_linewidth() == 2)
 
+    @slow
+    def test_bar_log(self):
+        expected = np.array([1., 10., 100., 1000.])
+
+        if not self.mpl_le_1_2_1:
+            expected = np.hstack((.1, expected, 1e4))
+
+        ax = Series([200, 500]).plot(log=True, kind='bar')
+        assert_array_equal(ax.yaxis.get_ticklocs(), expected)
+
     def test_rotation(self):
         df = DataFrame(randn(5, 5))
         ax = df.plot(rot=30)
@@ -342,8 +354,9 @@ def setUpClass(cls):
         try:
             import matplotlib as mpl
             mpl.use('Agg', warn=False)
+            cls.mpl_le_1_2_1 = str(mpl.__version__) <= LooseVersion('1.2.1')
         except ImportError:
-            raise nose.SkipTest
+            raise nose.SkipTest("matplotlib not installed")
 
     def tearDown(self):
         import matplotlib.pyplot as plt
@@ -559,22 +572,31 @@ def test_bar_center(self):
                          ax.patches[0].get_x() + ax.patches[0].get_width())
 
     @slow
-    def test_bar_log(self):
+    def test_bar_log_no_subplots(self):
         # GH3254, GH3298 matplotlib/matplotlib#1882, #1892
         # regressions in 1.2.1
+        expected = np.array([1., 10.])
+
+        if not self.mpl_le_1_2_1:
+            expected = np.hstack((.1, expected, 100))
 
+        # no subplots
         df = DataFrame({'A': [3] * 5, 'B': lrange(1, 6)}, index=lrange(5))
         ax = df.plot(kind='bar', grid=True, log=True)
-        self.assertEqual(ax.yaxis.get_ticklocs()[0], 1.0)
+        assert_array_equal(ax.yaxis.get_ticklocs(), expected)
+
+    @slow
+    def test_bar_log_subplots(self):
+        expected = np.array([1., 10., 100., 1000.])
+        if not self.mpl_le_1_2_1:
+            expected = np.hstack((.1, expected, 1e4))
 
-        p1 = Series([200, 500]).plot(log=True, kind='bar')
-        p2 = DataFrame([Series([200, 300]),
+        ax = DataFrame([Series([200, 300]),
                         Series([300, 500])]).plot(log=True, kind='bar',
                                                   subplots=True)
 
-        (p1.yaxis.get_ticklocs() == np.array([0.625, 1.625]))
-        (p2[0].yaxis.get_ticklocs() == np.array([1., 10., 100., 1000.])).all()
-        (p2[1].yaxis.get_ticklocs() == np.array([1., 10., 100., 1000.])).all()
+        assert_array_equal(ax[0].yaxis.get_ticklocs(), expected)
+        assert_array_equal(ax[1].yaxis.get_ticklocs(), expected)
 
     @slow
     def test_boxplot(self):