I would like to plot a sphere in the following plot:
And that is the code for it:
library(plotly)
fig <- plot_ly(mtcars, x = ~wt, y = ~hp, z = ~qsec,
marker = list(color = ~mpg, colorscale = c('#FFE1A1', '#683531'), showscale = TRUE))
fig <- fig %>% add_markers()
fig <- fig %>% layout(scene = list(xaxis = list(title = 'Weight'),
yaxis = list(title = 'Gross horsepower'),
zaxis = list(title = '1/4 mile time')),
annotations = list(
x = 1.13,
y = 1.05,
text = 'Miles/(US) gallon',
xref = 'paper',
yref = 'paper',
showarrow = FALSE
)) %>% add_markers(x = 2.8, y = 120, z = 20, color="red", marker=list(size=30,
opacity = .65,
line=list(width=2,
color='black')))
fig
At the moment my best version only contains a 2D circle. How could I integrate a 3d/wireframe sphere into it using plotly R-version?
A sphere is an isosurface. You can plot an isosurface with plotly but it is a bit slow, I prefer to use the misc3d package.
library(plotly)
f <- function(x, y, z){
x^2 + y^2 + z^2
}
R <- 2 # radius
x <- y <- z <- seq(-R, R, length.out = 100)
g <- expand.grid(x = x, y = y, z = z)
voxel <- array(with(g, f(x, y, z)), dim = c(100, 100, 100))
library(misc3d)
cont <- computeContour3d(voxel, level = R^2, x = x, y = y, z = z)
idx <- matrix(0:(nrow(cont)-1), ncol=3, byrow=TRUE)
plot_ly(
x = cont[, 1], y = cont[, 2], z = cont[, 3],
i = idx[, 1], j = idx[, 2], k = idx[, 3],
type = "mesh3d"
) %>% layout(scene = list(aspectmode = "data"))
Related
I have a 3d scatter plot that I created with Plotly in R - is there anyway to move the axes to the middle? My plot right now looks similar to this plot that I made real quick in R:
But I would like to remove the grid background, the axis ticks, and move the axes to the middle of the plot to make it look similar to this:
My main problem is moving the axes to the middle while maintaining the x, y, and z labels on them. I have used traces to simulate the central axes, but then I have the issue of no axis labels when I remove the background grid and axes. What is the best way to go about this?
The code to recreate the first plot is below as well:
coords = list("x"=c(), "y"=c(), "z"=c())
for(phi in seq(0, 2*pi, 0.2)) {
for(theta in seq(0, pi, 0.2)) {
x = (8 * sin(theta) * cos(phi))
y = (8 * sin(theta) * sin(phi))
z = (8 * cos(theta))
coords$x = append(coords$x, x)
coords$y = append(coords$y, y)
coords$z = append(coords$z, z)
}
}
df = data.frame("x"=coords$x, "y"=coords$y, "z"=coords$z)
fig = plot_ly(df, x=~x, y=~y, z=~z, type="scatter3d",
mode="markers", marker=list(size=3))
fig = layout(fig, scene=list(xaxis=list(range=c(-12, 12)),
yaxis=list(range=c(-12, 12)),
zaxis=list(range=c(-12, 12))))
fig
How about this?
If you make the text bold:
I think it would look better if I could make the text bold. In the object annots that will be made in the function getvals. Where you see text = "x" (y or z), if you prefer bold, annotate like text = "<b>x</b>".
I tried to make this dynamic so that it could be more readily repurposed, but I didn't test any Plotly possible extremes. It is based on the assumption that there is only 1 trace and that that trace is a scatter3d.
I want to point out that marking the lines as mode = "lines" was continuously trumped by Plotly, rendering as lines+markers. If I didn't designate it as lines+markers, I couldn't control the markers, either. That's why you see markers called but essentially hidden.
getvals <- function(plt) {
plt <- plotly_build(plt) # ensure data is in object
if(isTRUE(length(plt$x$data) == 1)) {
df1 <- data.frame(x = plt$x$data[[1]]$x, # extract data
y = plt$x$data[[1]]$y,
z = plt$x$data[[1]]$z)
mx <- max(df1$x); my <- max(df1$y); mz <- max(df1$z) # for segments
nx <-.25 * mx; ny <- .25 * my; nz <- .25 * mz # for line segment size
d <- colMeans(df1)
dx <- d[[1]]; dy <- d[[2]]; dz <- d[[3]] # line segment center
hx <- dx + nx; hy <- dy + ny; hz <- dz + nz # line segment high
lx <- dx - nx; ly <- dy - ny; lz <- dz - nz # line segment low
plt <- plt %>% # add the traces for the internal axes indic.
add_trace(x = c(lx, hx), y = c(dy, dy), z = c(dz, dz), # x axis line
mode = "lines+markers",
hoverinfo = "skip", marker = list(size = .01),
line = list(color = "black", width = 3)) %>%
add_trace(x = c(dx, dx), y = c(ly, hy), z = c(dz, dz), # y axis line
mode = "lines+markers",
hoverinfo = "skip", marker = list(size = .01),
line = list(color = "black", width = 3)) %>%
add_trace(x = c(dx, dx), y = c(dy, dy), z = c(lz, hz), # z axis line
mode = "lines+markers",
hoverinfo = "skip", marker = list(size = .01),
line = list(color = "black", width = 3)) %>%
add_trace(x = dx, y = dy, z = dz, type = "scattered", # center ball
mode = "markers", hoverinfo = "skip",
marker = list(size = 8, color = "black"))
annots <- list( # text annotations x, y, z
list(showarrow = F, x = hx, y = dy, z = dz,
text = "x", xanchor = "right", xshift = -5,
font = list(size = 20)),
list(showarrow = F, x = dx, y = hy, z = dz,
text = "y", xshift = -5,
font = list(size = 20)),
list(showarrow = F, x = dx, y = dy, z = hz,
text = "z", xshift = -5,
font = list(size = 20)))
assign("annots", annots, envir = .GlobalEnv)
plt # return plot; send annotations to the global env
}
}
Using that function
Take your original plot, less the call for layout. I added showlegend = F, because when the other traces are added, it would have created a legend. You could pipe this to the plot, or do it like this.
fig = plot_ly(df, x = ~x, y = ~y, z = ~z, type = "scatter3d",
mode = "markers", marker = list(size = 3),
showlegend = F)
fig %>% getvals() %>%
layout(
scene = list(
aspectratio = list(x = 1, y = 1, z = 1),
camera = list(
center = list(x = 0, y = 0, z = 0),
eye = list(x = -.5, y = .5, z = .6)),
up = list(x = 0, y = 0, z = 1),
xaxis = list(showgrid = FALSE, zeroline = FALSE, range = c(-12, 12),
showticklabels = FALSE, title = list(text = "")),
yaxis = list(showgrid = FALSE, zeroline = FALSE, range = c(-12, 12),
showticklabels = FALSE, title = list(text = "")),
zaxis = list(showgrid = FALSE, zeroline = FALSE, range = c(-12, 12),
showticklabels = FALSE, title = list(text = "")),
dragmode = "turntable",
annotations = annots
), margin = list(t = 30, r = 30, l = 30, b = 30, padding = 2))
I am trying to change markers' colors and symbols according to a categorical variable in r-plotly. But it is not working. Also, I would like to draw "contour lines" around the "walls" of the 3d panel, similar to what I have in the attached figure. Any help is welcome.
# Packages
library(plotly)
library(misc3d)
# Fake data
norm_vec <- function(x) sqrt(sum(x ^ 2))
X3d_norm <- data.frame(T3 = runif(100), T6 = runif(100), P4 = runif(100))
norms <- apply(X3d_norm, 1, norm_vec)
X3d_norm <- X3d_norm / norms
X3d_norm$cluster <- factor(sample(1:2, 100, replace = T), ordered = T)
# Getting octant data
f <- function(x, y, z){x ^ 2 + y ^ 2 + z ^ 2}
R <- 1
x <- y <- z <- seq(0, R, length.out = 100)
g <- expand.grid(x = x, y = y, z = z)
voxel <- array(with(g, f(x, y, z)),dim = c(100, 100, 100))
cont <- computeContour3d(voxel, level = R ^ 2, x = x, y = y, z = z)
idx <- matrix(0:(nrow(cont) - 1), ncol = 3,byrow = TRUE)
# Colors and symbols
pal <- c("#E69F00", "#009E73")
pal <- setNames(pal, c(1, 2))
symb <- c("circle", "square")
symb <- setNames(symb, c(1, 2))
# Plot
plot_ly() %>%
add_mesh(
x = cont[, 1], y = cont[, 2], z = cont[, 3],
i = idx[, 1], j = idx[, 2], k = idx[, 3],
opacity = 0.15
) %>%
add_markers(
data = X3d_norm,
x = ~T3, y = ~P4, z = ~T6,
color = ~cluster, colors = pal,
symbol = ~cluster, symbols = symb
) %>%
layout(
legend = list(title = list(text = 'Clusters')),
scene = list(
xaxis = list(
title = "T3", nticks = 4, tickvals = c(0.20, 0.40, 0.60, 0.80),
tickfont = list(size = 16), titlefont = list(size = 24)),
yaxis = list(
title = "P4", nticks = 4, tickvals = c(0.20, 0.40, 0.60, 0.80),
tickfont = list(size = 16), titlefont = list(size = 24)),
zaxis = list(
title = "T6", nticks = 4, tickvals = c(0.20, 0.40, 0.60, 0.80),
tickfont = list(size = 16), titlefont = list(size = 24))
)
)
Any idea how to control the font size of an axis title in R plotly?
Starting with this example:
library(plotly)
A <- B <- seq(-5, 5, by = 0.2)
z <- outer(A, B, function(x,y) exp(-x^2-y^2))
plot_ly(x = A, y = B, z = z) %>% add_surface() %>%
layout(scene = list(xaxis = list(title = "A"), yaxis = list(title = "B")))
I tried:
plot_ly(x = A, y = B, z = z) %>% add_surface() %>%
layout(scene = list(xaxis = list(title = "A",font = list(size = 10)), yaxis = list(title = "B")))
But there's no difference and neither titlefont = list(size = 10) makes any difference.
Thanks
library(plotly)
A <- B <- seq(-5, 5, by = 0.2)
z <- outer(A, B, function(x,y) exp(-x^2-y^2))
plot_ly(x = A, y = B, z = z) %>% add_surface() %>%
layout(scene = list(xaxis = list(title = "A",titlefont = list(size = 10)), yaxis = list(title = "B")))
I'm using Plotly in R and I want to create a plot like this:
But when trying with fill = "tozeroy" the plot looks like this:
Ignoring the value of y0
So, I want to fill the area from the lines to y0 value, instead to absolute zero.
A code example:
library(plotly)
library(magrittr)
x <- seq.Date(as.Date("2017/01/01"), as.Date("2017/12/31"), by = 1)
y1 <- rnorm(365, 100, 10)
y2 <- rnorm(365, 100, 10)
dat <- data.frame(x, y1, y2)
plot_ly(dat, x = ~x, y = ~y1, mode = "lines", type = "scatter", fill = "tozeroy", y0 = 80, name = "y1") %>%
add_trace(dat, x = ~x, y = ~y2, fill = 'tozeroy', y0 = 80, name = "y2")%>%
layout(xaxis = list(title = ""), yaxis = list(title = ""))
produces:
Ignoring the y0 = 80 parameter.
If I understood correctly you want the range to begin in y0.
To that end, you have to change the yaxis' range in the layout.
This should suffice:
library(plotly)
library(magrittr)
set.seed(19)
x <- seq.Date(as.Date("2017/01/01"), as.Date("2017/12/31"), by = 1)
y1 <- rnorm(365, 100, 10)
y2 <- rnorm(365, 100, 10)
y0 <- 80
ymax <- max(y1,y2)
dat <- data.frame(x, y1, y2)
plot_ly(dat, x = ~x, y = ~y1, mode = "lines", type = "scatter", fill = "tozeroy", y0 = 80, name = "y1") %>%
add_trace(dat, x = ~x, y = ~y2, fill = 'tozeroy', name = "y2")%>%
layout(xaxis = list(title = ""), yaxis = list(range = c(y0, ymax),title = ""))
The range options in plotly are explained here
I start from a simple interaction model z = 0.5*x*y. The goal is to plot simulated observations with add_markers() as well as the model with add_surface() using the plotly library.
So far I have manage to simulate the data and plot it:
library(plotly)
library(magrittr)
x <- rnorm(10000)
y <- rnorm(10000)
z <- 0.5 * x * y
plot_ly() %>% add_markers(x = x, y = y, z = z, marker = list(size = 1))
This is what the plot looks like:
I then tried to surface plot the model:
plot_ly() %>%
add_markers(x = x, y = y, z = z, marker = list(size = 1)) %>%
add_surface(z = matrix(z, 100, 100))
But it produces something completely different to what it would be expected. This is approximately the result that would be expect from the surface plot:
What am I doing wrong?
An easy workaround is to use:
plot_ly(x = x, y = y, z= z, type = 'mesh3d')
Or using surface plot:
library(tidyverse)
grid <- expand.grid(x = seq(from = -3, to = 3, length.out = 100),
y = seq(from = -3, to = 3, length.out = 100))
grid %>%
mutate(z = 0.5 * x * y) %>%
spread(key = y, value = z) %>%
as.matrix() -> z
plot_ly() %>%
add_surface(z = z, x = unique(grid$x), y = unique(grid$y)) %>%
layout(scene = list(xaxis = list(range = c(-2.5, 2.5)),
yaxis = list(range = c(-2.5, 2.5)))) #odd artifact if all values are plotted - check without layout call
or:
model = lm(z ~ x * y)
grid <- expand.grid(x = seq(from = -3, to = 3, length.out = 100),
y = seq(from = -3, to = 3, length.out = 100))
grid$z = predict(model, grid)
and proceed as above
grid %>%
spread(key = y, value = z) %>%
as.matrix() -> z