R Plotly show string on contour plots - r

I have overlayed two contour plots:
library(plotly)
cluster_count <- 5
volcan <- plot_ly(z = ~volcano,
type = "contour",
contours = list(
coloring= "fill",
showlines = F
))
cluster_matrix <- volcano
cluster_matrix[cluster_matrix < 100] <- 1
cluster_matrix[cluster_matrix <= 120 & cluster_matrix >= 100] <- 2
cluster_matrix[cluster_matrix < 140 & cluster_matrix >= 120] <- 3
cluster_matrix[cluster_matrix <= 160 & cluster_matrix >= 140] <- 4
cluster_matrix[cluster_matrix > 160] <- 5
cluster_name_matrix <- cluster_matrix
cluster_name_matrix[cluster_matrix ==1] <- "Eins"
cluster_name_matrix[cluster_matrix ==2] <- "Zwei"
cluster_name_matrix[cluster_matrix ==3] <- "Drei"
cluster_name_matrix[cluster_matrix ==4] <- "Vier"
cluster_name_matrix[cluster_matrix ==5] <- "Funf"
volcan %>% add_contour(cluster_matrix,
type = "contour",
opacity =1,
text=cluster_name_matrix,
hovertemplate = 'Cluster: %{text}<extra></extra>',
autocontour = F,
line=list(color="orange"),
contours = list(
start = 1,
showlabels = T,
coloring= "lines",
end = cluster_count,
size = 1,
showlines = T
))
Is it possible to have a plot like this:
Like I did for the hovering text? Thanks for tips and suggestions in advance!

What you've been looking for is the add_annotations() function. In the code below, I write a function that retrieves a random coordinate pair for each level and then passes the corresponding coordinates to the add_annotations() function. Note that I stored your contour plot in the variable p:
library(purrr)
# Custom function
find_rand_annotation_index <- function(name_matrix, string){
d <- which(name_matrix == string, arr.ind = TRUE)
d2 <- as.data.frame(d[sample(nrow(d), size = 1), , drop = FALSE])
cbind(d2, string)
}
# Get 5 random coordinates to plot the labels
text_coords <- purrr::map_dfr(c("Eins", "Zwei", "Drei", "Vier", "Funf"), ~ find_rand_annotation_index(cluster_name_matrix, .x))
# Plot the annotations on the contour plot
p %>%
add_annotations(
x = text_coords$col,
y = text_coords$row,
text = text_coords$string,
font = list(color = "IndianRed"),
showarrow = F
)
The positioning of the labels may not be to your liking (because the coordinates are chosen randomly), but you may want to do something about it in your code.

Related

R Programming other alternatives for plot

I wonder how you can simplify these two :
plot (payroll,wins)
id = identify(payroll, wins,labels = code, n = 5)
plot (payroll,wins)
with(data, text(payroll, wins, labels = code, pos = 1, cex=0.5))
using other alternatives - pch() dan as.numeric()?
Not sure it's easier but you change pch during identification as below (taken from the R-help). Every time you click empty point change to filled-in dot.
# data simulation
data <- data.frame(payroll = rnorm(10), wins = rnorm(10), code = letters[1:10])
identifyPch <- function(x, y = NULL, n = length(x), plot = FALSE, pch = 19, ...)
{
xy <- xy.coords(x, y)
x <- xy$x
y <- xy$y
sel <- rep(FALSE, length(x))
while (sum(sel) < n) {
ans <- identify(x[!sel], y[!sel], labels = which(!sel), n = 1, plot = plot, ...)
if(!length(ans)) {
break
}
ans <- which(!sel)[ans]
points(x[ans], y[ans], pch = pch)
sel[ans] <- TRUE
}
## return indices of selected points
which(sel)
}
if(dev.interactive()) { ## use it
with(data, plot(payroll,wins))
id = with(data, identifyPch(payroll, wins))
}

fixed fill for different sections of a density plot with ggplot

