# Plotting 1D data¶

ProPlot adds several new features to matplotlib’s plotting commands using the intermediate PlotAxes subclass. These additions represent a strict superset of matplotlib – if you are not interested, you can use the plotting commands just like you always have. This section documents the features added for 1D plotting commands like plot, scatter, and bar.

## Standardized arguments¶

Input arguments passed to 1D plotting commands are now uniformly standardized. For each command, you can optionally omit the dependent variable coordinates, in which case they are inferred from the data (see xarray and pandas integration), or pass 2D dependent or independent variable coordinates, in which case the plotting command is called for each column of the 2D array(s). If coordinates are string labels, they are converted to indices and tick labels using FixedLocator and IndexFormatter. All positional arguments can also be optionally specified as keyword arguments (see the individual command documentation).

Note

By default, when just the x or y axis was explicitly fixed by set_xlim or set_ylim (or, equivalently, by passing xlim or ylim to proplot.axes.CartesianAxes.format), ProPlot ignores the out of bounds data when determining the other axis limits. This can be useful if you wish to restrict the view within a large dataset. To disable this feature, pass inbounds=False to the plotting command or set rc['axes.inbounds'] to False (see also the rc['cmap.inbounds'] setting and the user guide).

[1]:

import proplot as pplt
import numpy as np

N = 5
state = np.random.RandomState(51423)
with pplt.rc.context({'axes.prop_cycle': pplt.Cycle('Grays', N=N, left=0.3)}):
# Sample data
x = np.linspace(-5, 5, N)
y = state.rand(N, 5)
fig = pplt.figure(share=False)

# Plot by passing both x and y coordinates
ax = fig.subplot(121)
ax.area(x, -1 * y / N, stack=True)
ax.bar(x, y, linewidth=0, alpha=1, width=0.8)
ax.plot(x, y + 1, linewidth=2)
ax.scatter(x, y + 2, marker='s', markersize=5**2)
ax.format(title='Manual x coordinates')

# Plot by passing just y coordinates
# Default x coordinates are inferred from DataFrame,
# inferred from DataArray, or set to np.arange(0, y.shape[0])
ax = fig.subplot(122)
ax.area(-1 * y / N, stack=True)
ax.bar(y, linewidth=0, alpha=1)
ax.plot(y + 1, linewidth=2)
ax.scatter(y + 2, marker='s', markersize=5**2)
ax.format(title='Auto x coordinates')
fig.format(xlabel='xlabel', ylabel='ylabel')
fig.format(suptitle='Standardized input demonstration')

/home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.8.0/lib/python3.8/site-packages/proplot/__init__.py:46: ProPlotWarning: Rebuilding font cache.
register_fonts(default=True)


## Pandas and xarray integration¶

The PlotAxes plotting commands are seamlessly integrated with pandas and xarray. If you omit dependent variable coordinates, the plotting command tries to infer them from the pandas.DataFrame or xarray.DataArray. If you did not explicitly set the x or y axis label or legend or colorbar title, the plotting command tries to retrieve them from the pandas.DataFrame or xarray.DataArray. You can also pass a Dataset, DataFrame, or dict to any plotting command using the data keyword, then pass string keys as the data arguments rather than arrays (for example, ax.plot('y', data=dataset) is translated to ax.plot(dataset['y'])). Finally, if you pass pint.Quantitys or xarray.DataArrays containing pint.Quantitys to a plotting command, ProPlot will automatically call setup_matplotlib and apply the unit string formatted as rc.unitformat for the default content labels.

These features restore some of the convenience you get with the builtin pandas and xarray plotting functions. They are also optional – installation of pandas and xarray are not required. All of these features can be disabled by setting rc.autoformat to False or by passing autoformat=False to any plotting command.

[2]:

import xarray as xr
import numpy as np
import pandas as pd

# DataArray
state = np.random.RandomState(51423)
data = (
np.sin(np.linspace(0, 2 * np.pi, 20))[:, None]
+ state.rand(20, 8).cumsum(axis=1)
)
coords = {
'x': xr.DataArray(
np.linspace(0, 1, 20),
dims=('x',),
attrs={'long_name': 'distance', 'units': 'km'}
),
'num': xr.DataArray(
np.arange(0, 80, 10),
dims=('num',),
attrs={'long_name': 'parameter'}
)
}
da = xr.DataArray(
data, dims=('x', 'num'), coords=coords, name='energy', attrs={'units': 'kJ'}
)

