Skip to content

plt.bar() fails on 2D arrays with singleton dimensions. error message is unhelpful. #10733

Closed as not planned

Description

@HoniSanders

Bug report

Bug summary
plt.bar() only accept 1D arrays, and does not accept 2D arrays with a dimension that has length 1. E.g., does not accept arrays with shape (3,1). Unfortunately, the traceback is unhelpful.

Code for reproduction

from matplotlib import pyplot as plt
import numpy as np
plt.bar(np.zeros((2,)),np.zeros((2,1)))

Actual outcome

  File "<stdin>", line 1, in <module>
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/pyplot.py", line 2648, in bar
    ret = ax.bar(*args, **kwargs)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/__init__.py", line 1717, in inner
    return func(ax, *args, **kwargs)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/axes/_axes.py", line 2087, in bar
    label='_nolegend_',
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/patches.py", line 659, in __init__
    Patch.__init__(self, **kwargs)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/patches.py", line 103, in __init__
    self.set_linewidth(linewidth)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/patches.py", line 351, in set_linewidth
    self._linewidth = float(w)
TypeError: only length-1 arrays can be converted to Python scalars

Expected outcome

plt.bar() should be able to figure out that a 1D vector and a 2D array with a singleton dimension are equivalent. This can be done by applying squeeze if necessary. In any case if plt.bar() is going to fail unintuitively on this, it should test to see if the input is a 2D array, and provide an informative error message.

Matplotlib version

  • Operating system: windows, linux, and mac
  • Matplotlib version: most recent
  • Matplotlib backend (print(matplotlib.get_backend())):
  • Python version: 2.7 and 3.6
  • Jupyter version (if applicable):
  • Other libraries:

This code works with matplotlib version 1.4.2 and does not work with 2.1.1. Other pyplot functions do not exhibit this behavior. E.g., plt.plot(np.zeros((2,1)),np.zeros((2,1))) works fine.

Activity

  1. jklymak commented on Mar 9, 2018

    @jklymak
    Member

    ping @anntzer You last touched the logic for linewidth here:

    x, height, width, y, linewidth = np.broadcast_arrays(

    @bringingheavendown I don't know that we can catch every input and give nice error messages, but I agree that there is no good reason for bar to be more persnickety than plot...

  2. added this to the milestone on Mar 9, 2018
  3. added
    API: consistencyConsistency of the matplotlib API, including naming, behavior, defaults, …
    on Mar 9, 2018
  4. anntzer commented on Mar 9, 2018

    @anntzer
    Contributor

    Actually I think that came in with @tacaswell's #9110 which added broadcasting to linewidths, which is apparently(?) unneeded (it's handled below with itertools.cycle(np.atleast_1d(linewidths))).

  5. HoniSanders commented on Mar 19, 2018

    @HoniSanders
    Author

    More info on comparing behavior of plt.plot() to plt.bar()

    >>> import numpy as np
    >>> from matplotlib import pyplot as plt
    >>> a=np.zeros((2,))
    >>> b=np.ones((2,))
    >>> c=np.zeros((2,1))
    >>> d=np.ones((2,1))
    >>> e=np.zeros((1,2))
    >>> f=np.ones((1,2))
    >>> plt.plot(a,b)
    [<matplotlib.lines.Line2D object at 0x1032a5f10>]
    >>> plt.figure()
    <matplotlib.figure.Figure object at 0x1026f9c90>
    >>> plt.plot(c,d)
    [<matplotlib.lines.Line2D object at 0x10330a450>]
    >>> plt.plot(e,f)
    [<matplotlib.lines.Line2D object at 0x1032b7a50>, <matplotlib.lines.Line2D object at 0x10330a9d0>]
    >>> plt.bar(a,b)
    <Container object of 2 artists>
    >>> plt.bar(c,d)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/pyplot.py", line 2648, in bar
        ret = ax.bar(*args, **kwargs)
      File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/__init__.py", line 1717, in inner
        return func(ax, *args, **kwargs)
      File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/axes/_axes.py", line 2087, in bar
        label='_nolegend_',
      File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/patches.py", line 659, in __init__
        Patch.__init__(self, **kwargs)
      File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/patches.py", line 103, in __init__
        self.set_linewidth(linewidth)
      File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/patches.py", line 351, in set_linewidth
        self._linewidth = float(w)
    TypeError: cannot convert to a float; scalar object is not a number
    >>> plt.bar(e,f)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/pyplot.py", line 2648, in bar
        ret = ax.bar(*args, **kwargs)
      File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/__init__.py", line 1717, in inner
        return func(ax, *args, **kwargs)
      File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/axes/_axes.py", line 2087, in bar
        label='_nolegend_',
      File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/patches.py", line 659, in __init__
        Patch.__init__(self, **kwargs)
      File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/patches.py", line 103, in __init__
        self.set_linewidth(linewidth)
      File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/patches.py", line 351, in set_linewidth
        self._linewidth = float(w)
    TypeError: only length-1 arrays can be converted to Python scalars

    I.e., plt.plot() treats rows of the inputs as different readings from a single line, and columns as different lines. Therefore, it treats a single-column 2D array the same as a 1D vector.

    plt.bar() seems to be missing the functionality of creating a grouped bar chart if passed multi-column data, as would be analogous to plt.plot(), see #10610

    In any case, the key line in plt.plot() is line 658 of https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/lines.py
    self._xy = np.column_stack(np.broadcast_arrays(x, y)).astype(float)

  6. efiring commented on Mar 20, 2018

    @efiring
    Member

    Moving grouped-bar support from hist to bar (#10610) seems like the right way to solve this. At present, bar specifies 1-D inputs. I'm leery of putting squeeze calls all over the place; I would rather leave it to the user to provide inputs with the appropriate dimensions.

  7. HoniSanders commented on Mar 20, 2018

    @HoniSanders
    Author

    Ok, but there should at least be a reasonable error message if plt.bar() is going to have unintuitive and undocumented requirements on its inputs.

  8. jklymak commented on Mar 20, 2018

    @jklymak
    Member

    We don't always add strict argument checking. We added some recently, but that was for a case where the error didn't trigger until draw() was called.

    In this case, its pretty clear that a call to bar is causing an error in bar due to bad argument sizes. So I don't think this is as striong a case, and I'm 50/50 on whether it needs an argument check. But if someone wanted to put in a PR that did this in a way that didn't break other things and wasn't a lot of extra messy code, I think that would have a decent chance of being accepted.

  9. efiring commented on Mar 20, 2018

    @efiring
    Member

    The requirement is documented: "scalar or sequence of scalars" means no more than 1-D.
    https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.bar.html#matplotlib-axes-axes-bar

  10. modified the milestones: v3.0, on Jul 26, 2018
  11. xapple commented on Jun 19, 2019

    @xapple

    I had this error on a python installation that had matplotlib version 3.1.0

    I was able to solve the problem by executing the following command:

    python3 -m pip install --user matplotlib==3.0.3
    

    It seems like this was fixed but then a regression was introduced.

  12. modified the milestones: , needs sorting on Oct 6, 2022
  13. github-actions commented on Oct 9, 2023

    @github-actions

    This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!

  14. added
    status: closed as inactiveIssues closed by the "Stale" Github Action. Please comment on any you think should still be open.
    on Nov 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    API: argument checkingValidation of acceptable argument valuesAPI: consistencyConsistency of the matplotlib API, including naming, behavior, defaults, …status: closed as inactiveIssues closed by the "Stale" Github Action. Please comment on any you think should still be open.status: inactiveMarked by the “Stale” Github Action

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions