Convert colors to imitate greyscale printing - r

When reading this question, I started to think whether it would be possible to convert colors to imitate an average greyscale printer (assuming that your screen is calibrated)? Finding an approvable approximation would save paper.
For example, how to convert these colors to see whether the light and dark blues and reds can be differentiated on paper?
temp <- rgb2hsv(239, 138, 98, maxColorValue=255)
Rl <- hsv(h = temp[1,], s = 0.5, v = 1)
Rd <- hsv(h = temp[1,], s = 0.5, v = 0.4)
temp <- rgb2hsv(103, 169, 207, maxColorValue=255)
Cl <- hsv(h = temp[1,], s = temp[2,], v = 1)
Cd <- hsv(h = temp[1,], s = temp[2,], v = 0.4)
plot(1:4, type = "p", col = c(Rl, Rd, Cl, Cd), pch = 19, cex = 8, xlim = c(0,5), ylim = c(0,5))

Use an HCL palette with chroma set to zero to create greyscale values that are indistinguishable to the human eye.
library(colorspace)
n <- 10
cols <- rainbow_hcl(n)
plot(seq_len(n), cex = 5, pch = 20, col = cols)
greys <- rainbow_hcl(n, c = 0)
plot(seq_len(n), cex = 5, pch = 20, col = greys)
If you want to generate the greys from your original colours, use the scales package.
library(scales)
greys2 <- col2hcl(cols, c = 0)
plot(seq_len(n), cex = 5, pch = 20, col = greys2)

The desaturate() function in the `colorspace package can also be used to convert colors to their gray levels only. This collapses chroma (colorfulness) in the HCL (or polar CIELUV) representation of the colors.
plot(1:4, type = "p",
col = colorspace::desaturate(c(Rl, Rd, Cl, Cd)),
pch = 19, cex = 8, xlim = c(0,5), ylim = c(0,5))

The col2grey (and/or col2gray) function in the TeachingDemos package uses one common method for doing this. The idea is to see what your colors will look like when printed or copied in grayscale instead of color.

You can use one of the 3 conversion algorithms outlined here, and just determine if they're outside whatever tolerance you think is too similar.

Related

Attempting to plot simple raster map but coordinate labels showing as 1s

I'm trying to run the below code to produce a simple map with data overlayed but I keep having the same issue with the gridline labels, they should read standard long/lat coords but all I'm getting are 1s.
Everything works until the bottom line of code but I've put all in incase there's anything earlier that might be causing the issue.
tern<-read.table("terndatanumbers2.txt",header=TRUE)
tern.coords <- SpatialPoints(data.frame(x=terns$long,y = terns$lat))
plot(tern.coords, pch = 15, cex = 0.5, col = "blue")
world <- rworldmap::getMap(resolution = "high")
e <- raster::extent(-32,-25,36,40)
azores <- as(raster::crop(world, e), "SpatialPolygons")
raster::crs(tern.coords) <- raster::crs(azores)
plot(azores, col = "gray", xlim = c(-32,-25), ylim = c(36, 40))
plot(tern.coords, pch = 15, cex = 0.5, col = "blue",xlim = c(-32,-25), ylim = c(36, 40), add = T)
plot(gridlines(azores), lty = 3, col = "gray", lwd = 0.5,xlim = c(-32,-25), ylim = c(36, 40), add = T)
text(labels(gridlines(azores)))
plot produced
Any help on this would be appreciated. Thanks.

Plot in R not recognizing pch numbers

I am trying to plot in base R with the regular plot() fcn. However, when passing a vector of which pch to use, it will not plot the pch, it will only plot the number '1' instead of the shape of the pch I am calling.
Generating some data (my real data has over 400 rows for both the loads and meta objects:
loads <- data.frame(PC1 = c(11.32, 13.18, 12.82, 24.70), PC2 = c(-23.05, -24.71, -20.28, 10.09))
row.names(loads) <- c("100_A", "100_B", "100_C", "100_Orig")
meta <- data.frame(pch = c(17, 17, 17, 16), color = c("red", "red", "blue", "blue"))
row.names(meta) <- row.names(loads)
To plot:
x <- loads[, 1] ; y <- loads[, 2]
pch <- meta$pch
col <- meta$color
plot(x, y,
col = col, pch = pch, cex = 2, lwd = 4,
xlab = paste("PC1"), ylab = paste("PC2"))
Now, this will graph the correct color (red and blue) in the order I have them in the vector; the real issue becomes the plotting the pch. Instead of a circle (pch = 16) or a triangle (pch = 17) it's plotting a red or blue number 1 instead! I have included a pic of what my data is actually doing.
Thinking that the pch vector I am passing cannot have quotes around it, I have removed the quotes with the following code:
pch <- meta$pch
pch <-as.vector(noquote(pch))
class(pch)
[1] "character"
However, this generates the same results (getting a number 1 plotted). Interestingly, when use this code, it works fine. It turns all my colors to blue, and I get nice blue circles.
plot(x, y,
col = "blue, pch = 16, cex = 2, lwd = 4,
xlab = paste("PC1"), ylab = paste("PC2"))
This tells me that the plot function isn't recognizing my long vector composed of pch 16 and 17's mixed in.
Alternatively, if I use the rep function to generate my pch vector, a test shows it works fine. But I have over 400 rows. I cannot manually type rep for each pch. I will be here for eternity typing that out.
Any suggestions on what to do?????
Try defining the col as character and the pch as numeric like this:
plot(x, y,
col = as.character(col), pch = as.numeric(pch), cex = 2, lwd = 4,
xlab = paste("PC1"), ylab = paste("PC2"))

Randomly generate 3 distinct colors

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)

How to plot a 3D surface of a 3D matrix with R

I have 3D matrix of floating point numbers and I would like to produce a smoothed 3D surface of this matrix using R. Any suggestions are welcome. Thanks
Now I am using scatterplot3d ... But this function did not produce a smoothed surface
x<-read.table("/Users/me/Desktop/data.txt")
scatterplot3d(x$V1, x$V2, x$V3, highlight.3d = TRUE, angle = 30, col.axis = "blue", col.grid = "lightblue", cex.axis = 1.3, cex.lab = 1.1, pch = 20)
I think that mba.surf from the MBA package would be a good choice for the smoothing, and as larrydag above suggests, persp would be good to image it. The code below is from the help page for the mba.surf function (swap LIDAR for your 3 column dataframe):
data(LIDAR)
mba.int <- mba.surf(LIDAR, 300, 300, extend=TRUE)$xyz.est
# Two ways of imaging....
image(mba.int, xaxs="r", yaxs="r")
persp(mba.int, theta = 135, phi = 30, col = "green3", scale = FALSE,
ltheta = -120, shade = 0.75, expand = 10, border = NA, box = FALSE)
If you are able to create a 2D matrix (x,y) with the value being the z-axis value you could use the following
persp
Here is an example from R Graph Gallery. persp example
require(misc3d)
a <- 2/5
wsqr <- 1 - a^2
w <- sqrt(wsqr)
denom <- function(a,w,u,v) a*((w*cosh(a*u))^2 + (a*sin(w*v))^2)
fx <- function(u,v) -u + (2*wsqr*cosh(a*u)*sinh(a*u)/denom(a,w,u,v))
fy <- function(u,v) 2*w*cosh(a*u)*(-(w*cos(v)*cos(w*v)) - (sin(v)*sin(w*v)))/denom(a,w,u,v)
fz = function(u,v) 2*w*cosh(a*u)*(-(w*sin(v)*cos(w*v)) + (cos(v)*sin(w*v)))/denom(a,w,u,v)
parametric3d(fx = fx, fy = fy, fz = fz,
umin = -17,
umax = 17,
vmin = -77,
vmax = 77,
n = 100,
color = c("grey17","grey21","red4","darkred","red4","grey21","grey17"),
engine = "rgl")

Modifying width of outline in a pie chart in R--what is the equivalent of lwd parameter for pie()?

I'm using base R plotting functions to produce a pie chart and I want to change the line thickness of the outlines of each pie segment. ?pie seems to indicate that I can add optional graphic parameters, but adding lwd= does not appear to work. Anyone have any clues as to how I might be able to do this. I'm not yet proficient in producing pie charts in ggplot, and would like to stick with base R plotting (if possible).
library(RColorBrewer)
x1 <- data.frame(V1 = c(200, 100)) ## generate data
row.names(x1) <- c("A", "B")
x1$pct <- round((x1$V1/sum(x1$V1))*100, 1)
lbls1 <- paste(row.names(x1), "-(",x1$pct, '%)', sep='') ## add some informative stuff
pie(x1$V1, labels=lbls1, col=tail(brewer.pal(3, 'PuBu'), n=2),
main=paste('My 3.1415'), cex=1.1, lwd= 3)
Notice lwd= does not increase line thickness like it would in other base plotting.
Anyone have any clues?
The call to polygon and lines within pie does not pass ... or lwd
...
polygon(c(P$x, 0), c(P$y, 0), density = density[i], angle = angle[i],
border = border[i], col = col[i], lty = lty[i])
P <- t2xy(mean(x[i + 0:1]))
lab <- as.character(labels[i])
if (!is.na(lab) && nzchar(lab)) {
lines(c(1, 1.05) * P$x, c(1, 1.05) * P$y)
....
You can get around this by setting par(lwd = 2) (or whatever) outside and prior to your call to pie
i.e.
# save original settings
opar <- par(no.readonly = TRUE)
par(lwd = 2)
pie(x1$V1, labels=lbls1, col=tail(brewer.pal(3, 'PuBu'), n=2),
main=paste('My 3.1415'), cex=1.1)
par(lwd = 3)
# reset to original
par(opar)
At the moment, the function inside pie that does the actual drawing is polygon and here is how it is called:
polygon(c(P$x, 0), c(P$y, 0), density = density[i], angle = angle[i],
border = border[i], col = col[i], lty = lty[i])
Notice there is no lwd argument and more critically no ... argument to accept arguments that might not have been hard coded.
Create a new pie2 function. First type pie, copy the code and make a few changes:
pie2 <-
function (x, labels = names(x), edges = 200, radius = 0.8, clockwise = FALSE,
init.angle = if (clockwise) 90 else 0, density = NULL, angle = 45,
col = NULL, border = NULL, lty = NULL, main = NULL, lwd=1,...)
{
................
polygon(c(P$x, 0), c(P$y, 0), density = density[i], angle = angle[i],
border = border[i], col = col[i], lty = lty[i], lwd=lwd )
.................
}
pie2(x1$V1, labels=lbls1, col=tail(brewer.pal(3, 'PuBu'), n=2),
main=paste('My 3.1415'), cex=1.1, lwd=5)

Resources