# DataFrame
data = (
(np.cos(np.linspace(0, 2 * np.pi, 20))**4)[:, None] + state.rand(20, 5) ** 2
)
ts = pd.date_range('1/1/2000', periods=20)
df = pd.DataFrame(data, index=ts, columns=['foo', 'bar', 'baz', 'zap', 'baf'])
df.name = 'data'
df.index.name = 'date'
df.columns.name = 'category'

[3]:

import proplot as pplt
fig = pplt.figure(share=False, refwidth=2.2)
fig.format(suptitle='Automatic subplot formatting')

# Plot DataArray
cycle = pplt.Cycle('dark blue', space='hpl', N=da.shape[1])
ax = fig.subplot(121)
ax.scatter(da, cycle=cycle, lw=3, colorbar='ul', colorbar_kw={'locator': 20})

# Plot Dataframe
cycle = pplt.Cycle('dark green', space='hpl', N=df.shape[1])
ax = fig.subplot(122)
ax.plot(df, cycle=cycle, lw=3, legend='uc')

[3]:

[<matplotlib.lines.Line2D at 0x7fba035bb1f0>,
<matplotlib.lines.Line2D at 0x7fba035bb400>,
<matplotlib.lines.Line2D at 0x7fba035bb7f0>,
<matplotlib.lines.Line2D at 0x7fba035bbb50>,
<matplotlib.lines.Line2D at 0x7fba035bbee0>]


## Property cycles¶

It is often useful to create on-the-fly property cycles and use different property cycles for different plot elements. You can create and apply property cycles on-the-fly using the cycle and cycle_kw keywords, available with most 1D plotting commands. cycle and cycle_kw are passed to the Cycle constructor function, and the resulting property cycle is used for the plot. You can specify cycle once with 2D input data (in which case each column is plotted in succession according to the property cycle) or call a plotting command multiple times with the same cycle argument each time (the property cycle is not reset). You can also disable property cycling with cycle=False, cycle='none', or cycle=() and re-enable the default property cycle with cycle=True. For more information on property cycling, see the color cycles section and this matplotlib tutorial.

[4]:

import proplot as pplt
import numpy as np

# Sample data
M, N = 9, 4
state = np.random.RandomState(51423)
data1 = state.rand(M, N)
data2 = state.rand(M, N) * 1.5

with pplt.rc.context({'lines.linewidth': 3}):
# Use property cycle for columns of 2D input data
fig = pplt.figure(refwidth=2.2, span=False)
ax = fig.subplot(121)
ax.plot(
data1 * data2,
cycle='black',
cycle_kw={'ls': ('-', '--', '-.', ':')}
)

# Use property cycle with successive plot() calls
ax = fig.subplot(122)
for i in range(data1.shape[1]):
ax.plot(data1[:, i], cycle='Reds', cycle_kw={'N': N, 'left': 0.3})
for i in range(data1.shape[1]):
ax.plot(data2[:, i], cycle='Blues', cycle_kw={'N': N, 'left': 0.3})
fig.format(xlabel='xlabel', ylabel='ylabel', suptitle='Local property cycles demo')


## Line plots¶

Line plots can be drawn with plot or plotx (or their aliases, line or linex). For the x commands, positional arguments are interpreted as x coordinates or (y, x) pairs. This is analogous to barh and areax. Also, the default x bounds for lines drawn with plot and y bounds for lines drawn with plotx are now “sticky”, i.e. there is no padding between the lines and axes edges by default.

Step and stem plots can be drawn with step, stepx, stem, and stemx. Plots of parallel vertical and horizontal lines can be drawn with vlines and hlines. You can have different colors for “negative” and “positive” lines using negpos=True, negcolor=color, and poscolor=color (the default colors are rc.negcolor = 'blue7' and rc.poscolor = 'red7').

[5]:

import proplot as pplt
import numpy as np
state = np.random.RandomState(51423)
gs = pplt.GridSpec(nrows=3, ncols=2)
fig = pplt.figure(refwidth=2.2, span=False, share='labels')

