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)
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:
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:
get_registered_cmaps(include_reversed=False)
¶
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 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()