Powered by AppSignal & Oban Pro

Colour gradients: scale_gradient

notebooks/scale_gradient.livemd

Colour gradients: scale_gradient

Mix.install([
  {:giggly, path: Path.join(__DIR__, "..")},
  {:kino, "~> 0.19"}
  ])

alias Giggly.Plot

render = fn plot ->
  plot
  |> Giggly.to_svg()
  |> IO.iodata_to_binary()
  |> Kino.Image.new(:svg)
end

About this notebook

This notebook follows the examples on ggplot2's reference page for scale_colour_gradient(), which covers scale_fill_gradient(), scale_colour_gradient2(), scale_fill_gradient2(), scale_colour_gradientn() and scale_fill_gradientn() too, in the order the page gives them. Each example shows ggplot2's code first and then Giggly's. An example Giggly cannot draw yet keeps its ggplot2 code and names what it is waiting for, so the page is also a list of what remains.

A gradient scale maps a continuous column to colour, or to fill, by blending between colours. scale_colour_gradient() blends from a low colour to a high one, scale_colour_gradient2() diverges from a low colour through a middle one to a high one, around a midpoint, and scale_colour_gradientn() blends through as many colours as it is given; the fill versions do the same for fill. A missing value is drawn in grey, or in the colour na.value gives. The legend that reads a gradient back is a colour bar, guide_colourbar(). Giggly has all six, Giggly.Plot.scale_color_gradient/2 and the rest, with their colours, gradient2's midpoint:, and their limits, breaks, labels and na_value:, and reads each back with a colour bar; it has no rescaler option (scales::rescale_mid()).

The default gradient

The examples draw from two tables of random numbers R makes after set.seed(1): df, and df_na, whose z1 is missing in its first ten rows.

set.seed(1)
df <- data.frame(
  x = runif(100),
  y = runif(100),
  z1 = rnorm(100),
  z2 = abs(rnorm(100))
)

df_na <- data.frame(
  value = seq(1, 20),
  x = runif(20),
  y = runif(20),
  z1 = c(rep(NA, 10), rnorm(10))
)

# Default colour scale colours from light blue to dark blue
ggplot(df, aes(x, y)) +
  geom_point(aes(colour = z2))

The cell writes out df's x, y, z1 and z2 as R made them, each number to the last digit R holds. A column of numbers mapped to colour takes ggplot2's default gradient, from #132B43 at the smallest z2 to #56B1F7 at the largest, blended in L*a*b*, and a colour bar reads it back.