# Vertical vs. horizontal
data = (state.rand(10, 5) - 0.5).cumsum(axis=0)
ax = fig.subplot(gs[0])
ax.format(title='Dependent x-axis')
ax.line(data, lw=2.5, cycle='seaborn')
ax = fig.subplot(gs[1])
ax.format(title='Dependent y-axis')
ax.linex(data, lw=2.5, cycle='seaborn')

# Vertical lines
gray = 'gray7'
data = state.rand(20) - 0.5
ax = fig.subplot(gs[2])
ax.area(data, color=gray, alpha=0.2)
ax.vlines(data, negpos=True, lw=2)
ax.format(title='Vertical lines')

# Horizontal lines
ax = fig.subplot(gs[3])
ax.areax(data, color=gray, alpha=0.2)
ax.hlines(data, negpos=True, lw=2)
ax.format(title='Horizontal lines')

# Step
ax = fig.subplot(gs[4])
data = state.rand(20, 4).cumsum(axis=1).cumsum(axis=0)
cycle = ('gray6', 'blue7', 'red7', 'gray4')
ax.step(data, cycle=cycle, labels=list('ABCD'), legend='ul', legend_kw={'ncol': 2})
ax.format(title='Step plot')

# Stems
ax = fig.subplot(gs[5])
data = state.rand(20)
ax.stem(data)
ax.format(title='Stem plot')
fig.format(suptitle='Line plots demo', xlabel='xlabel', ylabel='ylabel')


## Scatter plots¶

The scatter command now permits omitting x coordinates and accepts 2D y coordinates, just like plot. As with plotx, the scatterx command is just like scatter, except positional arguments are interpreted as x coordinates and (y, x) pairs. scatter also now accepts keywords that look like plot keywords (e.g., color instead of c and markersize instead of s). This way, scatter can be used simply to “plot markers, not lines” without changing the input arguments relative to plot.

The property cycler used by 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 Colormap and 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).

[6]:

import proplot as pplt
import numpy as np
import pandas as pd

# Sample data
state = np.random.RandomState(51423)
x = (state.rand(20) - 0).cumsum()
data = (state.rand(20, 4) - 0.5).cumsum(axis=0)
data = pd.DataFrame(data, columns=pd.Index(['a', 'b', 'c', 'd'], name='label'))

# Figure
gs = pplt.GridSpec(ncols=2, nrows=2)
fig = pplt.figure(refwidth=2.2, share='labels', span=False)

# Vertical vs. horizontal
ax = fig.subplot(gs[0])
ax.format(title='Dependent x-axis')
ax.scatter(data, cycle='538')
ax = fig.subplot(gs[1])
ax.format(title='Dependent y-axis')
ax.scatterx(data, cycle='538')

# Scatter plot with property cycler
ax = fig.subplot(gs[2])
ax.format(title='With property cycle')
obj = ax.scatter(
x, data, legend='ul', legend_kw={'ncols': 2},
cycle='Set2', cycle_kw={'m': ['x', 'o', 'x', 'o'], 'ms': [5, 10, 20, 30]}
)

# Scatter plot with colormap
ax = fig.subplot(gs[3])
ax.format(title='With colormap')
data = state.rand(2, 100)
obj = ax.scatter(
*data,
s=state.rand(100), smin=3, smax=60, marker='o',
c=data.sum(axis=0), cmap='maroon',
colorbar='lr', colorbar_kw={'label': 'label'},
)
fig.format(suptitle='Scatter plot demo', xlabel='xlabel', ylabel='ylabel')


## Bar plots and area plots¶

The bar and barh commands apply default x or y coordinates if you failed to provide them explicitly and can group or stack columns of data if you pass 2D arrays instead of 1D arrays – just like pandas. Similarly, fill_between and fill_betweenx also apply default x or y coordinates if you failed to provide them explicitly, and can stack or overlay columns of data by passing 2D arrays instead of 1D arrays. You can also use the shorthands area and areax instead of fill_between.

