Plotting 1D data

ProPlot adds new features to various Axes plotting methods using a set of wrapper functions. When a plotting method like plot is “wrapped” by one of these functions, it accepts the same parameters as the wrapper. These features are a strict superset of the matplotlib API. This section documents the features added by wrapper functions to 1D plotting commands like plot, scatter, bar, and barh.

Property cycles

It is often desirable to use different property cycles for different axes or different plot elements. To enable this, the cycle_changer adds the cycle and cycle_kw to the 1D plotting methods. These arguments are passed to the Cycle constructor function, and the resulting property cycle is used to style the input data. ProPlot iterates through property cycle properties when (1) making multiple calls to a plotting command, or (2) plotting successive columns of 2-dimensional input data. For more information on property cycles, see the color cycles section and this matplotlib tutorial.

  1. [1]:
  1. import proplot as plot
  2. import numpy as np
  3. state = np.random.RandomState(51423)
  4. data1 = state.rand(6, 4)
  5. data2 = state.rand(6, 4) * 1.5
  6. with plot.rc.context({'lines.linewidth': 3}):
  7. fig, axs = plot.subplots(ncols=2)
  8. axs.format(xlabel='xlabel', ylabel='ylabel', suptitle='Local property cycles demo')
  9. # Property cycles specific to datasets
  10. axs[0].plot(data1, cycle='Reds', cycle_kw={'left': 0.3})
  11. axs[0].plot(data2, cycle='Blues', cycle_kw={'left': 0.3})
  12. axs[1].plot(
  13. data1 * data2,
  14. cycle='black',
  15. cycle_kw={'linestyle': ('-', '--', '-.', ':')}
  16. )
  1. /home/docs/checkouts/readthedocs.org/user_builds/proplot/conda/v0.6.4/lib/python3.8/site-packages/proplot/config.py:1454: ProPlotWarning: Rebuilding font cache.

_images/1dplots_2_1.svg

Standardized arguments

The standardize_1d wrapper is used to standardize positional arguments across all 1D plotting methods. standardize_1d allows you to optionally omit x coordinates, in which case they are inferred from the data. It also permits passing 2D y coordinate arrays to any plotting method, in which case the plotting method is called for each column of the array.

  1. [2]:
  1. import proplot as plot
  2. import numpy as np
  3. # Figure and sample data
  4. N = 5
  5. state = np.random.RandomState(51423)
  6. with plot.rc.context({'axes.prop_cycle': plot.Cycle('Grays', N=N, left=0.3)}):
  7. fig, axs = plot.subplots(ncols=2, share=False)
  8. x = np.linspace(-5, 5, N)
  9. y = state.rand(N, 5)
  10. axs.format(xlabel='xlabel', ylabel='ylabel')
  11. axs.format(suptitle='Standardized arguments demonstration')
  12. # Plot by passing both x and y coordinates
  13. ax = axs[0]
  14. ax.area(x, -1 * y / N, stacked=True)
  15. ax.bar(x, y, linewidth=0, alpha=1, width=0.8)
  16. ax.plot(x, y + 1, linewidth=2)
  17. ax.scatter(x, y + 2, marker='s', markersize=5**2)
  18. ax.format(title='Manual x coordinates')
  19. # Plot by passing just y coordinates
  20. # Default x coordinates are inferred from DataFrame,
  21. # inferred from DataArray, or set to np.arange(0, y.shape[0])
  22. ax = axs[1]
  23. ax.area(-1 * y / N, stacked=True)
  24. ax.bar(y, linewidth=0, alpha=1)
  25. ax.plot(y + 1, linewidth=2)
  26. ax.scatter(y + 2, marker='s', markersize=5**2)
  27. ax.format(title='Auto x coordinates')

_images/1dplots_4_0.svg

Pandas and xarray integration