df = %{
  "x" => [
    0.2655086631421, 0.37212389963679016, 0.5728533633518964, 0.9082077899947762,
    0.2016819310374558, 0.8983896849676967, 0.9446752686053514, 0.6607977924868464,
    0.6291140438988805, 0.061786270467564464, 0.20597457489930093, 0.17655675252899528,
    0.6870228466577828, 0.384103718213737, 0.7698414199985564, 0.49769924208521843,
    0.7176185082644224, 0.9919060948304832, 0.3800351794343442, 0.7774452213197947,
    0.9347052311059088, 0.21214252128265798, 0.651673766085878, 0.12555509596131742,
    0.26722066872753203, 0.3861140925437212, 0.013390333158895373, 0.3823879570700228,
    0.86969084572047, 0.3403489966876805, 0.48208011547103524, 0.5995658254250884,
    0.4935413070488721, 0.1862176014110446, 0.8273733186069876, 0.6684667381923646,
    0.7942398607265204, 0.10794362588785589, 0.723710946040228, 0.4112744296435267,
    0.8209462941158563, 0.6470601938199252, 0.7829327622894198, 0.5530363116413355,
    0.5297195801977068, 0.7893562316894531, 0.023331202333793044, 0.47723006503656507,
    0.7323137386702001, 0.6927315564826131, 0.4776196221355349, 0.8612094768323004,
    0.4380971072241664, 0.24479727703146636, 0.07067904714494944, 0.09946616017259657,
    0.3162717071827501, 0.5186342631932348, 0.6620050764176995, 0.4068301871884614,
    0.9128759242594242, 0.29360337276011705, 0.4590657262597233, 0.3323946741875261,
    0.6508704670704901, 0.2580167807172984, 0.4785452482756227, 0.7663106706459075,
    0.08424691436812282, 0.875321330036968, 0.33907293784432113, 0.8394403501879424,
    0.3466834891587496, 0.3337749307975173, 0.4763512450736016, 0.8921983358450234,
    0.8643394706305116, 0.389989543473348, 0.7773206988349557, 0.9606179972179234,
    0.4346594847738743, 0.7125146787147969, 0.399994368897751, 0.3253521518781781,
    0.7570871480274945, 0.20269225514493883, 0.7111212224699557, 0.12169192102737725,
    0.24548851395957172, 0.14330437942408025, 0.2396294150967151, 0.05893437727354467,
    0.6422882585320622, 0.8762692126911134, 0.7789146774448454, 0.7973088258877397,
    0.45527445361949503, 0.4100840820465237, 0.8108702427707613, 0.6049332902766764
  ],
  "y" => [
    0.6547239280771464, 0.35319727193564177, 0.2702601458877325, 0.9926840611733496,
    0.6334932644385844, 0.2132081352174282, 0.12937234807759523, 0.47811803431250155,
    0.9240744698327035, 0.59876096714288, 0.9761706949211657, 0.7317925118841231,
    0.35672691208310425, 0.43147369055077434, 0.148211560677737, 0.013077575480565429,
    0.7155660660937428, 0.10318423574790359, 0.4462843486107886, 0.6401010451372713,
    0.991838620044291, 0.49559357785619795, 0.4843495243694633, 0.17344233486801386,
    0.7548209445085377, 0.4538954892195761, 0.511169783771038, 0.20754511328414083,
    0.22865814273245633, 0.5957119963131845, 0.5748721982818097, 0.07706438028253615,
    0.0355405795853585, 0.6427954921964556, 0.9286151996348053, 0.5980924223549664,
    0.5609007480088621, 0.5260277239140123, 0.985095223877579, 0.5076418223325163,
    0.6827880786731839, 0.6015412176493555, 0.23886867775581777, 0.25816592667251825,
    0.7293096233624965, 0.45257083140313625, 0.17512676841579378, 0.7466982696205378,
    0.10498764039948583, 0.864544949028641, 0.6146449716761708, 0.557159538846463,
    0.32877731905318797, 0.45313144545070827, 0.5004409726243466, 0.18086636113002896,
    0.5296306028030813, 0.07527574570849538, 0.2777559326495975, 0.2126995192375034,
    0.28479048097506166, 0.8950941029470414, 0.44623532332479954, 0.7799848897848278,
    0.8806190348695964, 0.4131242095027119, 0.06380848051048815, 0.33548749168403447,
    0.7237259466201067, 0.33761533349752426, 0.6304141224827617, 0.8406145540066063,
    0.8561316647101194, 0.3913592810276896, 0.3804938856046647, 0.8954454259946942,
    0.6443157629109919, 0.7410786487162113, 0.6053034465294331, 0.9030816114973277,
    0.2937301550991833, 0.1912601098883897, 0.8864509433042258, 0.5033394857309759,
    0.8770575430244207, 0.18919362244196236, 0.758103052387014, 0.7244988926686347,
    0.9437248182948679, 0.547646587016061, 0.7117438677232713, 0.3889050998259336,
    0.10087312618270516, 0.9273020885884762, 0.28323250031098723, 0.5905731588136405,
    0.11036060494370759, 0.840507032116875, 0.3179636844433844, 0.7828513369895518
  ],
  "z1" => [
    -0.6203666772241242, 0.04211587314423524, -0.9109216485524455, 0.15802877240407498,
    -0.6545846439188178, 1.767287269372646, 0.7167074760172057, 0.9101742294952272,
    0.3841853578263446, 1.6821760805194184, -0.635736453948977, -0.461644730360566,
    1.432282238541663, -0.6506963533103667, -0.20738074360196543, -0.3928079294419839,
    -0.31999286854850684, -0.27911330297655895, 0.494188331267827, -0.17733048226960638,
    -0.5059574621142574, 1.3430388251704115, -0.21457940854686872, -0.17955653004338712,
    -0.10019074121356196, 0.7126663070514053, -0.07356440412632634, -0.037634171467047915,
    -0.6816604787556569, -0.32427027224631905, 0.06016044043451516, -0.5888944862596638,
    0.5314961926325724, -1.5183940817867871, 0.3065578607897656, -1.536449823537586,
    -0.3009761268366107, -0.5282799044450062, -0.6520947806809988, -0.05689677784739255,
    -1.9143594256800118, 1.1765833120185625, -1.6649724362120033, -0.4635304014723856,
    -1.1159201050428453, -0.7508190011934479, 2.087166545628347, 0.017395619693251662,
    -1.2863005304343256, -1.6406055344185784, 0.45018710127265554, -0.018559832714637983,
    -0.3180683745438444, -0.9293621474537024, -1.4874603101414847, -1.0751922966156808,
    1.0000288037139142, -0.6212666947968234, -1.3844268473844914, 1.8692906224235806,
    0.4251003773724479, -0.23864710091303276, 1.05848304870902, 0.8864226513749359,
    -0.6192430482311471, 2.2061024645404674, -0.25502703014101524, -1.4244946502128084,
    -0.14439960195421941, 0.20753833923234477, 2.3079783990593614, 0.10580236789371146,
    0.45699880542341353, -0.07715293535653098, -0.3340008423665445, -0.03472602831127619,
    0.7876396056301618, 2.075245008652285, 1.027392438763768, 1.2079083983867038,
    -1.231323421558044, 0.9838955700533791, 0.21992480366065129, -1.4672500290922426,
    0.5210227426481387, -0.158754604716016, 1.4645873119697974, -0.7660819996046648,
    -0.43021175392854655, -0.9261094973774372, -0.17710396143654025, 0.4020117794863379,
    -0.7317481731196064, 0.8303731679816738, -1.208082786304465, -1.0479844128077416,
    1.441157706844281, -1.015847465304649, 0.41197471231751487, -0.3810760511089196
  ],
  "z2" => [
    0.4094018396509341, 1.6888732862040463, 1.5865884334419702, 0.3309078006827663,
    2.285235535292467, 2.497661589834164, 0.667066166765493, 0.5413273359637003,
    0.013399523145908702, 0.5101084229529265, 0.16437583176966725, 0.4206946432545128,
    0.4002467439776438, 1.3702078775474604, 0.9878382674548787, 1.5197450254995455,
    0.30874056922561366, 1.2532897556076912, 0.6422413056778238, 0.04470913689397912,
    1.7332184068248382, 0.002131859680269648, 0.6303003339281461, 0.3409685798604052,
    1.1565723626358533, 1.8031419079174669, 0.3311320363912211, 1.6055134122530774,
    0.19719343873948075, 0.26317564640547375, 0.9858267004092908, 2.888920671679545,
    0.6404817025651149, 0.5705076359204846, 0.059723276042610046, 0.09817874400523441,
    0.5608207286201161, 1.1864586385794738, 1.096777044274243, 0.0053440282781656925,
    0.7073106673980789, 1.034107734737462, 0.22348041491530438, 0.8787076128660186,
    1.1629645559673303, 2.0001649447854755, 0.5447907400017248, 0.2556707091569888,
    0.16612103676500617, 1.0204639087841076, 0.13622189310277766, 0.40716760342383645,
    0.06965481301290485, 0.24766434161933082, 0.6955508066196402, 1.146228357215801,
    2.4030962148918715, 0.5727395552458405, 0.37472440677865465, 0.4252677215560763,
    0.9510128075768164, 0.3892371817183789, 0.2843306617995735, 0.8574097780798033,
    1.7196272991206014, 0.2700549009372289, 0.4221840097876401, 1.189113294859585,
    0.3310329788790098, 0.9398293265100214, 0.25893258311878486, 0.39437916822157215,
    0.8518570920238634, 2.64916688109488, 0.1560116756650789, 1.1302072674549426,
    2.2891239798401055, 0.7410011571954386, 1.3162451604515617, 0.9198036776091413,
    0.39813015545195607, 0.40752857926977204, 1.3242586301772727, 0.7012316692469196,
    0.5806143042405362, 1.0010721810254186, 0.6681786067533935, 0.9451849533730824,
    0.43370214954516195, 1.0051592176770432, 0.3901186640536787, 0.3763702917746482,
    0.24416492448649424, 1.4262573423825382, 1.778429287475451, 0.13444766093367594,
    0.7655989991578642, 0.9551366769089816, 0.05056570144227013, 0.305815419766971
  ]
}