For both bar and area plots, you can have different colors for “negative” and “positive” regions using negpos=True, negcolor=color, and poscolor=color (the default colors are rc.negcolor = 'blue7' and rc.poscolor = 'red7'). Also, the default x bounds for shading drawn with area and y bounds for shading drawn with areax is now “sticky”, i.e. there is no padding between the shading and axes edges by default.

[7]:

import proplot as pplt
import numpy as np
import pandas as pd

# Sample data
state = np.random.RandomState(51423)
data = state.rand(5, 5).cumsum(axis=0).cumsum(axis=1)[:, ::-1]
data = pd.DataFrame(
data, columns=pd.Index(np.arange(1, 6), name='column'),
index=pd.Index(['a', 'b', 'c', 'd', 'e'], name='row idx')
)

# Figure
pplt.rc.abc = 'a.'
pplt.rc.titleloc = 'l'
gs = pplt.GridSpec(nrows=2, hratios=(3, 2))
fig = pplt.figure(refaspect=2, refwidth=4.8, share=False)

# Side-by-side bars
ax = fig.subplot(gs[0])
obj = ax.bar(
data, cycle='Reds', edgecolor='red9', colorbar='ul', colorbar_kw={'frameon': False}
)
ax.format(xlocator=1, xminorlocator=0.5, ytickminor=False, title='Side-by-side')

# Stacked bars
ax = fig.subplot(gs[1])
obj = ax.barh(
data.iloc[::-1, :], cycle='Blues', edgecolor='blue9', legend='lr', stack=True,
)
ax.format(title='Stacked')
fig.format(grid=False, suptitle='Bar plot demo')

[8]:

import proplot as pplt
import numpy as np

# Sample data
state = np.random.RandomState(51423)
data = state.rand(5, 3).cumsum(axis=0)
cycle = ('gray3', 'gray5', 'gray7')

# Figure
fig = pplt.figure(refwidth=2.3, share=False)

# Overlaid area patches
ax = fig.subplot(121)
ax.area(
np.arange(5), data, data + state.rand(5)[:, None], cycle=cycle, alpha=0.7,
legend='uc', legend_kw={'center': True, 'ncols': 2, 'labels': ['z', 'y', 'qqqq']},
)
ax.format(title='Fill between columns')

# Stacked area patches
ax = fig.subplot(122)
ax.area(
np.arange(5), data, stack=True, cycle=cycle, alpha=0.8,
legend='ul', legend_kw={'center': True, 'ncols': 2, 'labels': ['z', 'y', 'qqqq']},
)
ax.format(title='Stack between columns')
fig.format(grid=False, xlabel='xlabel', ylabel='ylabel', suptitle='Area plot demo')

[9]:

import proplot as pplt
import numpy as np

# Sample data
state = np.random.RandomState(51423)
data = 4 * (state.rand(40) - 0.5)

# Figure
fig, axs = pplt.subplots(nrows=2, refaspect=2, figwidth=5)
axs.format(
xmargin=0, xlabel='xlabel', ylabel='ylabel', grid=True,
suptitle='Positive and negative colors demo',
)
for ax in axs:
ax.axhline(0, color='k', linewidth=1)  # zero line

# Bar plot
ax = axs[0]
ax.bar(data, width=1, negpos=True)
ax.format(title='Bar plot')

# Area plot
ax = axs[1]
ax.area(data, negpos=True, lw=0.5, edgecolor='k')
ax.format(title='Area plot')

# Reset title styles changed above
pplt.rc.reset()


Error bars and error shading can be quickly added on-the-fly to line, linex (equivalently, plot, plotx), scatter, scatterx. bar, and barh plots using any of several keyword arguments.

If you pass 2D arrays to these commands with mean=True or median=True, the means or medians of each column are drawn as lines, points, or bars, while error bars or error shading indicates the spread of the distribution for each column. Invalid data is ignored. You can also specify the error bounds manually with the bardata, boxdata, shadedata, and fadedata keywords. These commands can draw and style thin error bars (the bar keywords), thick “boxes” overlaid on top of these bars (the box keywords; think of them as miniature boxplots), a transparent shading region (the shade keywords), and a more transparent secondary shading region (the fade keywords). See the documentation on the plotting commands for details.

[10]:

import numpy as np
import pandas as pd

