Handling Color Overflow in Brewer Palettes¶
ColorBrewer palettes have a limited number of colors (typically 8–12).
When your data has more categories than the palette provides, a larger palette is created either by interpolating the original colors, or by cycling, or by generating additional colors.
By default, qualitative palettes use "generate" (creating new colors with varied luminance),
while sequential and diverging palettes use "interpolate".
The overflow parameter controls the way the extended palette is generated: "interpolate", "cycle", or "generate".
%useLatestDescriptors
%use lets-plot
val letters = ('A'..'Z').map { it.toString() }
val data = mapOf(
"letter" to letters,
"x" to (0 until letters.size).toList()
)
val p = letsPlot(data) { x = "x"; color = "letter" } +
geomPoint(size = 7) +
guides(color = guideLegend(nrow = 9))
Default: "generate"¶
Additional colors are created based on the original palette colors.
p + scaleColorBrewer(palette = "Set1")
Cycle Palette Colors¶
p + scaleColorBrewer(palette = "Set1", overflow = "cycle")
Interpolate Palette Colors¶
p + scaleColorBrewer(palette = "Set1", overflow = "interpolate")
Choosing the Right Palette for Many Categories¶
When displaying a large number of categories (like countries on a world map), not all Brewer palettes work equally well with the generate overflow.
Palettes with high contrast colors like Set1 can produce jarring results when extended.
Softer palettes like Pastel1 work better, while Paired and Set3 tend to produce the most visually balanced results for large categorical datasets.
@file:Repository("https://repo.osgeo.org/repository/release/")
@file:DependsOn("org.geotools:gt-shapefile:33.2")
@file:DependsOn("org.geotools:gt-epsg-hsql:33.2")
%use lets-plot-gt
%use dataframe
import org.geotools.data.shapefile.ShapefileDataStoreFactory
import java.net.URL
val factory = ShapefileDataStoreFactory()
fun loadShp(name: String) =
factory.createDataStore(
URL("https://raw.githubusercontent.com/JetBrains/lets-plot-kotlin/master/docs/examples/shp/$name/$name.shp")
).featureSource.features.toSpatialDataset()
val worldSds = loadShp("naturalearth_lowres")
val df = worldSds.toDataFrame()
df.take(5)
DataFrame: rowsCount = 5, columnsCount = 6
| continent | pop_est | gdp_md_est | name | iso_a3 | geometry |
|---|---|---|---|---|---|
| Oceania | 920938,000000 | 8374,000000 | Fiji | FJI | {"type":"MultiPolygon","coordinates":... |
| Africa | 53950935,000000 | 150600,000000 | Tanzania | TZA | {"type":"MultiPolygon","coordinates":... |
| Africa | 603253,000000 | 906,500000 | W. Sahara | ESH | {"type":"MultiPolygon","coordinates":... |
| North America | 35623680,000000 | 1674000,000000 | Canada | CAN | {"type":"MultiPolygon","coordinates":... |
| North America | 326625791,000000 | 18560000,000000 | United States of America | USA | {"type":"MultiPolygon","coordinates":... |
val world = letsPlot() +
geomMap(map = worldSds, color = "white", size = 0.5) { fill = "name" } +
coordMap(ylim = -60 to null) +
ggsize(900, 500) +
theme().legendPositionNone() +
ggtb()
The Default Qualitative Palette (Set1)¶
Works, but not ideal for many categories.
world + scaleFillBrewer()
Pastel1¶
A softer palette that works better with many categories.
world + scaleFillBrewer(palette = "Pastel1")
Paired and Set3¶
These palettes produce the most balanced results for large categorical datasets.
// sort countries to ensure a consistent color palette order
val sortedByPop = df.sortBy("pop_est")["name"].toList().filterNotNull()
world + scaleFillBrewer(palette = "Paired", limits = sortedByPop)
world + scaleFillBrewer(palette = "Set3", limits = sortedByPop)