df
|> Plot.new(%{x: :x, y: :y})
|> Plot.geom_point(%{colour: :z2})
|> render.()

A diverging gradient

# For diverging colour scales use gradient2
ggplot(df, aes(x, y)) +
  geom_point(aes(colour = z1)) +
  scale_colour_gradient2()

The colours diverge from white at the midpoint, 0, toward a muted red below it and a muted blue above, each end reached only as far as the data goes from 0.

df
|> Plot.new(%{x: :x, y: :y})
|> Plot.geom_point(%{colour: :z1})
|> Plot.scale_color_gradient2()
|> render.()

Colours of your own

# Use your own colour scale with gradientn
ggplot(df, aes(x, y)) +
  geom_point(aes(colour = z1)) +
  scale_colour_gradientn(colours = terrain.colors(10))

R's terrain.colors(10) is written out as the ten colours it returns.

terrain =
  ~w(#00A600 #2DB600 #63C600 #A0D600 #E6E600 #E8C32E #EBB25E #EDB48E #F0C9C0 #F2F2F2)

df
|> Plot.new(%{x: :x, y: :y})
|> Plot.geom_point(%{colour: :z1})
|> Plot.scale_color_gradientn(colors: terrain)
|> render.()

Centring the gradient

# The gradientn scale can be centered by using a rescaler
ggplot(df, aes(x, y)) +
  geom_point(aes(colour = z1)) +
  scale_colour_gradientn(
    colours = c("blue", "dodgerblue", "white", "orange", "red"),
    rescaler = ~ scales::rescale_mid(.x, mid = 0)
  )

scales::rescale_mid() puts 0 at the middle of the colours, here white, however far the data reaches on either side.

Not in Giggly yet: scale_colour_gradientn()'s rescaler option and scales::rescale_mid().

Filling with a gradient

# Equivalent fill scales do the same job for the fill aesthetic
ggplot(faithfuld, aes(waiting, eruptions)) +
  geom_raster(aes(fill = density)) +
  scale_fill_gradientn(colours = terrain.colors(10))

Not in Giggly yet: the faithfuld dataset and rasters (geom_raster()).

Choosing the low and high colours

# Adjust colour choices with low and high
ggplot(df, aes(x, y)) +
  geom_point(aes(colour = z2)) +
  scale_colour_gradient(low = "white", high = "black")
# Avoid red-green colour contrasts because ~10% of men have difficulty
# seeing them
df
|> Plot.new(%{x: :x, y: :y})
|> Plot.geom_point(%{colour: :z2})
|> Plot.scale_color_gradient(low: "white", high: "black")
|> render.()

Hiding missing values

geom_bar(stat = "identity") draws heights already in the data, as geom_col() does, which Giggly has as Giggly.Plot.geom_col/3. With na.value = NA, a bar or point whose z1 is missing is not drawn, but still counts towards the axes' ranges.

# Use `na.value = NA` to hide missing values but keep the original axis range
ggplot(df_na, aes(x = value, y)) +
  geom_bar(aes(fill = z1), stat = "identity") +
  scale_fill_gradient(low = "yellow", high = "red", na.value = NA)

 ggplot(df_na, aes(x, y)) +
   geom_point(aes(colour = z1)) +
   scale_colour_gradient(low = "yellow", high = "red", na.value = NA)

Giggly draws the second plot. The cell writes out df_na as R made it, each number to the last digit R holds, its z1 missing in the first ten rows; R's NA for no colour is :none. Ten points are drawn, and the axes span all twenty.

df_na = %{
  "value" => Enum.to_list(1..20),
  "x" => [
    0.8142517504747957, 0.9287772257812321, 0.14748104522004724, 0.7498216649983078,
    0.975657349685207, 0.974792463472113, 0.3506255701649934, 0.3939490569755435,
    0.9509510090574622, 0.1066483196336776, 0.9347601165063679, 0.3461620998568833,
    0.5330606058705598, 0.5387942993547767, 0.7147179497405887, 0.4057905001100153,
    0.15278814127668738, 0.3402327613439411, 0.6266548475250602, 0.057372678304091096
  ],
  "y" => [
    0.8516676397994161, 0.21264534583315253, 0.5394620278384537, 0.13648758619092405,
    0.3248651404865086, 0.6210762937553227, 0.2559822485782206, 0.6348757988307625,
    0.4856721069663763, 0.9381769166793674, 0.8575015370734036, 0.3708835376892239,
    0.3142018299549818, 0.8285343598108739, 0.45184151316061616, 0.31587841222062707,
    0.09780853753909469, 0.06490053655579686, 0.6894573664758354, 0.668050603941083
  ],
  "z1" =>
    List.duplicate(nil, 10) ++
      [
        1.3079015201141457, 1.4970410094027833, 0.8147027308973563, -1.8697887902026127,
        0.4820295041237599, 0.45613560330120195, -0.3534002858299113, 0.17048947094798195,
        -0.8640359541269036, 0.6792307740156558
      ]
}

df_na
|> Plot.new(%{x: :x, y: :y})
|> Plot.geom_point(%{colour: :z1})
|> Plot.scale_color_gradient(low: "yellow", high: "red", na_value: :none)
|> render.()

Not in Giggly yet: geom_bar()'s stat option.