# Sample data
# Each column represents a distribution
state = np.random.RandomState(51423)
data = state.rand(20, 8).cumsum(axis=0).cumsum(axis=1)[:, ::-1]
data = data + 20 * state.normal(size=(20, 8)) + 30
data = pd.DataFrame(data, columns=np.arange(0, 16, 2))
data.columns.name = 'column number'
data.name = 'variable'

# Calculate error data
# Passed to 'errdata' in the 3rd subplot example
means = data.mean(axis=0)
means.name = data.name  # copy name for formatting

[11]:

import proplot as pplt
import numpy as np

# Loop through "vertical" and "horizontal" versions
varray = [[1], [2], [3]]
harray = [[1, 1], [2, 3], [2, 3]]
for orientation, array in zip(('horizontal', 'vertical'), (harray, varray)):
# Figure
fig = pplt.figure(refwidth=4, refaspect=1.5, share=False)
axs = fig.subplots(array, hratios=(2, 1, 1))
axs.format(abc='A.', suptitle=f'Indicating {orientation} error bounds')

# Medians and percentile ranges
ax = axs[0]
kw = dict(
color='light red', legend=True,
median=True, barpctile=90, boxpctile=True,
# median=True, barpctile=(5, 95), boxpctile=(25, 75)  # equivalent
)
if orientation == 'horizontal':
ax.barh(data, **kw)
else:
ax.bar(data, **kw)
ax.format(title='Bar plot')

# Means and standard deviation range
ax = axs[1]
kw = dict(
color='denim', marker='x', markersize=8**2, linewidth=0.8,
# mean=True, shadestd=(-1, 1)  # equivalent
)
if orientation == 'horizontal':
ax.scatterx(data, legend='b', legend_kw={'ncol': 1}, **kw)
else:
ax.scatter(data, legend='ll', **kw)
ax.format(title='Marker plot')

# User-defined error bars
ax = axs[2]
kw = dict(
color='ocean blue', barzorder=0, boxmarker=False,
)
if orientation == 'horizontal':
ax.linex(means, legend='b', legend_kw={'ncol': 1}, **kw)
else:
ax.line(means, legend='ll', **kw)
ax.format(title='Line plot')


## Histogram plots¶

Vertical and horizontal histograms can be drawn with hist and histh. As with the other plotting commands, multiple histograms can be drawn by passing 2D arrays instead of 1D arrays, and the color cycle used to color histograms can be changed on-the-fly using the cycle and cycle_kw keywords. Likewise, 2D histograms can be drawn with the hist2d hexbin commands, and their colormaps can be changed on-the-fly with the cmap and cmap_kw keywords (see the 2d plotting section). Marginal distributions for the 2D histograms can be added using panel axes. In the future, ProPlot may include options for drawing “smooth” kernel density estimations with these commands.

[12]:

import proplot as pplt
import numpy as np

# Sample data
M, N = 300, 3
state = np.random.RandomState(51423)
x = state.normal(size=(M, N)) + state.rand(M)[:, None] * np.arange(N) + 2 * np.arange(N)

# Sample overlayed histograms
fig, ax = pplt.subplots(refwidth=4, refaspect=(3, 2))
ax.format(suptitle='Overlaid histograms', xlabel='distribution', ylabel='count')
res = ax.hist(
x, pplt.arange(-3, 8, 0.2), alpha=0.7,
cycle=('indigo9', 'gray3', 'red9'), labels=list('abc'), legend='ul',
)

[13]:

import proplot as pplt
import numpy as np

# Sample data
N = 500
state = np.random.RandomState(51423)
x = state.normal(size=(N,))
y = state.normal(size=(N,))
bins = pplt.arange(-3, 3, 0.25)

# Histogram with marginal distributions
fig, axs = pplt.subplots(ncols=2, refwidth=2.3)
axs.format(
abc='A.', abcloc='l', titleabove=True,
ylabel='y axis', suptitle='Histograms with marginal distributions'
)
colors = ('indigo9', 'red9')
titles = ('Group 1', 'Group 2')
for ax, which, color, title in zip(axs, 'lr', colors, titles):
ax.hist2d(
x, y, bins, vmin=0, vmax=10, levels=50,
cmap=color, colorbar='b', colorbar_kw={'label': 'count'}
)
color = pplt.scale_luminance(color, 1.5)  # histogram colors
px = ax.panel(which, space=0)
px.hist(y, bins, lw=0, color=color, vert=False)  # or orientation='horizontal'
px.format(grid=False, xlocator=[], xreverse=(which == 'l'))
px = ax.panel('t', space=0)
px.hist(x, bins, lw=0, color=color)
px.format(grid=False, ylocator=[], title=title, titleloc='l')


