--- title: "Mapping Guerry's Moral Statistics with Cheysson Palettes" author: "Michael Friendly" date: "`r Sys.Date()`" output: rmarkdown::html_vignette: toc: true toc_depth: 2 vignette: > %\VignetteIndexEntry{Mapping Guerry's Moral Statistics with Cheysson Palettes} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 8, fig.height = 7, fig.align = "center", # ragg, not the default png(): on Intel macOS the Quartz png() device # segfaults drawing ggpattern's grid masks at >= 96 dpi (CRAN check ERROR on 1.0.1) dev = if (requireNamespace("ragg", quietly = TRUE)) "ragg_png" else "png", # draw showtext fonts at the device's real dpi (pkgdown renders retina at 2x; # without this, text there comes out at half size) fig.showtext = TRUE, warning = FALSE, message = FALSE ) ``` ## Introduction This vignette demonstrates how to create thematic (choropleth) maps using the `ggCheysson` package with André-Michel Guerry's pioneering data on moral statistics of France from 1833. This represents a fascinating combination of: - **Historical data**: Guerry's groundbreaking social statistics from 1830s France - **Historical cartography**: The visual style of Émile Cheysson's *Albums de Statistique Graphique* (1879-1897) - **Modern tools**: R, ggplot2, and spatial data packages Guerry (1802-1866) was among the first to use statistical maps to visualize social phenomena across regions. His major work predated Cheysson's work on the _Albums_, making this a fitting tribute to two pioneers of statistical graphics. In Guerry (1833), he displayed six thematic choropleth maps of France, using a monochrome shading scheme. What if Guerry could have re-done his maps using Cheysson's style? ## Required Packages ```{r packages} library(ggCheysson) library(ggplot2) library(Guerry) # Historical data on France library(sf) # Modern spatial data handling library(ggpattern) # For Cheysson-style hatching patterns ``` ## Loading Fonts ```{r load-fonts, eval=FALSE} # Load Cheysson fonts load_cheysson_fonts(method = "showtext") showtext::showtext_auto() ``` ```{r load-fonts-actual, include=FALSE} # Actual font loading (hidden from output) if (requireNamespace("showtext", quietly = TRUE) && requireNamespace("sysfonts", quietly = TRUE)) { load_cheysson_fonts(method = "showtext") showtext::showtext_auto() fonts_available <- TRUE } else { fonts_available <- FALSE } ``` ## Preparing the Data ### Load and Examine Guerry's Data ```{r load-data} # Load the dataset data(Guerry, package = "Guerry") # Key variables for mapping vars_of_interest <- c("Crime_pers", "Crime_prop", "Literacy", "Donations", "Infants", "Suicides") # View summary str(Guerry[, c("dept", "Department", vars_of_interest)]) ``` ### Load the Map The `gfrance85` object is a SpatialPolygonsDataFrame. We'll convert it to an sf object for modern spatial handling. ```{r load-map} # Load the map data(gfrance85, package = "Guerry") # Convert to sf object (simple features) france_sf <- st_as_sf(gfrance85) # Check structure head(france_sf[, c("Department", "Region")]) ``` ### Join Data with Map ```{r prepare-data} # Convert variables to ranks (since they're on different scales) guerry_ranked <- Guerry for (var in vars_of_interest) { guerry_ranked[[paste0(var, "_rank")]] <- rank(guerry_ranked[[var]], na.last = "keep") } # Join with spatial data france_data <- merge(france_sf, guerry_ranked, by = "Department", all.x = TRUE) # Check the join cat("Departments in map:", nrow(france_sf), "\n") cat("Departments with data:", sum(!is.na(france_data$Crime_pers_rank)), "\n") ``` ## Creating Choropleth Maps ### Crime Against Persons (Sequential Palette) ```{r map-crime-pers, fig.height=7, fig.width=8} # Map of crimes against persons p1 <- ggplot(france_data) + geom_sf(aes(fill = Crime_pers_rank), color = "black", linewidth = 0.3) + scale_fill_cheysson("1895_16", discrete = FALSE, name = "Rank") + labs( title = "Crimes Against Persons", subtitle = "France, 1830s (ranked by department)", caption = "Data: André-Michel Guerry (1833)" ) + theme_cheysson_map() + theme( legend.position = "right" ) print(p1) ``` ### Property Crime (Different Sequential Palette) ```{r map-crime-prop, fig.height=7, fig.width=8} p2 <- ggplot(france_data) + geom_sf(aes(fill = Crime_prop_rank), color = "black", linewidth = 0.3) + scale_fill_cheysson("1895_16", discrete = FALSE, name = "Rank") + labs( title = "Crimes Against Property", subtitle = "France, 1830s (ranked by department)", caption = "Data: André-Michel Guerry (1833)" ) + theme_cheysson_map() + theme( legend.position = "right" ) print(p2) ``` ### Literacy (Grouped Palette) ```{r map-literacy, fig.height=7, fig.width=8} # Create quintiles for discrete display france_data$Literacy_quint <- cut(france_data$Literacy_rank, breaks = quantile(france_data$Literacy_rank, probs = seq(0, 1, 0.2), na.rm = TRUE), include.lowest = TRUE, labels = c("Lowest", "Low", "Medium", "High", "Highest")) p3 <- ggplot(france_data) + geom_sf(aes(fill = Literacy_quint), color = "black", linewidth = 0.3) + scale_fill_cheysson("1881_22", name = "Literacy\nQuintile", na.value = "grey80") + labs( title = "Literacy Rates", subtitle = "Percent of military conscripts who can read & write (quintiles)", caption = "Data: André-Michel Guerry (1833)" ) + theme_cheysson_map() + theme( legend.position = "right" ) print(p3) ``` ### Literacy with Cheysson Patterns Now let's recreate the literacy map using Cheysson's signature hatching patterns. Palette `1888_27` is one of his sequential hatching scales: diagonal stripes that get progressively denser, ending in a solid fill. Mapping `pattern_spacing` as well as `pattern` reproduces that light-to-dark progression across the five quintiles. The palette's spacings were measured on Cheysson's small swatches, so they are scaled down here to suit a full-page map: ```{r map-literacy-pattern, fig.height=7, fig.width=8} # Literacy with patterns - quintessential Cheysson style lit_spacing <- cheysson_pattern_params(cheysson_pattern("1888_27"), "pattern_spacing") p3b <- ggplot(france_data) + geom_sf_pattern( aes(fill = Literacy_quint, pattern = Literacy_quint, pattern_fill = Literacy_quint, pattern_spacing = Literacy_quint), pattern_density = 0.3, pattern_colour = NA, color = "black", linewidth = 0.4 ) + scale_fill_cheysson_pattern("1888_27", na.value = "grey90") + scale_pattern_fill_cheysson("1888_27", na.value = "grey90") + scale_pattern_type_cheysson("1888_27") + scale_pattern_spacing_manual(values = 0.3 * lit_spacing) + labs( title = "Literacy Rates", subtitle = "Sequential hatching, sparse to solid (quintiles)", caption = "Data: André-Michel Guerry (1833)" ) + theme_cheysson_map() + theme( legend.position = "right" ) + guides( fill = guide_legend(title = "Literacy\nQuintile"), pattern = guide_legend(title = "Literacy\nQuintile"), pattern_spacing = guide_legend(title = "Literacy\nQuintile"), pattern_fill = guide_legend(title = "Literacy\nQuintile") ) print(p3b) ``` ### Charitable Donations (Category Palette) ```{r map-donations, fig.height=7, fig.width=8} # Create categories france_data$Donations_cat <- cut(france_data$Donations_rank, breaks = quantile(france_data$Donations_rank, probs = seq(0, 1, 0.25), na.rm = TRUE), include.lowest = TRUE, labels = c("Low", "Medium-Low", "Medium-High", "High")) p4 <- ggplot(france_data) + geom_sf(aes(fill = Donations_cat), color = "black", linewidth = 0.3) + scale_fill_cheysson("1883_31", name = "Donations\nLevel", na.value = "grey80") + labs( title = "Charitable Donations", subtitle = "Donations to the poor (quartiles)", caption = "Data: André-Michel Guerry (1833)" ) + theme_cheysson_map() + theme( legend.position = "right" ) print(p4) ``` ### Donations with Cheysson Patterns The combination of colors and patterns was a hallmark of the Albums. Palette `1883_31` is one of Cheysson's diverging schemes: two hues, each used both solid and hatched. As in his maps, the solid fills mark the extremes and the hatched versions the milder classes on each side. Diverging palettes are stored in that low-to-high order, so the Cheysson pattern scales apply it directly: ```{r map-donations-pattern, fig.height=7, fig.width=8} # Donations: solid at the extremes, hatched in the middle p4b <- ggplot(france_data) + geom_sf_pattern( aes(fill = Donations_cat, pattern = Donations_cat, pattern_fill = Donations_cat), pattern_density = 0.35, pattern_spacing = 0.025, pattern_colour = NA, color = "black", linewidth = 0.4 ) + scale_fill_cheysson_pattern("1883_31", na.value = "grey90") + scale_pattern_fill_cheysson("1883_31", na.value = "grey90") + scale_pattern_type_cheysson("1883_31") + labs( title = "Charitable Donations", subtitle = "Authentic Cheysson-style patterns and colors (quartiles)", caption = "Data: André-Michel Guerry (1833)" ) + theme_cheysson_map() + theme( legend.position = "right" ) + guides( fill = guide_legend(title = "Donations\nLevel"), pattern = guide_legend(title = "Donations\nLevel"), pattern_fill = guide_legend(title = "Donations\nLevel") ) print(p4b) ``` ### Illegitimate Births (Sequential Palette) ```{r map-infants, fig.height=7, fig.width=8} p5 <- ggplot(france_data) + geom_sf(aes(fill = Infants_rank), color = "black", linewidth = 0.3) + scale_fill_cheysson("1891_25", discrete = FALSE, name = "Rank") + labs( title = "Illegitimate Births", subtitle = "Population per illegitimate birth (ranked by department)", caption = "Data: André-Michel Guerry (1833)" ) + theme_cheysson_map() + theme( legend.position = "right" ) print(p5) ``` ### Suicides (Different Sequential Palette) ```{r map-suicides, fig.height=7, fig.width=8} p6 <- ggplot(france_data) + geom_sf(aes(fill = Suicides_rank), color = "black", linewidth = 0.3) + scale_fill_cheysson("1887_22", discrete = FALSE, name = "Rank") + labs( title = "Suicides", subtitle = "Annual suicides per population (ranked by department)", caption = "Data: André-Michel Guerry (1833)" ) + theme_cheysson_map() + theme( legend.position = "right" ) print(p6) ``` ## Small Multiples: Comparing Crime Types Create a faceted map showing multiple variables at once: ```{r map-faceted, fig.height=8, fig.width=10} # Prepare data in long format for faceting library(tidyr) library(dplyr) crime_long <- france_data |> st_as_sf() |> select(Department, Crime_pers_rank, Crime_prop_rank, Literacy_rank, Suicides_rank) |> pivot_longer(cols = ends_with("_rank"), names_to = "Variable", values_to = "Rank") |> mutate(Variable = recode(Variable, "Crime_pers_rank" = "Crimes Against Persons", "Crime_prop_rank" = "Property Crimes", "Literacy_rank" = "Literacy Rate", "Suicides_rank" = "Suicides")) p7 <- ggplot(crime_long) + geom_sf(aes(fill = Rank), color = "grey30", linewidth = 0.2) + scale_fill_cheysson("1895_16", discrete = FALSE, name = "Rank") + facet_wrap(~ Variable, ncol = 2) + labs( title = "Social Statistics of France, 1830s", subtitle = "Four measures of moral statistics (ranked by department)", caption = "Data: André-Michel Guerry (1833)" ) + theme_cheysson_map() + theme( strip.background = element_rect(fill = "#edd493", color = "black"), strip.text = element_text(size = 10, face = "bold"), legend.position = "bottom", legend.key.width = unit(2, "cm") ) print(p7) ``` ## Regional Patterns Let's also examine regional patterns using discrete categories: ```{r map-by-region, fig.height=7, fig.width=8} # Map showing regions # Note: After merge, Region column may be duplicated as Region.x or Region.y # We'll use the spatial data version (Region.x) or check which exists region_col <- if("Region" %in% names(france_data)) { "Region" } else if("Region.x" %in% names(france_data)) { "Region.x" } else { "Region.y" } p8 <- ggplot(france_data) + geom_sf(aes(fill = .data[[region_col]]), color = "black", linewidth = 0.4) + scale_fill_cheysson("category", name = "Region") + labs( title = "Regions of France", subtitle = "Administrative divisions circa 1830", caption = "Source: Guerry package" ) + theme_cheysson_map() + theme( legend.position = "right" ) print(p8) ``` ### Regions with Patterns: Classic Cheysson Cartography One of Cheysson's most distinctive techniques was using varied hatching patterns to distinguish regions. Most category palettes are solid colors only, so we pick one that has hatching: `1883_30` has red and blue stripes, a red-and-blue crosshatch, black stripes and solid black - one for each of the five regions. Mapping `pattern_angle` as well draws the stripes at the angles used in the original plate, and `pattern_fill2` gives the crosshatch its second color. ```{r map-regions-pattern, fig.height=7, fig.width=8} # Regions with distinctive patterns - very characteristic of Cheysson p8b <- ggplot(france_data) + geom_sf_pattern( aes(fill = .data[[region_col]], pattern = .data[[region_col]], pattern_fill = .data[[region_col]], pattern_fill2 = .data[[region_col]], pattern_angle = .data[[region_col]]), pattern_colour = NA, pattern_density = 0.3, pattern_spacing = 0.02, color = "black", linewidth = 0.5 ) + scale_fill_cheysson_pattern("1883_30") + scale_pattern_fill_cheysson("1883_30") + scale_pattern_fill2_cheysson("1883_30") + scale_pattern_type_cheysson("1883_30") + scale_pattern_angle_cheysson("1883_30") + labs( title = "Regions of France", subtitle = "Distinctive hatching patterns for each region - authentic Albums style", caption = "Source: Guerry package", fill = "Region", pattern = "Region", pattern_fill = "Region", pattern_fill2 = "Region", pattern_angle = "Region" ) + theme_cheysson_map() + theme( legend.position = "right" ) print(p8b) ``` ## Bivariate Comparison Compare two variables using different visual encodings: ```{r map-bivariate, fig.height=7, fig.width=9} # Create categories for both variables france_data$Crime_cat <- cut(france_data$Crime_pers_rank, breaks = 3, labels = c("Low", "Medium", "High")) france_data$Lit_cat <- cut(france_data$Literacy_rank, breaks = 3, labels = c("Low", "Medium", "High")) # Create bivariate category france_data$Bivariate <- paste0(france_data$Crime_cat, "\n", france_data$Lit_cat, " Literacy") # Plot p9 <- ggplot(france_data) + geom_sf(aes(fill = Crime_pers_rank), color = "black", linewidth = 0.5) + scale_fill_cheysson("1895_16", discrete = FALSE, name = "Crime\nRank") + # Add point symbols sized by literacy geom_sf_text(aes(label = ifelse(Literacy_rank > 70, "H", ifelse(Literacy_rank < 25, "L", ""))), size = 3, fontface = "bold") + labs( title = "Crime vs. Literacy", subtitle = "Crime Against Persons (color) and Literacy (H=High, L=Low)", caption = "Data: André-Michel Guerry (1833)" ) + theme_cheysson_map() print(p9) ``` ## Historical Context ### About Guerry's Data André-Michel Guerry (1802-1866) was a French lawyer and statistician who pioneered the use of statistical graphics and thematic maps. His 1833 *Essai sur la statistique morale de la France* was one of the first works to: - Use choropleth maps to visualize social data - Examine geographical patterns in crime, literacy, and social indicators - Apply statistical methods to moral and social questions ### About the Variables - **Crime_pers**: Crimes against persons (per capita) - **Crime_prop**: Crimes against property (per capita) - **Literacy**: Percent of military conscripts who can read and write - **Donations**: Donations to the poor (per capita) - **Infants**: Population per illegitimate birth - **Suicides**: Annual suicides (per capita) ### The Connection to Cheysson Émile Cheysson (1836-1910), working 40-50 years after Guerry, brought similar statistical visualization techniques to new heights in the *Albums de Statistique Graphique*. By combining Guerry's data with Cheysson's visual style, we honor both pioneers of data visualization. ## Available Palettes The ggCheysson package includes multiple palettes suitable for choropleth maps: ```{r show-palettes} # Sequential palettes (good for continuous rankings) list_cheysson_pals("sequential") # Grouped palettes (good for categories) list_cheysson_pals("grouped") # Category palettes (good for discrete regions) list_cheysson_pals("category") ``` **Note**: When using `discrete = FALSE`, even category palettes can create smooth color gradients for continuous data. The package includes: - 7 Sequential palettes (varying colors: 1-3) - 2 Diverging palettes (2-3 colors) - 5 Grouped palettes (2-5 colors) - 6 Category palettes (3-7 colors) ## Summary This vignette demonstrated: - Converting SpatialPolygonsDataFrame to sf objects for ggplot2 - Joining spatial and tabular data - Creating choropleth maps with various Cheysson color palettes - **Using Cheysson hatching patterns combined with colors** - a signature feature of the Albums - Applying `geom_sf_pattern()` with pattern scales for authentic Cheysson cartography - Using `theme_cheysson_map()` for cartographic styling - Displaying multiple maps in faceted layouts - Combining historical data with historical cartographic styles ### Key Cheysson Techniques Illustrated The pattern-enhanced maps (literacy, donations, and regions) showcase Cheysson's most distinctive cartographic innovation: **combining colors with hatching patterns**. This dual encoding: - Enhances visual distinction between categories - Creates rich, textured maps characteristic of 19th century statistical graphics - Improves readability and aesthetic appeal - Provides redundant encoding (both color and pattern) for better accessibility These techniques defined the visual language of the *Albums de Statistique Graphique* and influenced statistical cartography for decades. The combination of Guerry's groundbreaking statistical data with Cheysson's elegant visual style creates a fitting tribute to the pioneers of statistical graphics and thematic cartography. ## References - Guerry, A.-M. (1833). *Essai sur la statistique morale de la France*. Paris: Crochard. - Friendly, M. (2007). A.-M. Guerry's *Moral Statistics of France*: Challenges for Multivariable Spatial Analysis. *Statistical Science*, **22**(3), 368-399. - Friendly, M. (2008). The Golden Age of Statistical Graphics. *Statistical Science*, **23**(4), 502-535. ```{r cleanup, include=FALSE} # Clean up if (exists("fonts_available") && fonts_available) { showtext::showtext_auto(FALSE) } ```