been working around a little bit in the aesthetics on a 3dgraph I managed to get from some help here. Although I read in extense plotly library cant figure it out. I want to get a graph that looks like the image below, but I cant find the correct arguments to do so.
*Not an image of my property. For further information, please read: https://doi.org/10.1111/1365-2435.12268
My dataframe looks like this:
> data.ttl.gp
# A tibble: 3,000 x 4
X surv.prob temp time
<int> <dbl> <chr> <dbl>
1 1 1 42.0 3.26
2 1 1 44.0 0.686
3 1 1 46.0 0.144
4 2 0.999 42.0 6.53
5 2 0.999 44.0 1.37
6 2 0.999 46.0 0.288
7 3 0.998 42.0 9.79
8 3 0.998 44.0 2.06
9 3 0.998 46.0 0.432
10 4 0.997 42.0 13.1
# ... with 2,990 more rows
My basic R skills led me to something like this:
pgp<-plot_ly(scene='scene2')%>%
add_trace(x=data.ttl.gp$surv.prob, y=data.ttl.gp$time, z=data.ttl.gp$temp,
type="mesh3d", opacity=0.8)%>%
layout(
scene2=list(
xaxis=list(title='Survival probability'),
yaxis=list(title='Time (min)'),
zaxis=list(title='Temperature (°C)')))
And 3Dgraph output is the one below:
But the image above does not have any "full surface under the curve" as the first image. I understand this is because my df have just one vector for every x,y,z, so it is understandable the output graph. So my question is: Is there any other library I could use to make a full surface plot? Is there any other efficent way to plot it?
Thanks in advance!
EDIT
As Russ requested, sample data from dput(head(data.ttl.gp, n=30))
structure(list(X = c(1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L,
4L, 4L, 5L, 5L, 5L, 6L, 6L, 6L, 7L, 7L, 7L, 8L, 8L, 8L, 9L, 9L,
9L, 10L, 10L, 10L), surv.prob = c(1, 1, 1, 0.999, 0.999, 0.999,
0.998, 0.998, 0.998, 0.997, 0.997, 0.997, 0.996, 0.996, 0.996,
0.995, 0.995, 0.995, 0.994, 0.994, 0.994, 0.993, 0.993, 0.993,
0.992, 0.992, 0.992, 0.991, 0.991, 0.991), temp = c("42.0", "44.0",
"46.0", "42.0", "44.0", "46.0", "42.0", "44.0", "46.0", "42.0",
"44.0", "46.0", "42.0", "44.0", "46.0", "42.0", "44.0", "46.0",
"42.0", "44.0", "46.0", "42.0", "44.0", "46.0", "42.0", "44.0",
"46.0", "42.0", "44.0", "46.0"), time = c(3.2647446560386, 0.6858,
0.144060773368623, 6.5294893120772, 1.3716, 0.288121546737246,
9.79423396811581, 2.0574, 0.432182320105869, 13.0589786241544,
2.7432, 0.576243093474492, 16.323723280193, 3.429, 0.720303866843115,
19.5884679362316, 4.1148, 0.864364640211738, 22.8532125922702,
4.8006, 1.00842541358036, 26.1179572483088, 5.4864, 1.15248618694898,
29.3827019043474, 6.1722, 1.29654696031761, 32.647446560386,
6.858, 1.44060773368623)), row.names = c(NA, -30L), class = c("tbl_df",
"tbl", "data.frame"))
I know that this probably doesn't answer your question, but I found your question interesting, so this might give you an idea. Before moving on:
Since I have no access to your full dataset, I assume that the points are non-uniformly distributed over the surface. The approach that follows requires the points to be "uniformly distributed": essentially, we need x, y, z
to be 2D arrays, similar to what (Numpy) np.mgrid
creates. You should be able to get grid-like x,y,z
with scipy interpolate.
After that, you don't need to worry about triangulation. x, y
represents a grid on a plane. We can just add:
x
and y
. On z
, set it to a minimum value.x
and y
. On z
, set it to a minimum value.x
and y
. On z
, set it to a minimum value.x
and y
. On z
, set it to a minimum value.Then, Plotly is going to do the rest.
import numpy as np
import plotly.graph_objects as go
# assuming your data are in a grid-like layout
x, y = np.mgrid[-2:2:50j, -2:2:50j]
# surface
z = np.cos(x**2 + y**2)
add_rows = lambda arr, r1, r2: np.vstack([r1, arr, r2])
add_cols = lambda arr, c1, c2: np.concatenate([np.array([c1]).T, arr, np.array([c2]).T], axis=1)
# Chose an appropriate minimum z value
zz = -2 * np.ones_like(x[:, 0])
x = add_cols(x, x[:, 0], x[:, -1])
y = add_cols(y, y[:, 0], y[:, -1])
z = add_cols(z, zz, zz)
# Chose an appropriate minimum z value
zz = -2 * np.ones_like(x[0, :])
x = add_rows(x, x[0, :], x[-1, :])
y = add_rows(y, y[0, :], y[-1, :])
z = add_rows(z, zz, zz)
fig = go.Figure(data=[
go.Surface(x=x, y=y, z=z, **{ # draw wire frame
"opacity": 1,
"contours.x.show":True,
"contours.x.color":"#101010",
"contours.x.width":1,
"contours.x.start":-2,
"contours.x.end":2,
"contours.x.size":0.25,
"contours.y.show":True,
"contours.y.color":"#101010",
"contours.y.width":1,
"contours.y.start":-2,
"contours.y.end":2,
"contours.y.size":0.25,
})
])
fig