## Box plots and violin plots¶

Vertical and horizontal box and violin plots can be drawn using boxplot, violinplot, boxploth, and violinploth (or their new shorthands, box, violin, boxh, and violinh). The ProPlot versions employ aesthetically pleasing defaults and permit flexible configuration using keywords like color, barcolor, and fillcolor. They also automatically apply axis labels based on the DataFrame or DataArray column labels.

[14]:

import proplot as pplt
import numpy as np
import pandas as pd

# Sample data
N = 500
state = np.random.RandomState(51423)
data1 = state.normal(size=(N, 5)) + 2 * (state.rand(N, 5) - 0.5) * np.arange(5)
data1 = pd.DataFrame(data1, columns=pd.Index(list('abcde'), name='label'))
data2 = state.rand(100, 7)
data2 = pd.DataFrame(data2, columns=pd.Index(list('abcdefg'), name='label'))

# Figure
fig, axs = pplt.subplots([[1, 1, 2, 2], [0, 3, 3, 0]], span=False)
axs.format(
abc='A.', titleloc='l', grid=False,
suptitle='Boxes and violins demo'
)

# Box plots
ax = axs[0]
obj1 = ax.boxplot(data1, means=True, marker='x', meancolor='r', fillcolor='gray4')
ax.format(title='Box plots')

# Violin plots
ax = axs[1]
obj2 = ax.violinplot(data1, fillcolor='gray6', means=True, points=100)
ax.format(title='Violin plots')

# Boxes with different colors
ax = axs[2]
colors = pplt.get_colors('pastel2')  # list of colors from the cycle
ax.boxplot(data2, fillcolor=colors, orientation='horizontal')
ax.format(title='Multiple colors', ymargin=0.15)


## Parametric plots¶

To make “parametric” plots, use the new parametric command. Parametric plots are LineCollections that map individual line segments to individual colors, where each segment represents a “parametric” coordinate (e.g., time). The parametric coordinates are specified with the values keyword argument. See parametric for details. As shown below, it is also easy to build colorbars from the LineCollection returned by parametric.

[15]:

import proplot as pplt
import numpy as np
import pandas as pd
gs = pplt.GridSpec(ncols=2, wratios=(2, 1))
fig = pplt.figure(figwidth='16cm', refaspect=(2, 1), share=False)
fig.format(suptitle='Parametric plots demo')
cmap = 'IceFire'

# Sample data
state = np.random.RandomState(51423)
N = 50
x = (state.rand(N) - 0.52).cumsum()
y = state.rand(N)
c = np.linspace(-N / 2, N / 2, N)  # color values
c = pd.Series(c, name='parametric coordinate')

# Parametric line with smooth gradations
ax = fig.subplot(gs[0])
m = ax.parametric(
x, y, c, interp=10, capstyle='round', joinstyle='round',
lw=7, cmap=cmap, colorbar='b', colorbar_kw={'locator': 5}
)
ax.format(xlabel='xlabel', ylabel='ylabel', title='Line with smooth gradations')

# Sample data
N = 12
radii = np.linspace(1, 0.2, N + 1)
angles = np.linspace(0, 4 * np.pi, N + 1)
x = radii * np.cos(1.4 * angles)
y = radii * np.sin(1.4 * angles)
c = np.linspace(-N / 2, N / 2, N + 1)

# Parametric line with stepped gradations
ax = fig.subplot(gs[1])
m = ax.parametric(x, y, c, cmap=cmap, lw=15)
ax.format(
xlim=(-1, 1), ylim=(-1, 1), title='Step gradations',
xlabel='cosine angle', ylabel='sine angle'
)
ax.colorbar(m, loc='b', maxn=10, label='parametric coordinate')

[15]:

<matplotlib.colorbar.Colorbar at 0x7fba027cd460>