7979 If the latter, the levels should be monotonically increasing or
8080 decreasing (note that decreasing levels will only work with ``pcolor``
8181 plots, not ``contour`` plots). Default is :rc:`image.levels`.
82-
83- Since this function also wraps `~matplotlib.axes.Axes.pcolor` and
84- `~matplotlib.axes.Axes.pcolormesh`, this means they now
85- accept the `levels` keyword arg. You can now discretize your
86- colors in a ``pcolor`` plot just like with ``contourf``.
82+ Note this means you can now discretize your colormap colors in a
83+ ``pcolor`` plot just like with ``contourf``.
8784values : int or list of float, optional
8885 The number of level centers, or a list of level centers. If provided,
8986 levels are inferred using `~proplot.utils.edges`. This will override
@@ -1757,23 +1754,28 @@ def cycle_changer(
17571754 proplot.constructor.Cycle
17581755 proplot.constructor.Colors
17591756 """
1760- # Parse input
1761- cycle_kw = cycle_kw or {}
1762- legend_kw = legend_kw or {}
1763- colorbar_kw = colorbar_kw or {}
1764-
1765- # Test input
1757+ # Parse input args
17661758 # NOTE: Requires standardize_1d wrapper before reaching this. Also note
17671759 # that the 'x' coordinates are sometimes ignored below.
17681760 name = func .__name__
17691761 if not args :
17701762 return func (self , * args , ** kwargs )
1771- barh = name == 'bar' and kwargs .get ('orientation' , None ) == 'horizontal'
1763+ barh = stacked = False
1764+ if name in ('bar' , 'fill_between' , 'fill_betweenx' ):
1765+ stacked = kwargs .pop ('stacked' , False )
1766+ if name in ('bar' ,):
1767+ barh = kwargs .get ('orientation' , None ) == 'horizontal'
1768+ if barh :
1769+ kwargs .setdefault ('x' , 0 )
17721770 x , y , * args = args
1773- if len (args ) >= 1 and 'fill_between' in name :
1771+ ys = (y ,)
1772+ if len (args ) >= 1 and name in ('fill_between' , 'fill_betweenx' ):
17741773 ys , args = (y , args [0 ]), args [1 :]
1775- else :
1776- ys = (y ,)
1774+ if name in ('pie' ,): # add x coordinates as default pie chart labels
1775+ kwargs ['labels' ] = _not_none (labels , x ) # TODO: move to pie wrapper?
1776+ cycle_kw = cycle_kw or {}
1777+ legend_kw = legend_kw or {}
1778+ colorbar_kw = colorbar_kw or {}
17771779
17781780 # Determine and temporarily set cycler
17791781 # NOTE: Axes cycle has no getter, only set_prop_cycle, which sets a
@@ -1850,13 +1852,25 @@ def cycle_changer(
18501852 if labels is None or isinstance (labels , str ):
18511853 labels = [labels ] * ncols
18521854
1853- # Handle stacked bar plots
1854- stacked = kwargs .pop ('stacked' , False )
1855- if name in ('bar' ,):
1856- width = kwargs .pop ('width' , 0.8 )
1857- kwargs ['height' if barh else 'width' ] = (
1858- width if stacked else width / ncols
1859- )
1855+ # Get step size for bar plots
1856+ # WARNING: This will fail for non-numeric non-datetime64 singleton
1857+ # datatypes but this is good enough for vast majority of most cases.
1858+ if name in ('bar' ,) and not stacked :
1859+ x_test = np .atleast_1d (_to_ndarray (x ))
1860+ if len (x_test ) >= 2 :
1861+ x_step = x_test [1 :] - x_test [:- 1 ]
1862+ x_step = np .concatenate ((x_step , x_step [- 1 :]))
1863+ elif x_test .dtype == np .datetime64 :
1864+ x_step = np .timedelta64 (1 , 'D' )
1865+ else :
1866+ x_step = np .array (0.5 )
1867+ if np .issubdtype (x_test .dtype , np .datetime64 ):
1868+ # Avoid integer timedelta truncation
1869+ x_step = x_step .astype ('timedelta64[ns]' )
1870+ key = 'height' if barh else 'width'
1871+ width = kwargs .pop (key , 0.8 )
1872+ width = width * x_step / ncols
1873+ kwargs [key ] = width
18601874
18611875 # Plot susccessive columns
18621876 objs = []
@@ -1876,31 +1890,12 @@ def cycle_changer(
18761890 key = 'edgecolors'
18771891 kw [key ] = value
18781892
1879- # Add x coordinates as pi chart labels by default
1880- if name in ('pie' ,):
1881- kw ['labels' ] = _not_none (labels , x ) # TODO: move to pie wrapper?
1882-
1883- # Step size for grouped bar plots
1884- # WARNING: This will fail for non-numeric non-datetime64 singleton
1885- # datatypes but this is good enough for vast majority of most cases.
1886- x_test = np .atleast_1d (_to_ndarray (x ))
1887- if len (x_test ) >= 2 :
1888- x_step = x_test [1 :] - x_test [:- 1 ]
1889- x_step = np .concatenate ((x_step , x_step [- 1 :]))
1890- elif x_test .dtype == np .datetime64 :
1891- x_step = np .timedelta64 (1 , 'D' )
1892- else :
1893- x_step = np .array (0.5 )
1894- if np .issubdtype (x_test .dtype , np .datetime64 ):
1895- x_step = x_step .astype ('timedelta64[ns]' ) # avoid int timedelta truncation
1896-
18971893 # Get x coordinates for bar plot
18981894 x_col , y_first = x , ys [0 ] # samples
18991895 if name in ('bar' ,): # adjust
19001896 if not stacked :
1901- scale = i - 0.5 * (ncols - 1 ) # offset from true coordinate
1902- scale = width * scale / ncols
1903- x_col = x + x_step * scale
1897+ offset = width * (i - 0.5 * (ncols - 1 ))
1898+ x_col = x + offset
19041899 elif stacked and y_first .ndim > 1 :
19051900 key = 'x' if barh else 'bottom'
19061901 kw [key ] = _to_indexer (y_first )[:, :i ].sum (axis = 1 )
@@ -1915,7 +1910,7 @@ def cycle_changer(
19151910 # WARNING: If stacked=True then we always *ignore* second
19161911 # argument passed to fill_between. Warning should be issued
19171912 # by fill_between_wrapper in this case.
1918- if stacked and 'fill_between' in name :
1913+ if stacked and name in ( 'fill_between' , 'fill_betweenx' ) :
19191914 ys_col = tuple (
19201915 y_first if y_first .ndim == 1
19211916 else _to_indexer (y_first )[:, :ii ].sum (axis = 1 )
@@ -1949,15 +1944,12 @@ def cycle_changer(
19491944
19501945 # Build coordinate arguments
19511946 x_ys_col = ()
1952- if barh : # special, use kwargs only!
1947+ if barh : # special case , use kwargs only!
19531948 kw .update ({'bottom' : x_col , 'width' : ys_col [0 ]})
1954- kw .setdefault ('x' , kwargs .get ('bottom' , 0 )) # required
19551949 elif name in ('pie' , 'hist' , 'boxplot' , 'violinplot' ):
19561950 x_ys_col = ys_col
19571951 else : # has x-coordinates, and maybe more than one y
19581952 x_ys_col = (x_col , * ys_col )
1959-
1960- # Call plotting function
19611953 obj = func (self , * x_ys_col , * args , ** kw )
19621954 if isinstance (obj , (list , tuple )) and len (obj ) == 1 :
19631955 obj = obj [0 ]
0 commit comments