Recommended color schemes and how to customize them
When working with sequence data, clear and consistent colors matter because they help readers quickly distinguish states and compare figures. In Sequenzo, this logic is built directly into the SequenceData class. Instead of scattering color choices across different visualization functions, color control starts at the beginning of the workflow.
Each state is assigned a unique color when you create a SequenceData object. Once that mapping is set, downstream plots reuse it automatically. To customize the scheme, define your states, their human-readable labels, and, when needed, a list of custom_colors. SequenceData then acts as the single source for color handling across the analysis.
This design has two advantages:
- It keeps your code clean: you declare your states and their colors only once.
- It guarantees reproducibility: the same state will always appear in the same color across visualizations, no matter which visualization you produce.
As a result, your sequence plots remain both intuitive and coherent, even in complex projects.
Default colors
When you don't specify custom_colors, Sequenzo automatically assigns colors based on the number of states in your data:
- ≤20 states: Uses the Spectral palette (reversed for better readability)
- 21-40 states: Uses the viridis palette (colorful and perceptually uniform)
- >40 states: Uses a combined palette (viridis + Set3 + tab20 for maximum variety)
The palette is applied to all states at once, not in segments. For example, if you have 30 states, all 30 will use the viridis palette, not the first 20 with Spectral and the remaining 10 with viridis.
Practical recommendation: Keep the number of displayed states modest when possible. Around 10 states can already make sequence plots crowded, and more than 15 states usually requires careful grouping or a strong substantive reason.
The default palettes below show the available Spectral, viridis, and combined options, which are color-blind friendly:
Spectral palette (≤20 states, reversed)
Viridis palette (21-40 states)
Combined palette (>40 states)
Missing values
Missing values always get a fixed light gray color automatically:
Generally speaking, you can define states without explicitly considering "Missing" when creating the SequenceData() object.
If missing cells are detected, SequenceData() appends a Missing state and assigns it gray. For instance, if your default colors or custom_colors cover 9 non-missing states, SequenceData() will add the Missing color automatically.
If you would like to customize the color for the state "Missing", please refer to this section of the tutorial.
A minimal, working example with custom colors
We use the built-in pairfam-family dataset with a gentle and fresh palette.
# Import necessary libraries
# Your calling code (e.g., in a script or notebook)
from sequenzo import * # Import Sequenzo helpers used below
import pandas as pd # Data manipulation
df = load_dataset("pairfam_family")
# 1) Define time and states (order matters!)
time_list = [f"{i}" for i in range(1, 265)] # 264 months
states = list(range(1, 10)) # 1..9
# 2) Human-readable labels (strings)
labels = [
"Single, no child",
"LAT, no child",
"Cohabiting, no child",
"Married, no child",
"Single, with child(ren)",
"LAT, with child(ren)",
"Cohabiting, with child(ren)",
"Married, 1 child",
"Married, 2+ children"
]
# 3) A fresh palette (9 colors, same order as 'states')
colors_list = [
"#74C9B4", # fresh green
"#A6E3D0", # water mint
"#F9E79F", # pale apricot
"#F6CDA3", # warm light orange
"#F5B7B1", # soft rose
"#D7BDE2", # light lavender
"#A3C4F3", # porcelain blue
"#7FB3D5", # lake blue
"#EAECEE" # cloud white
]
sequence_data = SequenceData(
df,
time=time_list,
id_col="id",
states=states,
labels=labels,
# If your data do not contain weight, then no need to include the following parameter
weights=df["weight40"].values,
custom_colors=colors_list
)
# Optional: preview the legend that uses these colors
sequence_data.plot_legend()
# Use the horizontal style when you want a compact legend for a plot layout
sequence_data.plot_legend(style="horizontal")Output:
[>] SequenceData initialized successfully! Here's a summary:
[>] Number of sequences: 1866
[>] Number of time points: 264
[>] Min/Max sequence length: 264 / 264
[>] States: [1, 2, 3, 4, 5, 6, 7, 8, 9]
[>] Labels: ['Single, no child', 'Living apart together, no child', 'Cohabiting, no child', 'Married, no child', 'Single, with child(ren)', 'LAT, with child(ren)', 'Cohabiting, with child(ren)', 'Married, 1 child', 'Married, 2+ children']
[>] Weights: Provided (total weight=2346.268, mean=1.257, std=1.046)
plot_legend() uses a vertical layout by default. For publication-style plot panels or grouped index plots, style="horizontal" creates a multi-column legend and automatically chooses a cleaner column count. You can override it with ncol=..., adjust fontsize=..., and set show_border=True if you want a framed legend.
With the colors defined in SequenceData, you can go ahead and generate your visualizations and your plots will now appear with the customized palette you chose:
plot_sequence_index(sequence_data,
xlabel="Month (age 18-40)")
Ready-to-use palettes
Below are palettes you can copy directly into custom_colors. The squares show the actual colors. Use them as starting points, then check whether the contrast works for your states and figures.
In the examples we provide nine colors, because our demo dataset has nine states, which is already quite a lot. Your own dataset may have fewer or more states, and that’s perfectly fine. If you have fewer states, simply pick a subset of the palette and test how it looks in your visualizations (e.g., by calling plot_sequence_index(sequence_data)).
If you have more states, you can extend the palette in two ways:
Use a color picker tool (e.g., htmlcolorcodes.com) to select additional shades from an image you like.
Extend the palette with a color tool that checks contrast and color-blind accessibility.
In practice, defining the palette that you like is often an iterative process: try a set of colors, visualize your sequences, and adjust until the result feels both clear and aesthetically consistent.
A note on format: all colors are specified in hexadecimal (hex) codes, e.g. #74C9B4. A hex code is a six-digit representation of red, green, and blue values (RGB) in base-16. It’s the standard way of writing colors on the web and in plotting libraries such as Matplotlib, making it easy to copy and reuse across different tools.
Fresh Spring (9 colors, gentle and fresh)
Hex list:
["#74C9B4","#A6E3D0","#F9E79F","#F6CDA3","#F5B7B1","#D7BDE2","#A3C4F3","#7FB3D5","#EAECEE"]Ink-and-Wash Pastel (9 colors, airy pastels like Chinese traditional painting)
Hex list:
["#A3C9A8","#CFE8CF","#F2E6C2","#F6D5C3","#E8B4B8","#D8C7E8","#BBD3F2","#9EC5E6","#EDEDED"]Mint Lavender (9 colors, fresh mint and soft lavender tones)
Hex list:
["#A8E6CF","#B8F2E6","#C7F0DB","#D6F0E8","#E5F0F0","#F0E5F0","#E8D6F0","#D6C7F0","#C7B8F0"]Sequential (ordinal) single-hue ramp (9 colors, for ordered states)
Pick one ramp and use it when your states are naturally ordered (e.g., low→high). These are monotone-lightness, print-friendly. If you prefer other colors, try this webpage.
Blue ramp
Hex list:
["#EAF2FB","#D3E4F7","#BDD6F2","#A6C7EE","#90B9E9","#79AAE5","#639CE0","#4C8EDC","#3680D7"]Teal ramp
Hex list:
["#EAF6F3","#D1EEE7","#B9E6DC","#A1DED0","#89D6C5","#71CEB9","#59C6AE","#41BEA2","#29B697"]Want to be more creative?
If you’d like to go beyond the built-in palettes, you can
- refer to collections of color combinations via colorhunt.co
- or pick your own custom colors directly from an image. A simple tool like imagecolorpicker.com lets you upload any picture (say a painting, a photo, or even your project’s logo) and extract the exact hex codes you want.
How to include a custom Missing color
If you prefer another color for the Missing state (e.g., slightly darker than the default one), then you need to explicitly add Missing into both states and labels parameters in the following way:
# The following line of code is for your reference:
colors_no_missing = ["#74C9B4","#A6E3D0","#F9E79F","#F6CDA3","#F5B7B1",
"#D7BDE2","#A3C4F3","#7FB3D5","#EAECEE"]
# If you would like to include a custom Missing color explicitly
colors_with_missing = colors_no_missing + ["#C4C4C4"] # custom Missing
sequence_data = SequenceData(
df, time=time_list, id_col="id",
states=states + ["Missing"],
labels=labels + ["Missing"],
custom_colors=colors_with_missing
)Troubleshooting Q&A
You might encounter the following error messages:
“The following provided 'states' are not found in the data…”
Double-check that each state value actually appears in df[time] (exact equality). If you use strings, audit whitespace and capitalization.
“Length of custom_colors must match number of states.”
Count the states after Missing handling. If Missing is auto-added, either pass a palette with len = non-missing states (Sequenzo appends gray) or pass the full length including a color for Missing.
“My labels are numbers, is that OK?”
Please pass labels as strings (e.g., "Low", "High"). The validator will warn you otherwise.
Author: Yuqi Liang