[Repost: Note suggested patches] In mplot3d, how is format_zdata supposed to work? Why is sensible_format_data called?

[Repost from 21 Mar 2011: note suggested patches]

Hi all,
I am using Matplotlib 1.0.1 and am seeing weird behaviour with mplot3d and
ticker formatters, and I think I need to submit a patch to axes3d, but am not
sure how much it will break, because format_zdata() and format_coord() look to
be defined inconsistently.

When trying to rotate a plot, which was created including the following
commands,
  ...
  ax.plot(x[0,alow:atop],x[1,alow:atop],x[2,alow:atop],c=rgb.tolist())
  ax.w_xaxis.set_major_locator(tic.LinearLocator(3))
  ax.w_xaxis.set_major_formatter(tic.FormatStrFormatter(''))
  ax.w_yaxis.set_major_locator(tic.LinearLocator(3))
  ax.w_yaxis.set_major_formatter(tic.FormatStrFormatter(''))
  ax.w_zaxis.set_major_locator(tic.LinearLocator(3))
  ax.w_zaxis.set_major_formatter(tic.FormatStrFormatter(''))
  plt.draw()
  ...

I received the following backtrace and error message:

/usr/lib64/python2.6/site-packages/matplotlib/backend_bases.pyc in
mouse_move(self, event)
   2393 if event.inaxes and event.inaxes.get_navigate():
   2394
-> 2395 try: s = event.inaxes.format_coord(event.xdata,
event.ydata)
   2396 except ValueError: pass
   2397 except OverflowError: pass

/usr/lib64/python2.6/site-packages/mpl_toolkits/mplot3d/axes3d.pyc in
format_coord(self, xd, yd)
    474
    475 xs = self.format_xdata(x)
--> 476 ys = self.format_ydata(y)
    477 zs = self.format_ydata(z)
    478 return 'x=%s, y=%s, z=%s' % (xs, ys, zs)

/usr/lib64/python2.6/site-packages/mpl_toolkits/mplot3d/axes3d.pyc in
format_ydata(self, y)
    424 except TypeError:
    425 fmt = self.w_yaxis.get_major_formatter()
--> 426 return sensible_format_data(fmt, y)
    427
    428 def format_zdata(self, z):

/usr/lib64/python2.6/site-packages/mpl_toolkits/mplot3d/axes3d.pyc in
sensible_format_data(self, value)
     26 if abs(value) > 1e4 or abs(value)<1e-3:
     27 s = '%1.4e' % value
---> 28 return self._formatSciNotation(s)
     29 else:
     30 return '%4.3f' % value

AttributeError: FormatStrFormatter instance has no attribute
'_formatSciNotation'

···

---
I am using FormatStrFormatter('') to try to obtain an empty tick.

[1] It looks like sensible_format_data() assumes that self is class
ScalarFormatter(Formatter), since this is the only ticker Formatter that has
attribute _formatSciNotation(s). As far as I can tell, this means that
sensible_format_data(fmt,y) should *only* be called if fmt has class
ScalarFormatter(Formatter).

[2] In axes3d.py, I see:

    def format_zdata(self, z):
        """
        Return z string formatted. This function will use the attribute
        self.fmt_zdata if it is callable, else will fall back on the yaxis
        major formatter
        """
        try:
            return self.fmt_zdata(z)
        except (AttributeError, TypeError):
            fmt = self.w_zaxis.get_major_formatter()
            return sensible_format_data(fmt, z)

To me, it looks like the call to sensible_format_data(fmt, z) is wrong. The
same error occurs in format_xdata() and format_ydata(). So I would like to
submit a patch for format_xdata(), format_ydata(), format_zdata(), e.g.:

    def format_zdata(self, z):
        """
        Return z string formatted. This function will use the attribute
        self.fmt_zdata if it is callable, else will fall back on the zaxis
        major formatter
        """
        try:
            return self.fmt_zdata(z)
        except (AttributeError, TypeError):
            fmt = self.w_zaxis.get_major_formatter()
            try:
                return sensible_format_data(fmt, z)
            except (AttributeError, TypeError):
                return format_data(fmt, z)

[3] But I am also worried about the comment "else will fall back on the yaxis
major formatter". Shouldn't this say "the zaxis major formatter", since that
is what the code does? Or should the code use the yaxis major formatter, since
that is what the documentation says? The documentation
http://matplotlib.github.com/mpl_toolkits/mplot3d/api.html agrees with the
comment and not with the code.

[4] In def format_coord(self, xd, yd), I see

        xs = self.format_xdata(x)
        ys = self.format_ydata(y)
        zs = self.format_ydata(z)

Why doesn't the last line say
        zs = self.format_zdata(z)
?

Best, Paul

Paul,

This is a perfect example of the “with many eyes, all bugs are shallow” saying. I have been running into problems with this with respect to getting log scales to work. While your fixes doesn’t completely address the issue, it does bring me a few steps closer.

