1d plotting¶
ProPlot adds new features to various Axes
plotting methods thanks to a set of wrapper functions. These features are a strict superset of the existing matplotlib API – if you want, you can use plotting commands exactly as you always have.
Standardized input¶
The standardize_1d
wrapper is used to standardize the positional arguments for several different plotting functions.
standardize_1d
allows you to optionally omit x coordinates (in which case they are inferred from the y coordinates) or pass 2D y coordinate arrays (in which case the plotting method is called with each column of the array).
Pandas and xarray¶
The standardize_1d
wrapper also supports integration with pandas DataFrame
s and xarray DataArray
s. When you pass a DataFrame or DataArray to any plotting command, the x-axis label, y-axis label, legend label, colorbar label, and/or title are configured from the metadata. This restores some of the convenience you get with the builtin pandas and xarray plotting functions. This feature is optional; installation of pandas and xarray are not required.
[1]:
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)
da = xr.DataArray(data, dims=('x', 'cat'), coords={
'x': xr.DataArray(np.linspace(0, 1, 20), dims=('x',), attrs={'long_name': 'distance', 'units': 'km'}),
'cat': xr.DataArray(np.arange(0, 80, 10), dims=('cat',), attrs={'long_name': 'parameter', 'units': 'K'})
}, name='position series')
# DataFrame
ts = pd.date_range('1/1/2000', periods=20)
data = (np.cos(np.linspace(0, 2*np.pi, 20))**4)[:, None] + state.rand(20, 5)**2
df = pd.DataFrame(data, index=ts, columns=['foo', 'bar', 'baz', 'zap', 'baf'])
df.name = 'time series'
df.index.name = 'time (s)'
df.columns.name = 'columns'
[2]:
import proplot as plot
f, axs = plot.subplots(ncols=2, axwidth=2.2, share=0)
axs.format(suptitle='Automatic subplot formatting')
# Plot DataArray
color = plot.shade('light blue', 0.4)
cycle = plot.Cycle(color, fade=90, space='hpl')
axs[0].plot(da, cycle=cycle, lw=3, colorbar='ul', colorbar_kw={'locator': 20})
# Plot Dataframe
color = plot.shade('jade', 0.4)
cycle = plot.Cycle(color, fade=90, space='hpl')
axs[1].plot(df, cycle=cycle, lw=3, legend='uc')
/home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.5.0/lib/python3.7/site-packages/proplot/utils.py:105: ProPlotWarning: Rebuilding font cache.
[2]:
(<matplotlib.lines.Line2D at 0x7fd0bdbeb490>,
<matplotlib.lines.Line2D at 0x7fd0bdb90890>,
<matplotlib.lines.Line2D at 0x7fd0bdb90550>,
<matplotlib.lines.Line2D at 0x7fd0bdb90610>,
<matplotlib.lines.Line2D at 0x7fd0bdb96a90>)
On-the-fly error bars¶
Thanks to the add_errorbars
wrapper, you can add error bars on-the-fly by passing certain keyword args to plot
, scatter
, bar
, barh
, or violinplot
. If you pass 2D arrays to these commands with means=True
or medians=True
, the means or medians of each column are drawn as points, lines, or bars, and error bars are drawn to represent the spread in each column. You can draw both thin “bars” with optional whiskers, and thick “boxes” overlayed on top of these bars. You can also pass error bar coordinates manually with the bardata
and boxdata
keyword args. See add_errorbars
for details.
[3]:
import proplot as plot
import numpy as np
import pandas as pd
plot.rc['title.loc'] = 'uc'
plot.rc['axes.ymargin'] = plot.rc['axes.xmargin'] = 0.05
state = np.random.RandomState(51423)
data = state.rand(20, 8).cumsum(axis=0).cumsum(axis=1)[:, ::-1] \
+ 20*state.normal(size=(20, 8)) + 30
f, axs = plot.subplots(
nrows=3, aspect=1.5, axwidth=3,
share=0, hratios=(2, 1, 1)
)
axs.format(suptitle='Error bars with various plotting commands')
axs[1:].format(xlabel='column number', xticks=1, xgrid=False)
# Asking add_errorbars to calculate bars
ax = axs[0]
obj = ax.barh(data, color='red orange', means=True)
ax.format(title='Column statistics')
ax.format(ylabel='column number', title='Bar plot', ygrid=False)
# Showing a standard deviation range instead of percentile range
ax = axs[1]
ax.scatter(
data, color='k', marker='x', markersize=50, barcolor='gray5',
medians=True, barstd=True, barrange=(-1, 1), barzorder=0, boxes=False, capsize=2
)
ax.format(title='Scatter plot')
# Supplying error bar data manually
ax = axs[2]
boxdata = np.percentile(data, (25, 75), axis=0)
bardata = np.percentile(data, (5, 95), axis=0)
ax.plot(
data.mean(axis=0), boxes=False, marker='o', markersize=5,
edgecolor='k', color='cerulean', boxdata=boxdata, bardata=bardata
)
ax.format(title='Line plot')
plot.rc.reset()
Bar plots¶
bar_wrapper
and cycle_changer
make it easier to generate useful bar plots. You can now pass 2D arrays to bar
or barh
, and columns of data will be grouped or stacked together. Also, bar
and barh
now use default x and y coordinates if you failed to provide them explicitly, just like plot
. See bar_wrapper
for details.
[4]:
import proplot as plot
import numpy as np
import pandas as pd
plot.rc.titleloc = 'uc'
plot.rc.margin = 0.05
f, axs = plot.subplots(nrows=2, aspect=2, axwidth=3.5, share=0, hratios=(3, 2))
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')
)
# Side-by-side bars
ax = axs[0]
obj = ax.bar(
data, cycle='Reds', colorbar='ul',
edgecolor='red9', colorbar_kw={'frameon': False}
)
ax.format(
xlocator=1, xminorlocator=0.5, ytickminor=False,
title='Side-by-side', suptitle='Bar plot wrapper demo'
)
# Stacked bars
ax = axs[1]
obj = ax.barh(
data.iloc[::-1, :], cycle='Blues',
legend='ur', edgecolor='blue9', stacked=True
)
ax.format(title='Stacked')
axs.format(grid=False)
plot.rc.reset()
Area plots¶
To make area plots, use the convenient fill_between
aliases area
and areax
. These are wrapped with fill_between_wrapper
and fill_betweenx_wrapper
.
The fill_between
wrappers enable “stacking” successive columns of a 2D input array like in pandas
. They also add a new negpos
keyword for creating area plots that change color when the fill boundaries cross each other. The most common use case for this is highlighting the negative and positive areas underneath a line, as shown below.
[5]:
import proplot as plot
import numpy as np
plot.rc.margin = 0
f, axs = plot.subplots(array=[[1, 2], [3, 3]], hratios=(1, 0.8), share=0)
axs.format(xlabel='xlabel', ylabel='ylabel', suptitle='Area plot demo')
state = np.random.RandomState(51423)
data = state.rand(5, 3).cumsum(axis=0)
cycle = ('gray3', 'gray5', 'gray7')
# Overlaid and stacked area patches
ax = axs[0]
ax.area(
np.arange(5), data, data + state.rand(5)[:, None], cycle=cycle, alpha=0.5,
legend='uc', legend_kw={'center': True, 'ncols': 2, 'labels': ['z', 'y', 'qqqq']},
)
ax.format(title='Fill between columns')
ax = axs[1]
ax.area(
np.arange(5), data, stacked=True, cycle=cycle, alpha=0.8,
legend='ul', legend_kw={'center': True, 'ncols': 2, 'labels': ['z', 'y', 'qqqq']},
)
ax.format(title='Stack between columns')
# Positive and negative color area patches
ax = axs[2]
data = 5*(state.rand(20)-0.5)
ax.area(data, negpos=True, negcolor='blue7', poscolor='red7')
ax.format(title='Negative and positive data', xlabel='xlabel', ylabel='ylabel')
axs.format(grid=False)
plot.rc.reset()
Box plots and violin plots¶
boxplot
and violinplot
are now wrapped with boxplot_wrapper
, violinplot_wrapper
, and cycle_changer
, making it much easier to plot distributions of data with aesthetically pleasing default settings and automatic axis labeling.
[6]:
import proplot as plot
import numpy as np
import pandas as pd
N = 500
state = np.random.RandomState(51423)
f, axs = plot.subplots(ncols=2)
data = state.normal(size=(N, 5)) + 2*(state.rand(N, 5)-0.5)*np.arange(5)
data = pd.DataFrame(
data,
columns=pd.Index(['a', 'b', 'c', 'd', 'e'], name='xlabel')
)
axs.format(
ymargin=0.1, xmargin=0.1, grid=False,
suptitle='Boxes and violins demo'
)
# Box plots
ax = axs[0]
obj1 = ax.boxplot(
data, lw=0.7, marker='x', fillcolor='gray5',
medianlw=1, mediancolor='k'
)
ax.format(title='Box plots', titleloc='uc')
# Violin plots
ax = axs[1]
obj2 = ax.violinplot(
data, lw=0.7, fillcolor='gray7',
points=500, bw_method=0.3, means=True
)
ax.format(title='Violin plots', titleloc='uc')
/home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.5.0/lib/python3.7/site-packages/proplot/utils.py:135: UserWarning: Got conflicting or duplicate keyword args: {'lw': 0.7, 'linewidth': 0.7}. Using the first one.
f'Got conflicting or duplicate keyword args: {kwargs}. '
/home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.5.0/lib/python3.7/site-packages/proplot/utils.py:135: UserWarning: Got conflicting or duplicate keyword args: {'lw': 0.7, 'linewidth': 0.7}. Using the first one.
f'Got conflicting or duplicate keyword args: {kwargs}. '
Parametric plots¶
You can draw “parametric” plots in ProPlot using the parametric
method or by passing the cmap
keyword argument to plot
. “Parametric” plots are line collections that map individual line segments to individual colors, where each color represents a parametric coordinate (e.g. time). Parametric coordinates must be passed with the values
keyword argument. See parametric
for details.
[7]:
import proplot as plot
import numpy as np
N = 50
cmap = 'IceFire'
values = np.linspace(-N/2, N/2, N)
f, axs = plot.subplots(
share=0, ncols=2, wratios=(2, 1),
axwidth='7cm', aspect=(2, 1)
)
axs.format(suptitle='Parametric plots demo')
# Parametric line with smooth gradations
ax = axs[0]
state = np.random.RandomState(51423)
m = ax.plot((state.rand(N) - 0.5).cumsum(), state.rand(N),
cmap=cmap, values=values, lw=7, extend='both')
ax.format(
xlabel='xlabel', ylabel='ylabel',
title='Line with smooth gradations'
)
ax.format(xlim=(-1, 5), ylim=(-0.2, 1.2))
ax.colorbar(m, loc='b', label='parametric coordinate', locator=5)
# Parametric line with stepped gradations
N = 12
ax = axs[1]
values = np.linspace(-N/2, N/2, N + 1)
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)
m = ax.plot(x, y, values=values, linewidth=15, interp=False, cmap=cmap)
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=f'parametric coordinate')
[7]:
<matplotlib.colorbar.Colorbar at 0x7fd0ba741090>
Scatter plots¶
Thanks to scatter_wrapper
and cycle_changer
, scatter
now accepts 2D arrays, just like plot
. Also, successive calls to scatter
now use the property cycler properties (e.g. color
, marker
, and markersize
), and to reduce confusion, scatter
optionally accepts keywords that look like plot
keywords (e.g. color
instead of c
and markersize
instead of s
). You can also pass colormaps to scatter
just as with matplotlib.
We are also considering supporting 2D array input and property cycle iteration for more obscure matplotlib plotting commands like stem
, step
, vlines
, and hlines
. Stay tuned!
[8]:
import proplot as plot
import numpy as np
import pandas as pd
f, axs = plot.subplots(ncols=2, share=1)
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'))
# Scatter plot with property cycler
ax = axs[0]
ax.format(title='Extra prop cycle properties', suptitle='Scatter plot demo')
obj = ax.scatter(
x, data, legend='ul', cycle='warm', legend_kw={'ncols': 2},
cycle_kw={'marker': ['x', 'o', 'x', 'o'], 'markersize': [5, 10, 20, 30]}
)
# Scatter plot with colormap
ax = axs[1]
ax.format(title='Scatter plot with cmap')
data = state.rand(2, 100)
obj = ax.scatter(
*data, color=data.sum(axis=0), size=state.rand(100), smin=3, smax=30,
marker='o', cmap='plum', colorbar='lr', vmin=0, vmax=2,
colorbar_kw={'label': 'label', 'locator':0.5}
)
axs.format(xlabel='xlabel', ylabel='ylabel')