Given draws from a rnorm, and cutoff c I want my plot to use the following colors:
Red for the section that is to the left of -c
Blue for the section in between -c and c
and Green for the section that is to the right of c
For example, if my data is:
set.seed(9782)
mydata <- rnorm(1000, 0, 2)
c <- 1
I want to plot something like this:
But if my data is all to the right of c the whole plot should be green. Similarly, if all is between -c and c or to the left of -c the plot should be all red or blue.
This is the code I wrote:
MinD <- min(mydata)
MaxD <- max(mydata)
df.plot <- data.frame(density = mydata)
if(c==0){
case <- dplyr::case_when((MinD < 0 & MaxD >0) ~ "L_and_R",
(MinD > 0) ~ "R",
(MaxD < 0) ~ "L")
}else{
case <- dplyr::case_when((MinD < -c & MaxD >c) ~ "ALL",
(MinD > -c & MaxD > c) ~ "Center_and_R",
(MinD > -c & MaxD <c) ~ "Center",
(MinD < -c & MaxD < c) ~ "Center_and_L",
MaxD < -c ~ "L",
MaxD > c ~ "R")
}
# Draw the Center
if(case %in% c("ALL", "Center_and_R", "Center", "Center_and_L")){
ds <- density(df.plot$density, from = -c, to = c)
ds_data_Center <- data.frame(x = ds$x, y = ds$y, section="Center")
} else{
ds_data_Center <- data.frame(x = NA, y = NA, section="Center")
}
# Draw L
if(case %in% c("ALL", "Center_and_L", "L", "L_and_R")){
ds <- density(df.plot$density, from = MinD, to = -c)
ds_data_L <- data.frame(x = ds$x, y = ds$y, section="L")
} else{
ds_data_L <- data.frame(x = NA, y = NA, section="L")
}
# Draw R
if(case %in% c("ALL", "Center_and_R", "R", "L_and_R")){
ds <- density(df.plot$density, from = c, to = MaxD)
ds_data_R <- data.frame(x = ds$x, y = ds$y, section="R")
} else{
ds_data_R <- data.frame(x = NA, y = NA, section="R")
}
L_Pr <- round(mean(mydata < -c),2)
Center_Pr <- round(mean((mydata>-c & mydata<c)),2)
R_Pr <- round(mean(mydata > c),2)
filldf <- data.frame(section = c("L", "Center", "R"),
Pr = c(L_Pr, Center_Pr, R_Pr),
fill = c("red", "blue", "green")) %>%
dplyr::mutate(section = as.character(section))
if(c==0){
ds_data <- suppressWarnings(dplyr::bind_rows(ds_data_L, ds_data_R)) %>%
dplyr::full_join(filldf, by = "section") %>% filter(Pr!=0) %>%
dplyr::full_join(filldf, by = "section") %>% mutate(section = ordered(section, levels=c("L","R")))
ds_data <- ds_data[order(ds_data$section), ] %>%
filter(Pr!=0) %>%
mutate(Pr=scales::percent(Pr))
}else{
ds_data <- suppressWarnings(dplyr::bind_rows(ds_data_Center, ds_data_L, ds_data_R)) %>%
dplyr::full_join(filldf, by = "section") %>% mutate(section = ordered(section, levels=c("L","Center","R")))
ds_data <- ds_data[order(ds_data$section), ] %>%
filter(Pr!=0) %>%
mutate(Pr=scales::percent(Pr))
}
fillScale <- scale_fill_manual(name = paste0("c = ", c, ":"),
values = as.character(unique(ds_data$fill)))
p <- ggplot(data = ds_data, aes(x=x, y=y, fill=Pr)) +
geom_area() + fillScale
Alas, I cannot figure out how to assign the colors to the different sections while keeping the percentages as labels for the colors.
We use the density function to create the data frame we'll actually plot. Then, We use the cut function to create groups using ranges of the data values. Finally, we calculate the probability mass for each group and use those as the actual legend labels.
We also create a labeled vector of colors to ensure that the same color always goes with a given range of x-values, regardless of whether the data contains any values within a given range of x-values.
The code below packages all this into a function.
library(tidyverse)
library(gridExtra)
fill_density = function(x, cc=1, adj=1, drop_levs=FALSE) {
# Calculate density values for input data
dens = data.frame(density(x, n=2^10, adjust=adj)[c("x","y")]) %>%
mutate(section = cut(x, breaks=c(-Inf, -1, cc, Inf))) %>%
group_by(section) %>%
mutate(prob = paste0(round(sum(y)*mean(diff(x))*100),"%"))
# Get probability mass for each level of section
# We'll use these as the label values in scale_fill_manual
sp = dens %>%
group_by(section, prob) %>%
summarise %>%
ungroup
if(!drop_levs) {
sp = sp %>% complete(section, fill=list(prob="0%"))
}
# Assign colors to each level of section
col = setNames(c("red","blue","green"), levels(dens$section))
ggplot(dens, aes(x, y, fill=section)) +
geom_area() +
scale_fill_manual(labels=sp$prob, values=col, drop=drop_levs) +
labs(fill="")
}
Now let's run the function on several different data distributions:
set.seed(3)
dat2 = rnorm(1000)
grid.arrange(fill_density(mydata), fill_density(mydata[mydata>0]),
fill_density(mydata[mydata>2], drop_levs=TRUE),
fill_density(mydata[mydata>2], drop_levs=FALSE),
fill_density(mydata[mydata < -5 | mydata > 5], adj=0.3), fill_density(dat2),
ncol=2)

