how to plot lines in 3d graph in R - r

I have these two 3d graphs made in R, with two different functions:
x <- c(0,50,100,150,200,250,300,350,400,450)
y <- c(0,50,100,150,200,250,300,350,400,450)
z <- c(1,2,1,1,2,1,2,1,2,1)
plot_ly(x=x,y=y,z=z)
scatterplot3d(x, y, z)
I would like to plot in these two graphs, the following lines and the intersection area between them:
Lines plotted in both graphs

As per the lines and intersection with plotly, something like this will do:
library(scatterplot3d)
library(plotly)
x <- c(0,50,100,150,200,250,300,350,400,450)
y <- c(0,50,100,150,200,250,300,350,400,450)
z <- c(1,2,1,1,2,1,2,1,2,1)
data <- data.frame(x=x,y=y,z=z)
line <- data.frame(x = rep(200,5), y = seq(0,500,length.out = 5), z = rep(1,5))
line2 <- data.frame(x = rep(300,5), y = seq(0,500,length.out = 5), z = rep(1,5))
rect <- data.frame(expand.grid(x= c(200,300), y =c(200,300)),z = rep(1,4) )
plot_ly() %>%
add_trace(data=data, x=x, y=y, z=z, type="scatter3d", mode="markers") %>%
add_trace(line, x = line$x, y = line$y, z = line$z, type = 'scatter3d', mode = 'lines', line = list(color = 'rgb(1, 1, 1)', width = 1 , dash = 'dash', width = 4)) %>%
add_trace(line2, x = line2$x, y = line2$y, z = line2$z, type = 'scatter3d', mode = 'lines', line = list(color = 'rgb(1, 1, 1)', width = 1, dash = 'dash', width = 4)) %>%
add_trace(line, x = line$y, y = line$x, z = line$z, type = 'scatter3d', mode = 'lines', line = list(color = 'rgb(1, 1, 1)', width = 1 , dash = 'dash', width = 4)) %>%
add_trace(line2, x = line2$y, y = line2$x, z = line2$z, type = 'scatter3d', mode = 'lines', line = list(color = 'rgb(1, 1, 1)', width = 1, dash = 'dash', width = 4))%>%
add_trace(rect, x=rect$x, y=rect$y, z=rect$z, type="mesh3d" )
Result with plotly:

Related

How to center the axes in Plotly on a 3d scatter plot in R?

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))

How to get subplot to show two ploty plots?

I am trying to generate two simple plots of the same block side by side using subplot but only one of the plots is shown.
size = 50000
width = 4
x1 = 0
x2 = width
y1 = 0
y2 = size
p1 <- plot_ly(type='area',mode='none',x = c(x1,x2,x2,x1,x1),width = 500, height = 725,
y = c(y1,y1,y2,y2,y1),
fill = 'toself',
fillcolor = 'rgb(233,87,62)')
x1 = 0
x2 = width
y1 = 0
y2 = size
p2 <- plot_ly(type='area',mode='none',x = c(x1,x2,x2,x1,x1),width = 500, height = 725,
y = c(y1,y1,y2,y2,y1),
fill = 'toself',
fillcolor = 'rgb(233,87,62)')
subplot(p1,p2,nrows=1)
To properly create a area chart with plotly, start with a scatter chart and then set mode to "lines".
Here is example with a better sample dataset:
library(plotly)
p1 <- plot_ly(economics, type='scatter', x = ~date, y = ~uempmed,
name = 'Left chart',
fill = 'tozeroy',
mode = 'lines',
fillcolor = 'rgb(233,87,62)',
line = list(color = 'rgb(233,87,62)'))
p2 <- plot_ly(economics, type='scatter', x = ~date, y = ~unemploy,
name = 'Right chart',
fill = 'tozeroy', mode = 'lines', fillcolor = 'rgb(87,87,62)',
line = list(color = 'rgb(87,87,62)'))
subplot(p1, p2, nrows = 1, margin = 0.05, widths = c(0.6, 0.4))

Make all ticks appear on axis using R plotly