The standardize_1d wrapper integrates 1D plotting methods with pandas DataFrames and xarray DataArrays. 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. [3]:
  1. import xarray as xr
  2. import numpy as np
  3. import pandas as pd
  4. # DataArray
  5. state = np.random.RandomState(51423)
  6. data = (
  7. np.sin(np.linspace(0, 2 * np.pi, 20))[:, None]
  8. + state.rand(20, 8).cumsum(axis=1)
  9. )
  10. da = xr.DataArray(data, dims=('x', 'cat'), coords={
  11. 'x': xr.DataArray(
  12. np.linspace(0, 1, 20),
  13. dims=('x',),
  14. attrs={'long_name': 'distance', 'units': 'km'}
  15. ),
  16. 'cat': xr.DataArray(
  17. np.arange(0, 80, 10),
  18. dims=('cat',),
  19. attrs={'long_name': 'parameter', 'units': 'K'}
  20. )
  21. }, name='position series')
  22. # DataFrame
  23. data = (
  24. (np.cos(np.linspace(0, 2 * np.pi, 20))**4)[:, None] + state.rand(20, 5)**2
  25. )
  26. ts = pd.date_range('1/1/2000', periods=20)
  27. df = pd.DataFrame(data, index=ts, columns=['foo', 'bar', 'baz', 'zap', 'baf'])
  28. df.name = 'time series'
  29. df.index.name = 'time (s)'
  30. df.columns.name = 'columns'
  1. [4]:
  1. import proplot as plot
  2. fig, axs = plot.subplots(ncols=2, axwidth=2.2, share=0)
  3. axs.format(suptitle='Automatic subplot formatting')
  4. # Plot DataArray
  5. cycle = plot.Cycle('dark blue', fade=90, space='hpl', N=da.shape[1])
  6. axs[0].scatter(da, cycle=cycle, lw=3, colorbar='ul', colorbar_kw={'locator': 20})
  7. # Plot Dataframe
  8. cycle = plot.Cycle('dark green', fade=90, space='hpl', N=df.shape[1])
  9. axs[1].plot(df, cycle=cycle, lw=3, legend='uc')
  1. [4]:
  1. (<matplotlib.lines.Line2D at 0x7f5a3318e100>,
  2. <matplotlib.lines.Line2D at 0x7f5a3318e4f0>,
  3. <matplotlib.lines.Line2D at 0x7f5a3318e880>,
  4. <matplotlib.lines.Line2D at 0x7f5a3318ec10>,
  5. <matplotlib.lines.Line2D at 0x7f5a331d7790>)

_images/1dplots_7_1.svg

Shading and error bars

The indicate_error wrapper lets you draw error bars and error shading on-the-fly by passing certain keyword arguments to plot, scatter, bar, or barh.

If you pass 2D arrays to these methods with means=True or medians=True, the means or medians of each column are drawn as points, lines, or bars, and error bars or shading is drawn to represent the spread of the distribution for each column. You can also specify the error bounds manually with the bardata, boxdata, shadedata, and fadedata keywords. indicate_error can draw thin error bars with optional whiskers, thick “boxes” overlayed on top of these bars (think of this as a miniature boxplot), and up to 2 regions of shading. See indicate_error for details.

  1. [5]:
  1. import proplot as plot
  2. import numpy as np
  3. import pandas as pd
  4. plot.rc['title.loc'] = 'uc'
  5. # Generate sample data
  6. state = np.random.RandomState(51423)
  7. data = state.rand(20, 8).cumsum(axis=0).cumsum(axis=1)[:, ::-1]
  8. data = data + 20 * state.normal(size=(20, 8)) + 30
  9. data = pd.DataFrame(data, columns=np.arange(0, 16, 2))
  10. data.name = 'variable'
  11. # Generate figure
  12. fig, axs = plot.subplots(
  13. nrows=3, aspect=1.5, axwidth=4,
  14. share=0, hratios=(2, 1, 1)
  15. )
  16. axs.format(suptitle='Indicating error bounds with various plotting commands')
  17. axs[1:].format(xlabel='column number', xticks=1, xgrid=False)
  18. # Automatically calculate medians and display default percentile range
  19. ax = axs[0]
  20. obj = ax.barh(
  21. data,
  22. color='light red', legend=True,
  23. medians=True, boxpctiles=True, barpctiles=(5, 95),
  24. )
  25. ax.format(title='Column statistics')
  26. ax.format(ylabel='column number', title='Bar plot', ygrid=False)
  27. # Automatically calculate means and display requested standard deviation range
  28. ax = axs[1]
  29. ax.scatter(
  30. data,
  31. color='denim', marker='x', markersize=8**2, linewidth=0.8,
  32. means=True, shadestds=(-1, 1), shadelabel=True, legend='ll',
  33. )
  34. ax.format(title='Scatter plot')
  35. # Manually supply error bar data and legend labels
  36. ax = axs[2]
  37. means = data.mean(axis=0)
  38. means.name = data.name
  39. shadedata = np.percentile(data, (25, 75), axis=0) # dark shading
  40. fadedata = np.percentile(data, (5, 95), axis=0) # light shading
  41. ax.plot(
  42. means,
  43. shadedata=shadedata, fadedata=fadedata,
  44. shadelabel='50% CI', fadelabel='90% CI',
  45. color='ocean blue', barzorder=0, boxmarker=False, legend='ll',
  46. )
  47. ax.format(title='Line plot')
  48. plot.rc.reset()