Generating a sequence of equidistant points on polygon boundary

I am looking for a procedure that allows me to generate a sequence of equidistant points (coordinates) along the sides of an arbitrary polygon.
Imaging a polygon defined by the coordinates of its vertexes:
poly.mat <- matrix(c(0,0,
0,1,
0.5,1.5,
0.5,0,
0,0 # last row included to close the polygon
), byrow = T, ncol = 2)
colnames(poly.mat) <- c("x", "y")
plot(poly.mat, type = "l")
If the length of the sequence I want to generate is n (adjustable), how I can produce a sequence, starting at (0,0), of equidistant coordinates.
I got as far as calculating the perimeter of the shape with the geosphere package (which I believe I need)
library(geosphere)
n <- 50 # sequence of length set to be 50
perim <- perimeter(poly.mat)
perim/n # looks like every section needs to be 8210.768 something in length
You will have to write the code yourself. Sorry, there isn't a library function for every last detail of every last assignment. Assuming that each pair of points defines a line segment, you could just generate N points along each segment, as in
begin = [xbegin, ybegin ];
end = [xend, yend ];
xdist = ( xend - xbegin ) / nintervals;
ydist = ( yend - ybegin ) / nintervals;
then your points are given by [ xbegin + i * xdist, ybegin + i * ydist ]
Here is the solution I came up with.
pointDistance <- function(p1, p2){
sqrt((p2[,1]-p1[,1])^2) + sqrt((p2[,2]-p1[,2])^2)
}
getPos <- function(shp.mat, ll){
greaterLL <- shp.mat$cumdis > ll
if(all(greaterLL == FALSE)) return(poly.mat[nrow(poly.mat), c("x", "y")])
smallRow <- min(which(greaterLL)) # the smallest coordinate that has greater length
p.start <- shp.mat[smallRow-1, c("x","y")]
p.end <- shp.mat[smallRow, c("x","y")]
cumVal <- shp.mat$cumdis[smallRow]
prop <- (ll-shp.mat$cumdis[smallRow-1])/(shp.mat$cumdis[smallRow]-shp.mat$cumdis[smallRow-1])
p.start + (prop)* (p.end-p.start)
}
# shp1
poly.mat <- matrix(c(0,0,
0,1,
0.5,1.5,
0.5,0,
0,0
),byrow = T, ncol = 2)
colnames(poly.mat) <- c("x", "y")
poly.mat <- as.data.frame(poly.mat)
# Main fun
pointsOnPath <- function(shp.mat, n){
dist <- vector(mode = "numeric", length = nrow(shp.mat)-1)
for(i in 2:nrow(shp.mat)){
dist[i] <- pointDistance(p1 = shp.mat[i,], p2 = shp.mat[i-1,])
}
shp.mat$dist <- dist
shp.mat$cumdis <- cumsum(shp.mat$dist)
dis <- matrix(seq(from = 0, to = max(shp.mat$cumdis), length.out = n+1), ncol = 1)
out <- lapply(dis, function(x) getPos(shp.mat = shp.mat, ll = x))
out <- do.call("rbind", out)
out$dis <- dis
out[-nrow(out),]
}
df <- pointsOnPath(shp.mat = poly.mat, 5)
# Plot
plot(poly.mat$x, poly.mat$y, type = "l", xlim = c(0,1.5), ylim = c(0,1.5))
points(df$x, df$y, col = "red", lwd = 2)
There is room for improving the code, but it should return the correct result

Graphical output of density for the function gammamixEM (package mixtools)

