I have the following code. It produces a levelplot in which square values less than 0 should be colored in a red hue and squares with values greater than 0 in a blue hue. And then I would like squares with values of 0 to be colored white. However, nothing ends up being white. How can I fix this?
All three squares in that first column should be white.
library(lattice)
cc = colorRampPalette( c("red", "white","blue"))
trellis.par.set(regions=list(col=cc(20)))
x = c(1,2,3,1,2,3,1,2,3)
y = c(1,1,1,2,2,2,3,3,3)
z = c(0,-2,-3,0,2,3,0,1,-1)
df = data.frame(x,y,z)
p <- levelplot(z~x*y, df,
panel=function(...) {
arg <- list(...)
panel.levelplot(...)
})
print(p)
Update:
Here is a reproducible example that attempts to fix it, but still isn't quite right:
Here is a dataframe
df
:
x y z
1 1 1 -0.17457167
2 2 1 0.93407856
3 3 1 0.55129545
4 4 1 0.97388216
5 5 1 -1.00000000
6 6 1 0.52883410
7 7 1 -1.00000000
8 8 1 0.85112829
9 9 1 -1.00000000
10 10 1 1.00000000
11 11 1 -0.87714166
12 12 1 1.00000000
13 13 1 -0.95403260
14 14 1 1.00000000
15 15 1 -0.91600501
16 16 1 1.00000000
17 17 1 -1.00000000
18 18 1 -0.38800669
19 19 1 -0.52110322
20 20 1 0.00000000
21 21 1 -0.08211450
22 22 1 0.55390723
23 23 1 1.00000000
24 24 1 -0.04147514
25 25 1 -1.00000000
26 26 1 -0.39751358
27 27 1 -0.99550773
28 28 1 0.00000000
29 29 1 0.20737568
30 30 1 0.00000000
31 31 1 0.00000000
32 32 1 0.00000000
33 33 1 -0.26702883
And then here is the code:
cc = colorRampPalette( c("red", "white","blue"))
trellis.par.set(regions=list(col=cc(21)))
zrng <- range(z) # what's the range of z
tol <- 1e-2 # what tolerance is necessary?
colorBreaks <- c(
seq(zrng[1] - 0.01, 0 - tol, length.out = 11),
seq(0 + tol,zrng[2] + 0.01,length.out = 10))
p <- levelplot(z~x*y, df,
at = colorBreaks,
panel=function(...) {
arg <- list(...)
panel.levelplot(...)
})
print(p)
It produces this plot, which does not have a slot for the color white in the spectrum:
As thelatemail pointed out, cc(20)
will never produce white ("#FFFFFF"
). You have to use an odd number for the middle value of the color ramp to be represented exactly (checkout cc(3)
vs. cc(4)
).
Now, you need to set the at
argument for levelplot
to set breakpoints for the colors. The default is at = pretty(z)
:
#[1] -3 -2 -1 0 1 2 3
But you don't want 0 to be a breakpoint. You want it to have it's own color, and align with the middle of the color ramp.
You can achieve that by setting breakpoints as close to 0 as necessary (within some tol
) to prevent any other values from mapping to white. The rough idea is to leave a little spot for 0 by doing something like this at = c(seq(-3.01, -0.00001, length.out = 11), seq(0.00001, 3.01, length.out = 11))
or using the similar method shown below. Because the color ramp has an odd number of values, the sequence needs an even number of values. (i.e. a color ramp of 3 colors can be divide by 2 breakpoints, but a color ramp of 4 values can be divided by 3 breakpoints)
trellis.par.set(regions=list(col=cc(21)))
# Define a sequence of breaks for the at argument to levelplot.
zrng <- range(z) # what's the range of z
tol <- 1e-5 # what tolerance is necessary?
colorBreaks <- c(
seq(zrng[1] - 0.01, # adding a small buffer on end
0 - tol,
length.out = 11),
seq(0 + tol,
zrng[2] + 0.01,
length.out = 11))
# note, I chose length.out = 11.
# Don't do more than roughly ceiling((# of colors) / 2)
p <- levelplot(z~x*y, df,
at = colorBreaks,
panel=function(...) {
arg <- list(...)
panel.levelplot(...)
})