Randomly generate 3 distinct colors - r

I've looked at this one but does not help with the random part. Is there a better way to randomly generate 3 different colors so that the square, the circle, and the text stand out visually from one another in the code below. The colors have to be randomly generated and also distinct enough from one another. The current code works maybe only half the time
plot(0, type = "n", xlim = c(0,10), ylim = c(0,10),
ann = FALSE, axes = FALSE, asp = 1)
cols = colorRampPalette(sample(2:9,2), alpha = TRUE)(8)
polygon(x = c(1,9,9,1), y = c(1,1,9,9), border = NA, col = cols[1])
symbols(x = 5, y = 5, circles = 4, inches = FALSE,
add = TRUE, bg = cols[4], fg = NA)
text(x = 5, y = 5, labels = "Hi", col = cols[7], font = 2, cex = 3)
EXAMPLES
GOOD
POLYGON: "#FF00FFFF", CIRCLE: "#916DFFFF", TEXT: "#24DAFFFF"
BAD
POLYGON: "#00FFFFFF", CIRCLE: "#51E3E3FF", TEXT: "#A2C7C7FF"

My original answer, which uses the hcl color space, often generated color combinations that were hard to distinguish. This updated answer uses the Lab color space, which is scaled based on the perceptual distance between colors, so similar distances in Lab space should correspond to similar perceptual color differences. In Lab, L is luminance or brightness on a scale of 0 to 100. a represents green to red, and b represents blue to yellow, with both on a scale of -100 to 100.
The code below generates two random values for a and b. If we think of these two values as representing a point in the ab plane, we generate two more colors with maximum perceptual distance from each other by rotating this point first by 120 degrees and then by 240 degrees. We then choose a single L value to give us three equally spaced colors.
Below I've packaged this in a function to make it easy to generate several plots with random colors. I've also set a minimum absolute value for a and b so that we don't get colors that are too similar, and included an Lval argument for choosing the L value of the Lab colors.
Based on a several runs, it looks like this approach performs much better than my original hcl version (although this may be due not only to the use of Lab space instead of hcl space, but also because I used only one dimension of hcl space but two dimensions of Lab space).
library(colorspace)
random.colors = function(Lval=80, ABmin=50) {
# 120 deg rotation matrix
aa = 2*pi/3
rot = matrix(c(cos(aa), -sin(aa), sin(aa), cos(aa)), nrow=2, byrow=TRUE)
# Generate random A and B points in LAB space
x = runif(2, ABmin, 100) * sample(c(-1,1), 2,replace=TRUE)
# Create three equally spaced colors in Lab space and convert to RGB
cols = LAB(cbind(rep(Lval,3), rbind(x, x %*% rot, x %*% rot %*% rot)))
cols = rgb(convertColor(cols#coords, from="Lab", to="sRGB"))
plot(0, type = "n", xlim = c(0,10), ylim = c(0,10),
ann = FALSE, axes = FALSE, asp = 1)
polygon(x = c(1,9,9,1), y = c(1,1,9,9), border = NA, col = cols[1])
symbols(x = 5, y = 5, circles = 4, inches = FALSE,
add = TRUE, bg = cols[2], fg = NA)
text(x = 5, y = 5, labels = "Hi", col = cols[3], font = 2, cex = 3)
}
par(mfrow=c(3,3), mar=rep(0,4))
replicate(9,random.colors())
For simplicity, the example above constrains the a and b values to be a constant distance from the origin (in ab-space) and uses the same L value for all three colors. You could instead extend this method to use all three dimensions of the Lab space. In addition, instead of requiring a constant distance from the origin, you could pick the first color at random and then require that the next two colors be picked such that all three colors are maximally separated from each other in Lab space.
Original Answer
You could generate colors that are equally spaced in the hue dimension (that is, have the maximum possible hue separation from each other). For example:
set.seed(60)
cols = hcl(runif(1,0,359.99) + c(0,120,240), 100, 65)
plot(0, type = "n", xlim = c(0,10), ylim = c(0,10),
ann = FALSE, axes = FALSE, asp = 1)
polygon(x = c(1,9,9,1), y = c(1,1,9,9), border = NA, col = cols[1])
symbols(x = 5, y = 5, circles = 4, inches = FALSE,
add = TRUE, bg = cols[2], fg = NA)
text(x = 5, y = 5, labels = "Hi", col = cols[3], font = 2, cex = 3)
Here are nine more random draws. As you can see, there are some combinations that don't work too well. But perhaps you can play around with different ways of slicing up the color space to see if you can get something better.

One super simple way would be to sample among the eight "standard" colours.
par(mar=c(0, 0, 0, 0))
set.seed(1)
plot(0, type = "n", xlim = c(0,10), ylim = c(0,10),
ann = FALSE, axes = FALSE, asp = 1)
cols <- sample(2:8, 3)
polygon(x = c(1,9,9,1), y = c(1,1,9,9), border = NA, col = cols[1])
symbols(x = 5, y = 5, circles = 4, inches = FALSE,
add = TRUE, bg = cols[2], fg = NA)
text(x = 5, y = 5, labels = "Hi", col = cols[3], font = 2, cex = 3)

Related

Color by group and legend in plot3D

I'm trying to create my own 3D histogram using plot3D, so I took a look at this page as a starter before applying it on my own data. The code is clear and straightforward enough but I wonder how I can group rows of data (formatted as matrix indices in the example VADeaths data) into fewer categories (e.g., 50-54 & 55-59 --> "Fifties", 60-64 & 65-69 --> "Sixties", 70-74 --> "> 70") and assign them to different colors (e.g, "Fifties: blue", "Sixties: yellow", "> 70: white")? And, also, to have this information displayed on the legend to the right of the 3D plot?
Below is a working example listed on the reference page.
# install.packages("plot3D")
library(plot3D)
data(VADeaths)
# plotting
hist3D (x = 1:5, y = 1:4, z = VADeaths,
bty = "g", phi = 20, theta = -60,
xlab = "", ylab = "", zlab = "", main = "VADeaths",
col = "#0072B2", border = "black", shade = 0.8,
ticktype = "detailed", space = 0.15, d = 2, cex.axis = 1e-9)
# label x axis: age group (this part is to be sorted into fewer categories)
text3D(x = 1:5, y = rep(0.5, 5), z = rep(3, 5),
labels = rownames(VADeaths),
add = TRUE, adj = 0)
# label y axis: gender by urbal/rural residency
text3D(x = rep(1, 4), y = 1:4, z = rep(0, 4),
labels = colnames(VADeaths),
add = TRUE, adj = 1)
I've referenced this earlier post but can't seem to understand it.
It'll be really appreciated if someone could share some tips on this.

contourplot color and labels options in Lattice for R

I am quite new to Lattice and I am stuck with some possibly basic coding. I am using shapefiles and geoTIFFS to produce maps of animals distribution and in particular I have:
1 x point shapefile
2 x geoTIFF
1 x polygon shapefile
I am overlapping a levelplot of one of the geoTIFF (UD generated with adehabitatHR) with a contourplot of the same geoTIFF at specific intervals (percentile values), a contourplot of the second geoTIFF (depth raster from ETOPO2) for three specific values (-200, -1000 and -2000), the point shapefile (animal locations) and the polygon shapefile (land). All works fine but I need to change the font size of contour plot labels, their length (i.e. from 0.12315 to 0.123) and positioning for all the contourplots. For the depth contourplot I would like to change the style of each line in something like "continous line", "dashed line" and "point line", and for the contourplot of the UD I would like to change the color of each line using a yellow to red palette.
As far as I understand, I should use panel functions to implement these changes (e.g. Controlling z labels in contourplot) but i am not quite sure how to do it. Part of my code to generate the "plot":
aa <-
quantile(
UD_raster,
probs = c(0.25, 0.75),
type = 8,
names = TRUE
)
my.at <- c(aa[1], aa[2])
depth<-c(-100, -200, -2000)
levelplot(
UD_raster,
xlab = "",
ylab = "",
margin = FALSE,
contour = FALSE,
col.regions = viridis(100),
main = "A",
maxpixels = 2e5
) + layer(sp.polygons(Land, fill = "grey40", col = NA)) + layer(sp.points(locations, pts = 2, col = "red")) + contourplot(
UD_raster,
at = my.at,
labels = TRUE,
margin = FALSE
) + contourplot(
ETOPO2,
at = depth,
labels = TRUE,
margin = FALSE
)
A simplified image, with no UD layer and no point shapefile can be found here and as you can see it is pretty messy. Thanks for your help.
So far for the ETOPO2 countourplot I have solved by eliminating the labels and adding the argument lty to style the line. Because I can't figure out how to use lty with different values for each single line in my contour, I have replicated the contourplot function three times on the same surface, one for each contour I am interested into (this was easy because I only need three contours).
For the position, font and font size of the labels of the remaining contourplot I have used
labels = list(cex = 0.8, "verdana"),
label.style = "flat"
To "shorten" the length of the labels I have used the function round where I specify to which decimal digit to round number.
So now my new code looks like:
aa <-
quantile(
UD_raster,
probs = c(0.25, 0.75),
type = 8,
names = TRUE
)
my.at <- c(aa[1], aa[2])
my.at <- round(my.at, 3)
levelplot(
UD_raster,
xlab = "",
ylab = "",
margin = FALSE,
contour = FALSE,
col.regions = viridis(100),
main = "A",
maxpixels = 2e5
) + layer(sp.polygons(Land, fill = "grey40", col = NA)) + layer(sp.points(positions, pts = 2, col = "red")) + contourplot(
UD_raster,
at = my.at,
labels = list(cex = 0.8, "verdana"),
label.style = "flat",
margin = FALSE
) + contourplot(
ETOPO2,
at = -200,
labels = FALSE,
margin = FALSE,
lty = 1,
pretty = TRUE
) + contourplot(
ETOPO2,
at = -1000,
labels = FALSE,
margin = FALSE,
lty = 2,
pretty = TRUE
) + contourplot(
ETOPO2,
at = -2000,
labels = FALSE,
margin = FALSE,
lty = 3,
pretty = TRUE
)
As one could expect, it takes a bit longer to produce the plot. Still no idea on how to change the colors of the UD contourplot.

How to consistently skip (or not skip) x axis labels in R base graphics

I want to create a figure where for various reasons I need to specify the axis labels myself. But when I specify my labels (some have one digit, some two digits) R suppresses every other two-digit label because it decides there isn't enough room to show them all, but it leaves all of the one-digit labels, leaving the axis looking lopsided.
Is there a way to suppress labels consistently across the whole axis, based on whether any of them need to be skipped? Note: I have a lot of plots with varying scales, so I was looking for something I could use for all of them - I don't want to render all the labels for every plot, or to skip every other label in every plot. Suppressing labels will be desirable for some plots and not for others. I just want to skip every other label consistently, if that's what R chooses to do for the particular plot.
(Here is an example figure of what I mean. What I want is for the "6%" label to also be suppressed in the x axis.)
Example code:
library(labeling)
df <- data.frame("estimate" = c(9.81, 14.29, 12.94),
"lower" = c(4.54, 6.25, 5.12),
"upper" = c(12.85, 20.12, 15.84))
ticks <- extended(min(df$lower), max(df$upper), m = 5, only.loose = TRUE,
Q=c(2, 5, 10))
png("examplePlot.png", width = 1200, height = 900, pointsize = 10, res = 300)
bars <- barplot(df$estimate, horiz = TRUE, col = "white", border = NA,
xlim = c(min(ticks), max(ticks)), xaxt = "n", main = "Example")
arrows(df$lower, bars, df$upper, bars, code = 3, angle = 90, length = 0.03)
points(df$estimate, bars, pch = 20)
tickLabels <- paste(ticks, "%", sep = "")
axis(1, at=ticks, labels = tickLabels, cex.axis=1)
axis(2, at = bars, labels = c("c", "b", "a"), lwd = 0, las = 2)
dev.off()
This depends on the size of the plot, so you'll have to plot each label separately:
axis(1, lwd.ticks = 1, labels = FALSE, at = ticks) # plot line and ticks
i <- seq(1,length(ticks),2) # which labels to plot
for(ii in i)
axis(1, at = ticks[ii], labels = tickLabels[ii], cex.axis = 1, lwd = 0)

Add an extra color in wes_palette

I know this is a little rookie, but I've created a wes_palette like this:
rnq<-wesanderson::wes_palette("Zissou", 5, "discrete")
to plot an interval of values from 0 to 4.
But I have a single value in my data that is 9, and I would like to disclose it, using, for example, black.
Im using spplot:
rng = seq(0, 4, length=5)
rnq<-wesanderson::wes_palette("Zissou", 5, "discrete")
scale = list("SpatialPolygonsRescale", layout.scale.bar(),
offset = c(-900000,-1100000),
scale = 300000, fill=c("transparent","black"))
text1 = list("sp.text", c(-900000,-1150000), "0")
text2 = list("sp.text", c(-550000,-1150000), "300 Km")
text4<-list("sp.text", c( -730000, -1270000), cex=0.6, "Projection: EPSG 102003")
arrow = list("SpatialPolygonsRescale", layout.north.arrow(),
offset = c(-900000, -400000), scale = 200000)
spplot(spdf_img, "re1", col = "white", at = rng,
col.regions = rnq,
colorkey = list(
space = "bottom", labels=list(
at=round(rng, 1))),
sp.layout=list(scale, text1, text2, text4, arrow),
par.settings = list(axis.line = list(col = NA)))
Mainly, I don't know how to define my interval("rnq"), plus the color ramp and then the cuts (at argument in spplot) to change the value 9 to black.
The map is this:
gh
And the white polygon is the one I need to change the color.
Add the value 9 and the color black at your current rng and rnq:
rng <- c(rng,9)
rnq <- c(rnq,"black")
Then your code should work.
Yes, I managed to fix it, with more or less your code.
rng = c(seq(0, 4, length=5), 9)
rnq = c("#3B9AB2", "#78B7C5", "#EBCC2A", "darkorange1", "#F21A00")
Anyway, Thank you ;)

Complete missing lines using rgl grid3d

Does anyone know of a way to 1) complete the missing gridlines in the grid3d call for y, and 2) draw horizontal gridlines to close the top of the grids constructed by the grid3d calls for x and y? I've played around with various combinations of pretty calls within grid3d to no avail and am wondering if this is an rgl quirk or a misspecification on my part. Additionally, I'd like to extend the vertical axis numbering to wherever the closed grids end up.
library(rgl)
cpts <- seq(0, 2, length = 40)
spts <- seq(0, 1, length = 20)
grid <- expand.grid(s=spts, c=cpts)
UFn <- function(s,c){c^(0.5) - exp(s) + 1}
U <- UFn(grid$s, grid$c)
open3d()
rgl.surface(x = spts, y = matrix(U,nrow = 40, ncol = 20), z = cpts,
coords = c(1,3,2), specular = "black")
axes3d("x", at = pretty(spts, n = 2), color = "black")
axes3d("y", at = pretty(cpts, n = 5), color = "black")
axes3d("z--", color = "black")
grid3d("x")
grid3d("y", at = pretty(spts, n = 2))
title3d(xlab ='s', ylab = 'c', zlab = 'U', color = "black")
rgl.snapshot("3d.png")
I would say it is a bug. You don't get any z-lines when using grid3d("y",n=2) even though it should be the same. You can work around it by using the list specification of at, setting the x element of the list, eg:
grid3d("y", at = list(x=pretty(spts, n = 2)))

Resources