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
cycle = plot.Cycle(plot.shade('light blue', 0.4), fade=90, space='hpl')
axs[0].plot(da, cycle=cycle, lw=3, colorbar='ul', colorbar_kw={'locator': 20})
# Plot Dataframe
cycle = plot.Cycle(plot.shade('jade', 0.4), fade=90, space='hpl')
axs[1].plot(df, cycle=cycle, lw=3, legend='uc')
/home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.3.0/lib/python3.7/site-packages/pandas/plotting/_matplotlib/converter.py:103: FutureWarning: Using an implicitly registered datetime converter for a matplotlib plotting method. The converter was registered by pandas on import. Future versions of pandas will require you to explicitly register matplotlib converters.
To register the converters:
>>> from pandas.plotting import register_matplotlib_converters
>>> register_matplotlib_converters()
warnings.warn(msg, FutureWarning)
[2]:
(<matplotlib.lines.Line2D at 0x7fb4cbb97400>,
<matplotlib.lines.Line2D at 0x7fb4cbb52d68>,
<matplotlib.lines.Line2D at 0x7fb4cbb52d30>,
<matplotlib.lines.Line2D at 0x7fb4cbb16588>,
<matplotlib.lines.Line2D at 0x7fb4cbb16828>)
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')
# Asking add_errorbars to calculate bars
ax = axs[0]
obj = ax.barh(data, color='red orange', means=True)
ax.format(title='Column statistics')
# 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)
# 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)
# Formatting
axs[0].format(ylabel='column number', title='Bar plot', ygrid=False)
axs[1].format(title='Scatter plot')
axs[2].format(title='Line plot')
axs[1:].format(xlabel='column number', xticks=1, xgrid=False)
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'))
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')
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')
# 2D arrays
ax = axs[0]
ax.areax(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 colors
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'))
ax = axs[0]
# , boxprops={'color':'C0'})#, labels=data.columns)
obj1 = ax.boxplot(data, lw=0.7, marker='x', fillcolor='gray5',
medianlw=1, mediancolor='k')
ax.format(title='Box plots', titleloc='uc')
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')
axs.format(ymargin=0.1, xmargin=0.1, grid=False,
suptitle='Boxes and violins demo')
/home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.3.0/lib/python3.7/site-packages/proplot/utils.py:102: ProPlotWarning: Got conflicting or duplicate keyword args, using the first one: {'lw': 0.7, 'linewidth': 0.7}
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='6cm', aspect=(2, 1))
axs.format(suptitle='Parametric plots demo')
# 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)
# Step 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 0x7fb4c8930390>
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 demo
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]})
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')