I wrote a code to make a subplots with scatterplots using my data. Here is a graph:
This is hours on x axis. As you see, not all of them appear on x axis. How could i make all 24 hours be on axis? Even if for example in dataframe there is no value for 23 o'clock, i want it to be on x axis. How to do that?
Here is my code:
plot <- function(df) {
subplotList <- list()
for(metric in unique(df$metrics)){
subplotList[[metric]] <- df[df$metrics == metric,] %>%
plot_ly(
x = ~ hr,
y = ~ actual,
name = ~ paste(metrics, " - ", time_pos),
colors = ~ time_pos,
hoverinfo = "text",
hovertemplate = paste(
"<b>%{text}</b><br>",
"%{xaxis.title.text}: %{x:+.1f}<br>",
"%{yaxis.title.text}: %{y:+.1f}<br>",
"<extra></extra>"
),
type = "scatter",
mode = "lines+markers",
marker = list(
size = 7,
color = "white",
line = list(width = 1.5)
),
width = 700,
height = 620
) %>% layout(autosize = T,legend = list(font = list(size = 8)))
}
subplot(subplotList, nrows = length(subplotList), margin = 0.05)
}
This could be achieved in layout via the attribute xaxis like so. The ticks or breaks can be set via tickvals, the tick labels via ticktext.
This is illustrasted using some random data in the reproducible example below:
library(plotly)
set.seed(42)
d <- data.frame(
x = sort(sample(24, 15)),
y = 1:15 + runif(15),
z = 1:15 + runif(15)
)
plot_ly(d) %>%
add_trace(x = ~x, y = ~y, type = "scatter", mode = "lines+markers") %>%
add_trace(x = ~x, y = ~z, type = "scatter", mode = "lines+markers") %>%
layout(xaxis = list(tickvals = 1:24, ticktext = paste0(1:24, "h")))

How fill a plot to a given y0 value

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

R: Plot_ly 3d graph with trace line

I am using plotly 4.7.0. I am trying to add a 3d line to a 3d plot_ly surface plot. When I don't add the add_lines() function inside of the plot_ly call it looks fine. As soon as I do add the add_lines, the graph gets all messed up and doesn't add the 3d line graph.
library(plotly)
m_x = seq(-2, 2, .01)
m_y = seq(-2, 2, .01)
df = expand.grid(m_x, m_y)
df['matrix'] = exp(-(df$Var1^2+df$Var2^2))
m_z = matrix(df$matrix, nrow = length(m_x), ncol = length(m_y))
m_df = list(m_x, m_y, m_z)
x1 = seq(-2, 0, by=0.0202)
y1 = runif(100, min=-0.03,max=0.03)
z1 = exp(-(x1^2+y1^2))
df = data.frame(x1, y2, z2)
names(df) <- c("df_x", "df_y", "df_z")
colors = c( "Blue", "Cyan", "Green", "Yellow", "Orange", "Red")
p1 <- plot_ly(x = m_df[[1]], y = m_df[[2]], z = m_df[[3]],
colors = colors, color = m_df[[3]]) %>% add_surface() %>%
add_lines(x = df$df_x, y = df$df_y, z = df$df_z, data = df,
line = list(color = 'red', width = 1)) %>%
layout(title = "Hike_Example",
scene = list(aspectratio = list(x = 4, y = 4, z = 1)))
p1
Here is the code of the surface plot with the 3D line.
I plotted the 3D line using add_trace in place of add_lines.
library(plotly)
m_x = seq(-2, 2, .01)
m_y = seq(-2, 2, .01)
df = expand.grid(m_x, m_y)
df['matrix'] = exp(-(df$Var1^2+df$Var2^2))
m_z = matrix(df$matrix, nrow = length(m_x), ncol = length(m_y))
m_df = list(m_x, m_y, m_z)
x1 = seq(-2, 0, by=0.0202)
y1 = runif(100, min=-0.03,max=0.03)
z1 = exp(-(x1^2+y1^2))
df = data.frame(x1, y1, z1)
names(df) <- c("df_x", "df_y", "df_z")
colors = c( "Blue", "Cyan", "Green", "Yellow", "Orange", "Red")
p1 <- plot_ly(x = m_df[[1]], y = m_df[[2]], z = m_df[[3]],
colors = colors, color = m_df[[3]]) %>% add_surface() %>%
add_trace(x = ~df_x, y = ~df_y, z = ~df_z, data = df,
type="scatter3d", mode="lines",
line = list(color = "red", width = 4)) %>%
layout(title = "Hike_Example",
scene = list(aspectratio = list(x = 4, y = 4, z = 1)))
p1

Resources