_images/1dplots_9_0.svg

Bar plots and area plots

The bar and barh methods are wrapped by bar_wrapper, cycle_changer, and standardize_1d. You can now group or stack columns of data by passing 2D arrays to bar or barh, just like in pandas, or use different colors for negative and positive bars by passing negpos=True. Also, bar and barh now employ “default” x coordinates if you failed to provide them explicitly.

To make filled “area” plots, use the new area and areax methods. These are alises for fill_between and fill_betweenx, which are wrapped by fill_between_wrapper and fill_betweenx_wrapper. You can now stack or overlay columns of data by passing 2D arrays to area and areax, just like in pandas. You can also now draw area plots that change color when the fill boundaries cross each other by passing negpos=True to fill_between. The most common use case for this is highlighting negative and positive areas with different colors, as shown below.

  1. [6]:
  1. import proplot as plot
  2. import numpy as np
  3. import pandas as pd
  4. state = np.random.RandomState(51423)
  5. data = state.rand(5, 5).cumsum(axis=0).cumsum(axis=1)[:, ::-1]
  6. data = pd.DataFrame(
  7. data, columns=pd.Index(np.arange(1, 6), name='column'),
  8. index=pd.Index(['a', 'b', 'c', 'd', 'e'], name='row idx')
  9. )
  10. # Generate figure
  11. plot.rc.titleloc = 'uc'
  12. fig, axs = plot.subplots(nrows=2, aspect=2, axwidth=4.8, share=0, hratios=(3, 2))
  13. # Side-by-side bars
  14. ax = axs[0]
  15. obj = ax.bar(
  16. data, cycle='Reds', colorbar='ul',
  17. edgecolor='red9', colorbar_kw={'frameon': False}
  18. )
  19. ax.format(
  20. xlocator=1, xminorlocator=0.5, ytickminor=False,
  21. title='Side-by-side', suptitle='Bar plot demo'
  22. )
  23. # Stacked bars
  24. ax = axs[1]
  25. obj = ax.barh(
  26. data.iloc[::-1, :], cycle='Blues',
  27. legend='ur', edgecolor='blue9', stacked=True
  28. )
  29. ax.format(title='Stacked')
  30. axs.format(grid=False)
  31. plot.rc.reset()

_images/1dplots_11_0.svg

  1. [7]:
  1. import proplot as plot
  2. import numpy as np
  3. state = np.random.RandomState(51423)
  4. data = state.rand(5, 3).cumsum(axis=0)
  5. cycle = ('gray3', 'gray5', 'gray7')
  6. # Generate figure
  7. fig, axs = plot.subplots(ncols=2, axwidth=2.3, share=0)
  8. axs.format(grid=False, xlabel='xlabel', ylabel='ylabel', suptitle='Area plot demo')
  9. # Overlaid area patches
  10. ax = axs[0]
  11. ax.area(
  12. np.arange(5), data, data + state.rand(5)[:, None], cycle=cycle, alpha=0.7,
  13. legend='uc', legend_kw={'center': True, 'ncols': 2, 'labels': ['z', 'y', 'qqqq']},
  14. )
  15. ax.format(title='Fill between columns')
  16. # Stacked area patches
  17. ax = axs[1]
  18. ax.area(
  19. np.arange(5), data, stacked=True, cycle=cycle, alpha=0.8,
  20. legend='ul', legend_kw={'center': True, 'ncols': 2, 'labels': ['z', 'y', 'qqqq']},
  21. )
  22. ax.format(title='Stack between columns')

