Skip to content

Commit

Permalink
Improve docs, implementation of marker size-scaling
Browse files Browse the repository at this point in the history
  • Loading branch information
lukelbd committed Oct 10, 2021
1 parent 30bd0ee commit 87e3333
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 25 deletions.
36 changes: 30 additions & 6 deletions docs/1dplots.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,16 +352,27 @@
# `~proplot.axes.PlotAxes.scatter` also now accepts keywords
# that look like `~proplot.axes.PlotAxes.plot` keywords (e.g., `color` instead of
# `c` and `markersize` instead of `s`). This way, `~proplot.axes.PlotAxes.scatter`
# can be used simply to "plot markers, not lines" without changing the input
# can be used to simply "plot markers, not lines" without changing the input
# arguments relative to `~proplot.axes.PlotAxes.plot`.
#
# The property cycler used by `~proplot.axes.PlotAxes.scatter` can be changed
# using the `cycle` keyword argument, and unlike matplotlib it can include
# properties like `marker` and `markersize`. The colormap `cmap` and normalizer
# `norm` used with the optional `c` color array are now passed through the
# `~proplot.constructor.Colormap` and `~proplot.constructor.Norm` constructor
# functions, and the the `s` marker size array can now be conveniently scaled using
# the keywords `smin` and `smax` (analogous to `vmin` and `vmax` used for colors).
# functions.

# .. important::
#
# In matplotlib, arrays passed to the marker size keyword `s` always represent the
# area in units ``points ** 2``. In proplot, arrays passed to `s` are scaled so
# that the minimum data value has the area ``1`` while the maximum data value
# has the area :rc:`lines.markersize` squared. These minimum and maximum marker
# sizes can also be specified manually with the `smin` and `smax` keywords,
# analogous to `vmin` and `vmax` used to scale the color array `c`. This feature
# can be disabled by passing ``absolute_size=True`` to `~proplot.axes.Axes.scatter`
# or `~proplot.axes.Axes.scatterx`. This is done automatically when `seaborn`_
# calls `~proplot.axes.Axes.scatter` internally.

# %%
import proplot as pplt
Expand Down Expand Up @@ -474,9 +485,7 @@
# The `~proplot.axes.PlotAxes.bar` and `~proplot.axes.PlotAxes.barh` commands
# apply default *x* or *y* coordinates if you failed to provide them explicitly
# and can *group* or *stack* successive columns of data if you pass 2D arrays instead
# of 1D arrays -- just like `pandas`_. The widths of bars are expressed in step
# size-relative units by default, but matplotlib's behavior can be restored by
# passing ``absolute_width=True``. When bars are grouped, their widths are
# of 1D arrays -- just like `pandas`_. When bars are grouped, their widths and
# positions are adjusted according to the number of bars in the group.
#
# The `~proplot.axes.PlotAxes.fill_between` and `~proplot.axes.PlotAxes.fill_betweenx`
Expand All @@ -489,6 +498,21 @@
# bounds for shading drawn with `~proplot.axes.PlotAxes.areax` is now "sticky",
# i.e. there is no padding between the shading and axes edges by default.

# .. important::
#
# In matplotlib, bar widths for horizontal `~matplotlib.axes.Axes.barh` plots
# are expressed with the `height` keyword. In proplot, bar widths are always
# expressed with the `width` keyword. Note that bar widths can also be passed
# as a third positional argument.
#
# Additionally, matplotlib bar widths are always expressed in data units,
# while proplot bar widths are expressed in step size-relative units by
# default. For example, ``width=1`` with a dependent coordinate step
# size of ``2`` fills 100% of the space between each bar rather than 50%. This
# can be disabled by passing ``absolute_width=True`` to `~proplot.axes.Axes.bar`
# or `~proplot.axes.Axes.barh`. This is done automatically when `seaborn`_ calls
# `~proplot.axes.Axes.bar` or `~proplot.axes.Axes.barh` internally.

# %%
import proplot as pplt
import numpy as np
Expand Down
51 changes: 32 additions & 19 deletions proplot/axes/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -625,10 +625,10 @@
Parameters
----------
%(plot.args_1d_{y})s
s, size, ms, markersize : float or sequence of float or unit-spec, optional
The marker area(s). If this is an array matching the shape of `x` and `y`, the
units are scaled by `smin` and `smax`. If this contains unit string(s), it is
processed by `~proplot.utils.units` and represents the width rather than area.
s, size, ms, markersize : float or array-like or unit-spec, optional
The marker size area(s). If this is an array matching the shape of `x` and `y`,
the units are scaled by `smin` and `smax`. If this contains unit string(s), it
is processed by `~proplot.utils.units` and represents the width rather than area.
c, color, colors, mc, markercolor, markercolors, fc, facecolor, facecolors \
: array-like or color-spec, optional
The marker color(s). If this is an array matching the shape of `x` and `y`,
Expand All @@ -639,9 +639,9 @@
if `absolute_size` is ``True``. Default value for `smin` is ``1`` and for
`smax` is the square of :rc:`lines.markersize`.
absolute_size : bool, optional
Whether the marker sizes should be taken to be in physical units or
scaled by `smin` and `smax`. Default is ``True`` if `s` is scalar
and ``False`` if `s` is an array.
Whether `s` should be taken to represent "absolute" marker size areas in units
``points ** 2`` or "relative" marker size areas scaled by `smin` and `smax`.
Default is ``True`` if `s` is scalar and ``False`` if `s` is array-like.
%(plot.vmin_vmax)s
%(plot.args_1d_shared)s
Expand Down Expand Up @@ -1177,6 +1177,25 @@
)


def _default_absolute():
"""
Try to detect `seaborn` calls to `scatter` and `bar` and then automatically
apply `absolute_size` and `absolute_width`.
"""
frame = sys._getframe()
absolute_names = (
'seaborn.distributions',
'seaborn.categorical',
'seaborn.relational',
'seaborn.regression',
)
while frame is not None:
if frame.f_globals.get('__name__', '') in absolute_names:
return True
frame = frame.f_back
return False


def _get_vert(vert=None, orientation=None, **kwargs):
"""
Get the orientation specified as either `vert` or `orientation`. This is
Expand Down Expand Up @@ -3109,7 +3128,11 @@ def _parse_markersize(
s = s.copy()
s.flat[:] = utils.units(s.flat, 'pt')
s = s.astype(np.float) ** 2
absolute_size = _not_none(absolute_size, default_size)
if absolute_size is None:
if _default_absolute():
absolute_size = True
else:
absolute_size = default_size
if not absolute_size or smin is not None or smax is not None:
smin = _not_none(smin, 1)
smax = _not_none(smax, rc['lines.markersize'] ** 2)
Expand Down Expand Up @@ -3326,18 +3349,8 @@ def _apply_bar(
stack = _not_none(stack=stack, stacked=stacked)
xs, hs, kw = self._parse_plot1d(xs, hs, orientation=orientation, **kw)
edgefix_kw = _pop_params(kw, self._apply_edgefix)

# Default absolute width with horrible kludge
# (the faint of heart should look away now).
if absolute_width is None:
frame = sys._getframe()
absolute_width = False
absolute_names = ('seaborn.distributions', 'seaborn.categorical')
while frame is not None:
if frame.f_globals.get('__name__', '') in absolute_names:
absolute_width = True
break
frame = frame.f_back
absolute_width = _default_absolute()

# Call func after converting bar width
b0 = 0
Expand Down

0 comments on commit 87e3333

Please sign in to comment.