I'm using the function gammamixEM from the package mixtools. How can I return the graphical output of density as in the function normalmixEM (i.e., the second plot in plot(...,which=2)) ?
Update:
Here is a reproducible example for the function gammamixEM:
x <- c(rgamma(200, shape = 0.2, scale = 14), rgamma(200,
shape = 32, scale = 10), rgamma(200, shape = 5, scale = 6))
out <- gammamixEM(x, lambda = c(1, 1, 1)/3, verb = TRUE)
Here is a reproducible example for the function normalmixEM:
data(faithful)
attach(faithful)
out <- normalmixEM(waiting, arbvar = FALSE, epsilon = 1e-03)
plot(out, which=2)
I would like to obtain this graphical output of density from the function gammamixEM.
Here you go.
out <- normalmixEM(waiting, arbvar = FALSE, epsilon = 1e-03)
x <- out
whichplots <- 2
density = 2 %in% whichplots
loglik = 1 %in% whichplots
def.par <- par(ask=(loglik + density > 1), "mar") # only ask and mar are changed
mix.object <- x
k <- ncol(mix.object$posterior)
x <- sort(mix.object$x)
a <- hist(x, plot = FALSE)
maxy <- max(max(a$density), .3989*mix.object$lambda/mix.object$sigma)
I just had to dig into the source code of plot.mixEM
So, now to do this with gammamixEM:
x <- c(rgamma(200, shape = 0.2, scale = 14), rgamma(200,
shape = 32, scale = 10), rgamma(200, shape = 5, scale = 6))
gammamixEM.out <- gammamixEM(x, lambda = c(1, 1, 1)/3, verb = TRUE)
mix.object <- gammamixEM.out
k <- ncol(mix.object$posterior)
x <- sort(mix.object$x)
a <- hist(x, plot = FALSE)
maxy <- max(max(a$density), .3989*mix.object$lambda/mix.object$sigma)
main2 <- "Density Curves"
xlab2 <- "Data"
col2 <- 2:(k+1)
hist(x, prob = TRUE, main = main2, xlab = xlab2,
ylim = c(0,maxy))
for (i in 1:k) {
lines(x, mix.object$lambda[i] *
dnorm(x,
sd = sd(x)))
}
I believe it should be pretty straight forward to continue this example a bit, if you want to add the labels, smooth lines, etc. Here's the source of the plot.mixEM function.

Cycle for plotting multiple graphs according to the number of factors

I've a factor vector containing 25 unique variables for categorizing two numeric variables (x,y)
I want to plot for each single factor a scatterplot
for (factor in Coordinates$matrixID) {
dev.new()
plot(grid, type = "n")
vectorField(Coordinates$Angle,Coordinates&Length,Coordinates$x,Coordinates$y,scale=0.15, headspan=0, vecspec="deg")
}
This function result in generating 63 identical graphs of overall data. I want 25 different graphs, one for each factor
Could you please help me,
Thanks
EDIT: Example given
library(VecStatGraphs2D)
Data <- data.frame(
x = sample(1:100),
y = sample(1:100),
angle = sample(1:100),
lenght = sample(1:100),
matrixID = sample(letters[1:25], 20, replace = TRUE))
for (factor in matrixID) {
dev.new()
plot(grid, type = "n") V
VectorField(Data$angle,Data$lenght,Data$x,Data$y,scale=0.15,headspan=0, vecspec="deg")
}
Not so tidy, but you may try something like:
library(plotrix)
library(VecStatGraphs2D)
Data <- data.frame(
x = sample(1:100),
y = sample(1:100), angle = sample(1:100), lenght = sample(1:100),
matrixID = sample(letters[1:4], 20, replace = TRUE))
for (i in unique(Data$matrixID))
{
dev.new()
Data1 <- subset(Data, matrixID == i)
plot(0:100, type = "n")
vectorField(Data1$angle,Data1$lenght,Data1$x,Data1$y,scale=0.15, headspan=0, vecspec="deg")
}
for your example, and
for (i in unique(Coordinates$matrixID))
{
dev.new()
Coordinates1 <- subset(Coordinates, matrixID == i)
plot(grid, type = "n")
vectorField(Coordinates1$Angle,Coordinates1&Length,Coordinates1$x,Coordinates1$y,scale=0.15, headspan=0, vecspec="deg")
}
in your code.
Is this what you're trying to achieve?
# Dummy dataset
library(plotrix)
Data <- data.frame(
x = sample(1:100),
y = sample(1:100), angle = sample(1:100), lenght = sample(1:100), matrixID = sample(letters[1:4], 20, replace = TRUE))
# Get the levels of matrixID
lev <- levels(Data$matrixID)
# Plot each graph
for (i in lev) {
temp <- subset(Data,matrixID==i)
plot(temp$x,temp$y,type="n", main=i)
with(temp, vectorField(u=angle,v=lenght,x=x,y=y,scale=0.15,headspan=0, vecspec="deg"))
}

Resources