_images/1dplots_12_0.svg

  1. [8]:
  1. import proplot as plot
  2. import numpy as np
  3. state = np.random.RandomState(51423)
  4. data = 4 * (state.rand(50) - 0.5)
  5. # Generate figure
  6. fig, axs = plot.subplots(nrows=2, width=5, aspect=2)
  7. axs.format(
  8. xmargin=0, xlabel='xlabel', ylabel='ylabel', grid=True,
  9. suptitle='Positive and negative colors demo',
  10. )
  11. axs.axhline(0, color='k', linewidth=1) # zero line
  12. # Bar plot
  13. axs[0].bar(data, width=1, edgecolor='none', negpos=True)
  14. axs[0].format(title='Bar plot')
  15. # Area plot
  16. axs[1].area(data, negpos=True)
  17. axs[1].format(title='Area plot')

_images/1dplots_13_0.svg

Box plots and violin plots

The boxplot and violinplot methods are now wrapped with boxplot_wrapper, violinplot_wrapper, cycle_changer, and standardize_1d. These wrappers add some useful options and apply aesthetically pleasing default settings. They also automatically apply axis labels based on the DataFrame column labels or the input x coordinate labels.

  1. [9]:
  1. import proplot as plot
  2. import numpy as np
  3. import pandas as pd
  4. # Generate sample data
  5. N = 500
  6. state = np.random.RandomState(51423)
  7. data = state.normal(size=(N, 5)) + 2 * (state.rand(N, 5) - 0.5) * np.arange(5)
  8. data = pd.DataFrame(
  9. data,
  10. columns=pd.Index(['a', 'b', 'c', 'd', 'e'], name='xlabel')
  11. )
  12. # Generate figure
  13. fig, axs = plot.subplots(ncols=2, axwidth=2.5)
  14. axs.format(grid=False, suptitle='Boxes and violins demo')
  15. # Box plots
  16. ax = axs[0]
  17. obj1 = ax.boxplot(
  18. data, lw=0.7, marker='x', fillcolor='gray5',
  19. medianlw=1, mediancolor='k'
  20. )
  21. ax.format(title='Box plots', titleloc='uc')
  22. # Violin plots
  23. ax = axs[1]
  24. obj2 = ax.violinplot(
  25. data, lw=0.7, fillcolor='gray7',
  26. points=500, bw_method=0.3, means=True
  27. )
  28. ax.format(title='Violin plots', titleloc='uc')

_images/1dplots_15_0.svg

Parametric plots

To make “parametric” plots, use the new parametric method. 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.

  1. [10]:
  1. import proplot as plot
  2. import numpy as np
  3. fig, axs = plot.subplots(
  4. share=0, ncols=2, wratios=(2, 1),
  5. width='16cm', aspect=(2, 1)
  6. )
  7. axs.format(suptitle='Parametric plots demo')
  8. cmap = 'IceFire'
  9. # Parametric line with smooth gradations
  10. ax = axs[0]
  11. state = np.random.RandomState(51423)
  12. N = 50
  13. x = (state.rand(N) - 0.52).cumsum()
  14. y = state.rand(N)
  15. c = np.linspace(-N / 2, N / 2, N) # color values
  16. m = ax.parametric(
  17. x, y, c, cmap=cmap, lw=7, interp=5, capstyle='round', joinstyle='round'
  18. )
  19. ax.format(xlabel='xlabel', ylabel='ylabel', title='Line with smooth gradations')
  20. ax.colorbar(m, loc='b', label='parametric coordinate', locator=5)
  21. # Parametric line with stepped gradations
  22. N = 12
  23. ax = axs[1]
  24. radii = np.linspace(1, 0.2, N + 1)
  25. angles = np.linspace(0, 4 * np.pi, N + 1)
  26. x = radii * np.cos(1.4 * angles)
  27. y = radii * np.sin(1.4 * angles)
  28. c = np.linspace(-N / 2, N / 2, N + 1)
  29. m = ax.parametric(x, y, c, cmap=cmap, lw=15)
  30. ax.format(
  31. xlim=(-1, 1), ylim=(-1, 1), title='Step gradations',
  32. xlabel='cosine angle', ylabel='sine angle'
  33. )
  34. ax.colorbar(m, loc='b', maxn=10, label='parametric coordinate')
  1. [10]:
  1. <matplotlib.colorbar.Colorbar at 0x7f5a32e3d760>

