Skip to content

buencolors

Functions

get_palette(name, n=None, as_hex=True, reverse=False)

Get colors from a BuenColors palette.

This is the pythonic equivalent of R's jdb_palette function, providing flexible access to palette colors for discrete categorical data or interpolated continuous colors.

Parameters:

Name Type Description Default
name str

Name of the palette. Must be a valid BuenColors palette name.

required
n int, list[int], or None

Number of colors to return, specific indices to extract, or None for all colors. - If None: returns all colors from the palette - If int: returns first n colors (discrete) or interpolates n colors if n > palette length - If list: returns colors at those specific indices (0-based, like R's c(1,3) but 0-indexed)

None
as_hex bool

If True, return hex color strings. If False, return RGB(A) arrays. Default is True.

True
reverse bool

If True, reverse the color order. Default is False.

False

Returns:

Type Description
list[str] or ndarray

If as_hex is True: list of hex color strings (e.g., ['#FF0000', '#00FF00']) If as_hex is False: numpy array of RGB(A) values with shape (n, 3) or (n, 4)

Raises:

Type Description
ValueError

If palette name is not found or if discrete mode is requested with n > palette length.

Examples:

>>> # Get all colors from a palette
>>> colors = get_palette("Cavalcanti")
>>> print(colors)
['#D8B70A', '#02401B', '#A2A475', '#81A88D', '#972D15']
>>> # Get first 3 colors
>>> colors = get_palette("Cavalcanti", 3)
>>> print(colors)
['#D8B70A', '#02401B', '#A2A475']
>>> # Get specific color indices (like R's c(0, 2, 4) in 0-based indexing)
>>> colors = get_palette("Cavalcanti", [0, 2, 4])
>>> print(colors)
['#D8B70A', '#A2A475', '#972D15']
>>> # Interpolate more colors than palette has
>>> colors = get_palette("Zissou", 21)
>>> len(colors)
21
>>> # Get colors as RGB arrays
>>> colors_rgb = get_palette("berry", as_hex=False)
>>> colors_rgb.shape
(2, 4)
>>> # Reverse the palette
>>> colors = get_palette("solar_flare", reverse=True)
Notes

Unlike the R version which strictly differentiates between discrete and continuous modes, this function automatically interpolates when n > palette length, providing smoother gradients. For strict discrete behavior, ensure n <= len(palette).

display_palette(name, n=None, figsize=(8, 0.5), show_name=True)

Display a BuenColors palette visually.

Parameters:

Name Type Description Default
name str

Name of the palette to display

required
n int

Number of colors to show. If None, shows all colors from the palette.

None
figsize tuple

Figure size as (width, height). Default is (8, 0.5).

(8, 0.5)
show_name bool

If True, display the palette name above the colors. Default is True.

True

Returns:

Type Description
Figure

The matplotlib figure object containing the palette display.

Examples:

>>> # Display a full palette
>>> fig = display_palette("solar_flare")
>>> plt.show()
>>> # Display only first 5 colors
>>> fig = display_palette("Zissou", 5)
>>> plt.show()
>>> # Display interpolated palette
>>> fig = display_palette("berry", 21)
>>> plt.show()
>>> # Larger figure for presentation
>>> fig = display_palette("brewer_spectra", figsize=(12, 1))
>>> plt.show()

list_palettes(pattern=None, categories=False)

List available BuenColors palettes.

Parameters:

Name Type Description Default
pattern str

If provided, only return palettes whose names contain this substring (case-insensitive).

None
categories bool

If True, return palettes grouped by category. Default is False.

False

Returns:

Type Description
list[str] or dict

If categories is False: sorted list of palette names If categories is True: dictionary mapping categories to palette names

Examples:

>>> # List all palettes
>>> palettes = list_palettes()
>>> len(palettes)
117
>>> # Find flame palettes
>>> flame_palettes = list_palettes("flame")
>>> print(flame_palettes)
['flame_artic', 'flame_blind', 'flame_flame', ...]
>>> # List by category
>>> by_category = list_palettes(categories=True)
>>> print(by_category.keys())
dict_keys(['wesanderson', 'special', 'jdb', 'brewer', ...])

get_registered_cmaps(include_reversed=False)

Get list of all registered BuenColors colormap names.

Parameters:

Name Type Description Default
include_reversed bool

If True, include reversed variants (ending in _r). Default is False.

False

Returns:

Type Description
list[str]

Sorted list of registered colormap names.

Examples:

>>> # Get all normal palettes
>>> cmaps = get_registered_cmaps()
>>> # Include reversed variants
>>> all_cmaps = get_registered_cmaps(include_reversed=True)

eject_legend(ax=None)

Eject the legend from the plot area to the right side of the axes.

This is useful for creating publication-quality plots where the legend should not overlap with the data.

Parameters:

Name Type Description Default
ax Axes

The axes to modify. If None, uses the current axes.

None

Returns:

Type Description
None

Examples:

>>> import matplotlib.pyplot as plt
>>> import numpy as np
>>> import buencolors
>>>
>>> # Create a plot with multiple lines
>>> x = np.linspace(0, 10, 100)
>>> plt.plot(x, np.sin(x), label='sin(x)')
>>> plt.plot(x, np.cos(x), label='cos(x)')
>>> plt.plot(x, np.sin(x) * np.cos(x), label='sin(x)cos(x)')
>>>
>>> # Eject legend to the right
>>> buencolors.eject_legend()
>>> plt.show()
>>>
>>> # Or use with a specific axes
>>> fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
>>> ax1.plot(x, np.sin(x), label='sin(x)')
>>> ax1.plot(x, np.cos(x), label='cos(x)')
>>> ax2.plot(x, x**2, label='x²')
>>> ax2.plot(x, x**3, label='x³')
>>>
>>> buencolors.eject_legend(ax1)
>>> buencolors.eject_legend(ax2)
>>> plt.show()

rotate_discrete_xticks(ax=None, rotation=45)

Rotate the x-tick labels for discrete axes with proper alignment.

This will rotate the x-tick labels and ensure that the ending of the labels are directly under the ticks for better readability.

Parameters:

Name Type Description Default
ax Axes

The axes to modify. If None, uses the current axes.

None
rotation float

The angle of rotation for the x-tick labels. Default is 45 degrees.

45

Returns:

Type Description
None

Examples:

>>> import matplotlib.pyplot as plt
>>> import buencolors
>>>
>>> # Bar plot with long category names
>>> categories = ['First Category', 'Second Category', 'Third Category',
...               'Fourth Category', 'Fifth Category']
>>> values = [23, 45, 56, 78, 32]
>>>
>>> plt.bar(categories, values)
>>> buencolors.rotate_discrete_xticks
>>> plt.show()
>>>
>>> # Custom rotation angle
>>> plt.bar(categories, values)
>>> buencolors.rotate_discrete_xticks(rotation=60)
>>> plt.show()
>>>
>>> # Use with specific axes
>>> fig, ax = plt.subplots()
>>> ax.bar(categories, values)
>>> buencolors.rotate_discrete_xticks(ax, rotation=30)
>>> plt.show()

grab_legend(ax=None, remove=True)

Grab the legend from the axes and return it in a new figure for external saving or modification.

This is useful for creating separate legend files for publications or presentations, or for combining legends from multiple plots.

Parameters:

Name Type Description Default
ax Axes

The axes to grab the legend from. If None, uses the current axes.

None
remove bool

If True (default), remove the legend from the original axes after extraction. If False, keep the legend on the original axes.

True

Returns:

Type Description
Figure

A new figure containing only the legend.

Examples:

>>> import matplotlib.pyplot as plt
>>> import numpy as np
>>> import buencolors
>>>
>>> # Create a plot and extract legend (removing it from original)
>>> x = np.linspace(0, 10, 100)
>>> plt.plot(x, np.sin(x), label='sin(x)', color='blue')
>>> plt.plot(x, np.cos(x), label='cos(x)', color='red')
>>> plt.plot(x, np.tan(x), label='tan(x)', color='green')
>>> plt.legend()
>>> plt.ylim(-2, 2)
>>>
>>> # Extract and save legend separately (legend removed from plot)
>>> legend_fig = buencolors.grab_legend()
>>> legend_fig.savefig('legend.pdf', bbox_inches='tight')
>>> plt.savefig('plot.pdf')  # Plot saved without legend
>>> plt.show()
>>>
>>> # Extract legend while keeping it on the original axes
>>> fig, ax = plt.subplots()
>>> ax.scatter([1, 2, 3], [1, 4, 9], label='Data A', color='blue')
>>> ax.scatter([1, 2, 3], [2, 3, 5], label='Data B', color='red')
>>> ax.legend()
>>>
>>> # Keep legend on original plot
>>> legend_fig = buencolors.grab_legend(ax, remove=False)
>>> legend_fig.savefig('my_legend.png', dpi=300, bbox_inches='tight')
>>> plt.show()  # Original plot still has legend
>>>
>>> # Remove legend from original (default behavior)
>>> fig, ax = plt.subplots()
>>> ax.plot(x, np.exp(x), label='exp(x)')
>>> ax.legend()
>>> legend_fig = buencolors.grab_legend(ax, remove=True)
>>> plt.show()  # Original plot has no legend

get_density(x, y, n=200)

Compute the density of points in a grid.

Based on the R implementation. This is useful for coloring scatter plot points by their local density to visualize data concentration.

Credit to Kamil Slowikowski See post: http://slowkow.com/notes/ggplot2-color-by-density/

Parameters:

Name Type Description Default
x array - like

X coordinates

required
y array - like

Y coordinates

required
n int

Number of bins to divide grid. Default is 200. Higher values give more detail but are slower.

200

Returns:

Type Description
ndarray

A vector of densities for plotting

Examples:

>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> import buencolors
>>>
>>> # Create sample data with varying density
>>> x = np.concatenate([
...     np.random.normal(0, 0.1, 10000),
...     np.random.normal(0, 0.1, 1000)
... ])
>>> y = np.concatenate([
...     np.random.normal(0, 0.1, 10000),
...     np.random.normal(0.1, 0.2, 1000)
... ])
>>>
>>> # Compute density for each point
>>> density = buencolors.get_density(x, y)
>>>
>>> # Plot with color mapped to density
>>> plt.scatter(x, y, c=density, cmap='viridis', s=1)
>>> plt.colorbar(label='Point Density')
>>> plt.show()
>>>
>>> # Use with a BuenColors palette
>>> density = buencolors.get_density(x, y, n=300)  # Higher resolution
>>> plt.scatter(x, y, c=density, cmap='solar_flare', s=1, alpha=0.5)
>>> plt.colorbar()
>>> plt.show()

shuffle(x)

shuffle(x: pd.DataFrame) -> pd.DataFrame
shuffle(x: pd.Series) -> pd.Series
shuffle(x: np.ndarray) -> np.ndarray
shuffle(x: ArrayLike) -> list
shuffle(x: list) -> list
shuffle(x: tuple) -> tuple
shuffle(x: anndata.AnnData) -> anndata.AnnData

Shuffle the order of rows/elements to make plots independent of point ordering.

This function accepts various data types and returns a shuffled version while preserving the original type when possible. Useful for preventing visual artifacts caused by data ordering in scatter plots.

Parameters:

Name Type Description Default
x array-like, DataFrame, or Series

Data to shuffle. Can be a list, tuple, numpy array, pandas DataFrame, or pandas Series.

required

Returns:

Type Description
same type as input

Shuffled version of the input data. For DataFrames and Series, the index is reset. For numpy arrays, a shuffled copy is returned. For lists, a shuffled list is returned.

Examples:

>>> import numpy as np
>>> import pandas as pd
>>> import matplotlib.pyplot as plt
>>> import buencolors
>>>
>>> # Shuffle a DataFrame for plotting
>>> df = pd.DataFrame({
...     'x': np.random.randn(1000),
...     'y': np.random.randn(1000),
...     'category': np.random.choice(['A', 'B', 'C'], 1000)
... })
>>>
>>> # Without shuffling, later categories may cover earlier ones
>>> df_shuffled = buencolors.shuffle(df)
>>> colors = {'A': 'red', 'B': 'blue', 'C': 'green'}
>>> df_shuffled['color'] = df_shuffled['category'].map(colors)
>>> plt.scatter(df_shuffled['x'], df_shuffled['y'], c=df_shuffled['color'], alpha=0.5)
>>> plt.show()
>>>
>>> # Shuffle a numpy array
>>> arr = np.random.randn(100, 2)
>>> arr_shuffled = buencolors.shuffle(arr)
>>>
>>> # Shuffle a list
>>> lst = [1, 2, 3, 4, 5]
>>> lst_shuffled = buencolors.shuffle(lst)
Notes

This also supports scanpy AnnData objects by shuffling the obs index when the anndata package is installed.

number_to_color(values, palette, value_range=None, n_bins=None, return_rgb=False)

Map numeric values to colors from a palette.

This is the pythonic equivalent of R's numberToColorVec function, with support for both continuous and discrete (binned) color mapping.

Parameters:

Name Type Description Default
values array - like

Numeric values to map to colors

required
palette str or Colormap

Name of a registered colormap or a Colormap object. Can be any matplotlib colormap or a BuenColors palette name.

required
value_range tuple[float, float]

(min, max) values to clip and normalize. If None, uses the min/max of values. Values outside this range will be clipped to the range bounds.

None
n_bins int

Number of discrete color bins to use. If None (default), uses continuous mapping. If specified, values will be binned into n_bins discrete colors, similar to the R implementation (which uses 100 bins). Common values: 100, 256.

None
return_rgb bool

If True, return RGB arrays instead of hex strings. Default is False.

False

Returns:

Type Description
list[str] or ndarray

If return_rgb is False: list of hex color strings (e.g., '#FF5733') If return_rgb is True: numpy array of shape (n, 4) with RGBA values in [0, 1]

Examples:

>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> import buencolors
>>>
>>> # Basic usage: map values to colors
>>> values = np.random.randn(1000) ** 2
>>> colors = buencolors.number_to_color(values, "viridis")
>>> plt.scatter(np.arange(len(values)), values, c=colors)
>>> plt.show()
>>>
>>> # Use a BuenColors palette
>>> colors = buencolors.number_to_color(values, "solar_flare")
>>> plt.scatter(np.arange(len(values)), values, c=colors)
>>> plt.show()
>>>
>>> # Discrete binned colors (like R implementation)
>>> colors = buencolors.number_to_color(values, "brewer_spectra", n_bins=100)
>>> plt.scatter(np.arange(len(values)), values, c=colors, s=20)
>>> plt.show()
>>>
>>> # With custom value range (capping)
>>> colors = buencolors.number_to_color(values, "plasma", value_range=(0, 2))
>>> plt.scatter(np.arange(len(values)), values, c=colors)
>>> plt.show()
>>>
>>> # Get RGB arrays instead of hex
>>> colors_rgb = buencolors.number_to_color(values[:10], "magma", return_rgb=True)
>>> print(colors_rgb.shape)  # (10, 4) RGBA values
(10, 4)
>>>
>>> # Combined with shuffle for better visualization
>>> import pandas as pd
>>> df = pd.DataFrame({
...     'x': np.random.randn(500),
...     'y': np.random.randn(500),
...     'value': np.random.randn(500) ** 2
... })
>>> df['color'] = buencolors.number_to_color(df['value'], "flame_flame", n_bins=50)
>>> df = buencolors.shuffle(df)
>>> plt.scatter(df['x'], df['y'], c=df['color'], s=30, alpha=0.6)
>>> plt.show()
Notes

The default behavior (n_bins=None) uses continuous color mapping for smoother gradients. Set n_bins=100 to match the R implementation's discrete binning.

For more advanced discrete colormapping, consider using matplotlib's BoundaryNorm.

clean_umap(adata, color, ax=None, axis_len=0.2, thickness=3.0, **kwargs)

Plot a clean UMAP with minimal decorations and custom axis indicators.

Plots a Scanpy UMAP with no borders/ticks, but adds a small 'L' shaped axis indicator in the bottom left corner with arrowheads. The legend is ejected to the right side of the plot. Cells are automatically shuffled to avoid non-random ordering artifacts.

Parameters:

Name Type Description Default
adata AnnData

AnnData object containing the UMAP coordinates

required
color str

Column in adata.obs or gene name to color cells by

required
ax Axes

Existing matplotlib axis. If None, creates a new axis.

None
axis_len float

Length of the custom axis arrows in relative axes coordinates (0-1). Default is 0.2.

0.2
thickness float

Line width of the custom axes. Default is 3.0.

3.0
**kwargs

Additional keyword arguments passed to sc.pl.umap

{}

Returns:

Type Description
Axes

The matplotlib axes containing the UMAP plot

Examples:

>>> import scanpy as sc
>>> import buencolors
>>>
>>> # Load example dataset
>>> adata = sc.datasets.pbmc3k_processed()
>>>
>>> # Plot clean UMAP colored by cell type
>>> buencolors.clean_umap(adata, color='louvain')
>>> plt.show()
>>>
>>> # Customize axis length and thickness
>>> buencolors.clean_umap(adata, color='louvain', axis_len=0.3, thickness=4.0)
>>> plt.show()
>>>
>>> # Use with existing axis and pass additional arguments
>>> fig, ax = plt.subplots(figsize=(8, 6))
>>> buencolors.clean_umap(adata, color='CST3', ax=ax, cmap='viridis')
>>> plt.tight_layout()
>>> plt.show()