Your hunches are correct. There have been plenty of copy-n-paste mistakes within mplot3d, and it makes no sense to fall back onto the y-axis formatter. Also, note that the documentation is automatically generated from the string comments, so it is no surprise that they match.

I will make a patch for this for master and hopefully v1.0.x.

Thank you,
Ben Root

···

On Thu, May 5, 2011 at 7:16 PM, Paul Leopardi <paul.leopardi@…3313…> wrote:

[Repost from 21 Mar 2011: note suggested patches]

Hi all,

I am using Matplotlib 1.0.1 and am seeing weird behaviour with mplot3d and

ticker formatters, and I think I need to submit a patch to axes3d, but am not

sure how much it will break, because format_zdata() and format_coord() look to

be defined inconsistently.

When trying to rotate a plot, which was created including the following

commands,

ax.plot(x[0,alow:atop],x[1,alow:atop],x[2,alow:atop],c=rgb.tolist())

ax.w_xaxis.set_major_locator(tic.LinearLocator(3))

ax.w_xaxis.set_major_formatter(tic.FormatStrFormatter(’’))

ax.w_yaxis.set_major_locator(tic.LinearLocator(3))

ax.w_yaxis.set_major_formatter(tic.FormatStrFormatter(’’))

ax.w_zaxis.set_major_locator(tic.LinearLocator(3))

ax.w_zaxis.set_major_formatter(tic.FormatStrFormatter(’’))

plt.draw()

I received the following backtrace and error message:

/usr/lib64/python2.6/site-packages/matplotlib/backend_bases.pyc in

mouse_move(self, event)

2393 if event.inaxes and event.inaxes.get_navigate():

2394

-> 2395 try: s = event.inaxes.format_coord(event.xdata,

event.ydata)

2396 except ValueError: pass

2397 except OverflowError: pass

/usr/lib64/python2.6/site-packages/mpl_toolkits/mplot3d/axes3d.pyc in

format_coord(self, xd, yd)

474

475         xs = self.format_xdata(x)

–> 476 ys = self.format_ydata(y)

477         zs = self.format_ydata(z)

478         return 'x=%s, y=%s, z=%s' % (xs, ys, zs)

/usr/lib64/python2.6/site-packages/mpl_toolkits/mplot3d/axes3d.pyc in

format_ydata(self, y)

424         except TypeError:

425             fmt = self.w_yaxis.get_major_formatter()

–> 426 return sensible_format_data(fmt, y)

427

428     def format_zdata(self, z):

/usr/lib64/python2.6/site-packages/mpl_toolkits/mplot3d/axes3d.pyc in

sensible_format_data(self, value)

 26     if abs(value) > 1e4 or abs(value)<1e-3:

 27         s = '%1.4e' % value

—> 28 return self._formatSciNotation(s)

 29     else:

 30         return '%4.3f' % value

AttributeError: FormatStrFormatter instance has no attribute

‘_formatSciNotation’


I am using FormatStrFormatter(’’) to try to obtain an empty tick.

[1] It looks like sensible_format_data() assumes that self is class

ScalarFormatter(Formatter), since this is the only ticker Formatter that has

attribute _formatSciNotation(s). As far as I can tell, this means that

sensible_format_data(fmt,y) should only be called if fmt has class

ScalarFormatter(Formatter).

[2] In axes3d.py, I see:

def format_zdata(self, z):

    """

    Return z string formatted.  This function will use the attribute

    self.fmt_zdata if it is callable, else will fall back on the yaxis

    major formatter

    """

    try:

        return self.fmt_zdata(z)

    except (AttributeError, TypeError):

        fmt = self.w_zaxis.get_major_formatter()

        return sensible_format_data(fmt, z)

To me, it looks like the call to sensible_format_data(fmt, z) is wrong. The

same error occurs in format_xdata() and format_ydata(). So I would like to

submit a patch for format_xdata(), format_ydata(), format_zdata(), e.g.:

def format_zdata(self, z):

    """

    Return z string formatted.  This function will use the attribute

    self.fmt_zdata if it is callable, else will fall back on the zaxis

    major formatter

    """

    try:

        return self.fmt_zdata(z)

    except (AttributeError, TypeError):

        fmt = self.w_zaxis.get_major_formatter()

        try:

            return sensible_format_data(fmt, z)

        except (AttributeError, TypeError):

            return format_data(fmt, z)

[3] But I am also worried about the comment "else will fall back on the yaxis

major formatter". Shouldn’t this say “the zaxis major formatter”, since that

is what the code does? Or should the code use the yaxis major formatter, since

that is what the documentation says? The documentation

http://matplotlib.github.com/mpl_toolkits/mplot3d/api.html agrees with the

comment and not with the code.

[4] In def format_coord(self, xd, yd), I see

    xs = self.format_xdata(x)

    ys = self.format_ydata(y)

    zs = self.format_ydata(z)

Why doesn’t the last line say

    zs = self.format_zdata(z)

?

Best, Paul