_images/1dplots_17_1.svg

Other plotting methods

The scatter method is now wrapped by scatter_wrapper, cycle_changer, and standardize_1d. This means that 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 scatter now optionally accepts keywords that look like plot keywords (e.g. color instead of c and markersize instead of s).

ProPlot also supports property cycling for step plots and wraps the vlines and hlines methods with vlines_wrapper and hlines_wrapper, which adds the ability to use different colors for “negative” and “positive” lines.

  1. [11]:
  1. import proplot as plot
  2. import numpy as np
  3. import pandas as pd
  4. fig, axs = plot.subplots(ncols=2, share=1)
  5. state = np.random.RandomState(51423)
  6. x = (state.rand(20) - 0).cumsum()
  7. data = (state.rand(20, 4) - 0.5).cumsum(axis=0)
  8. data = pd.DataFrame(data, columns=pd.Index(['a', 'b', 'c', 'd'], name='label'))
  9. # Scatter plot with property cycler
  10. ax = axs[0]
  11. ax.format(suptitle='Scatter plot demo', title='Extra prop cycle properties')
  12. obj = ax.scatter(
  13. x, data, legend='ul', cycle='Set2', legend_kw={'ncols': 2},
  14. cycle_kw={'marker': ['x', 'o', 'x', 'o'], 'markersize': [5, 10, 20, 30]}
  15. )
  16. # Scatter plot with colormap
  17. ax = axs[1]
  18. ax.format(title='Scatter plot with cmap')
  19. data = state.rand(2, 100)
  20. obj = ax.scatter(
  21. *data, color=data.sum(axis=0), size=state.rand(100), smin=3, smax=30,
  22. marker='o', cmap='dark red', colorbar='lr', vmin=0, vmax=2,
  23. colorbar_kw={'label': 'label', 'locator': 0.5}
  24. )
  25. axs.format(xlabel='xlabel', ylabel='ylabel')

_images/1dplots_19_0.svg

  1. [12]:
  1. import proplot as plot
  2. import numpy as np
  3. state = np.random.RandomState(51423)
  4. fig, axs = plot.subplots(ncols=2, nrows=2, share=0)
  5. axs.format(suptitle='Line plots demo', xlabel='xlabel', ylabel='ylabel')
  6. # Step
  7. ax = axs[0]
  8. data = state.rand(20, 4).cumsum(axis=1).cumsum(axis=0)
  9. cycle = ('blue7', 'gray5', 'red7', 'gray5')
  10. ax.step(data, cycle=cycle, labels=list('ABCD'), legend='ul', legend_kw={'ncol': 2})
  11. ax.format(title='Step plot')
  12. # Stems
  13. ax = axs[1]
  14. data = state.rand(20)
  15. ax.stem(data, linefmt='k-')
  16. ax.format(title='Stem plot')
  17. # Vertical lines
  18. gray = 'gray7'
  19. data = state.rand(20) - 0.5
  20. ax = axs[2]
  21. ax.area(data, color=gray, alpha=0.2)
  22. ax.vlines(data, negpos=True, linewidth=2)
  23. ax.format(title='Vertical lines')
  24. # Horizontal lines
  25. ax = axs[3]
  26. ax.areax(data, color=gray, alpha=0.2)
  27. ax.hlines(data, negpos=True, linewidth=2)
  28. ax.format(title='Horizontal lines')

_images/1dplots_20_0.svg