# Sphere in 3D with dates as an axis

I have a 3D plot which traces prices over a number of days. it works fine as a Scatter3d.

I’m trying to introduce a sphere, to be drawn around one of the points. I’m using the first part of Empet’s code as a reference

The problem I’m facing is having dates in one of the axes. When I uncomment the createSphere statement, a sphere is drawn but at y=0, i.e. some time in the year 1970.

It’s also changing the axis labeling.

What can I do to manipulate a date/datetime in the y-axis? I’ve tried converting the date into a .timestamp() but that doesn’t seem to work.

Code is below, with my thanks in advance for any suggestions.

``````import plotly.graph_objects as go
import datetime

prices=[26269, 26281, 27110, 27572, 27272, 26989, 25128, 25605]

base = datetime.datetime(2022, 2, 1)
dates = [base + datetime.timedelta(days=x) for x in range(8)]
counts = [x+1 for x in range(8)]

fig = go.Figure()

marker=dict(size=2))

def createSphere(fig, prices, dates, counts):
from numpy import pi, sin, cos
import numpy as np

theta = np.linspace(0, 2*pi, 120)
phi = np.linspace(0, pi, 60)
u , v = np.meshgrid(theta, phi)
xs = 3*cos(u)*sin(v) + counts
ys = sin(u)*sin(v) #??? how to apply a date/datetime value here?
zs = 1000*cos(v) + prices

colorscale='Greens',
showscale=False, opacity=0.5)  # or opacity=1

#createSphere(fig, prices, dates, counts)

fig.show()
``````

Hi @samri ,

I defined a new function, `createSphere`. Inside its body the interval [-1,1], as the range of the scalar function sin(u)*sin(v), is mapped onto an interval of dates, through two successive transformations, commented and defined below:

``````def createSphere(fig, prices, dates, counts):
from numpy import pi, sin, cos
import numpy as np
a = datetime_to_float(dates) #convert datetime to float, i.e. to seconds
b = datetime_to_float(dates[-1])
u = np.linspace(0, 2*pi, 120)
v = np.linspace(0, pi, 60)
u , v = np.meshgrid(u, v)
#map the interval [-1, 1]  (i.e. the range of sin(u)*sin(v)), onto the interval of seconds [a, b]
sec = (a+(b-a)*(sin(u)*sin(v)+1)/2).astype(int)
r, c = sec.shape
#convert seconds to datetime
ys =  np.array([[datetime.datetime.fromtimestamp(sec[i, j]) for j in range(c)] for i in range(r)])
xs = 3*cos(u)*sin(v) + counts
zs = 1000*cos(v) + prices

colorscale='Greens',
showscale=False, opacity=0.5)
``````

Note that your surface is not a sphere, but an ellipsoid. The parametric equations of an ellipsoid of center (x0, y0, z0) and semi-axes A, B, C are:

``````xe = x0+ A*cos(u)*sin(v)
ye = y0+B* sin(u)*sin(v)
zs = z0+C*cos(v)
``````

For a sphere, A=B=C=sphere radius

@samri I’m back to explain how is mapped [-1,1] to [a, b]:

``````y = sin(u)*sin(v) in [-1,1] --->t=(y-(-1))/(1-(-1)=(y+1)/2-->a+(b-a)*t,     t in [0,1]
``````

i.e.

``````sin(u)*sin(v)-->a+(b-a)*(sin(u)*sin(v)+1)/2
``````

I used your data without being able to figure out why you chose the coeffficient B=1 in ellipsoid parameterization. With your settings, B=1 means that y goes from -1seconds to 1 seconds. If you want a greater interval of seconds for y, then you must assign a bigger value to B,
i.e. define `y= B*sin(u)*sin(v)`

Thanks @empet , both for this and the original code I had used.

It’s beginning to make sense now. While waiting for an answer I did try converting the dates to floats, even though there are still parts that I’m trying to understand. Here’s the code and I’ll explain afterwards my choice of value for B:

``````import plotly.graph_objects as go
import datetime

prices = [26269, 26281, 27110, 27572, 27272, 26989, 25128, 25605]

base   = datetime.datetime(2022, 2, 1)
dates  = [base + datetime.timedelta(days=x) for x in range(8)]
counts = [x+1 for x in range(8)]

fig = go.Figure()

mode = 'lines+markers+text',
marker=dict(size=5)
)

def createSphere_Samri(fig, prices, dates, counts):
from numpy import pi, sin, cos
import numpy as np

theta = np.linspace(0, 2*pi, 12*10)
phi = np.linspace(0, pi, 6*10)
u , v = np.meshgrid(theta, phi)

one_day  = 24*60*60
tot_secs = (dates-datetime.datetime(1970, 1, 1)).total_seconds()

#still trying to understand why i have to multiply by 1000 (is axis in milliseconds?)
one_day  = one_day  * 1000
tot_secs = tot_secs * 1000

xs = counts       + cos(u)*sin(v) * 1/2           #multiplying by 1/2 in order to center it between count=5 and count = 6
zs = prices       + cos(v)        * 250           #multiplying by 250, just an arbitrary value for now

ys = (tot_secs)      + sin(u)*sin(v) * 1/2 * one_day #again, multiply by 1/2 trying to center between Feb 5 and Feb 6

# however, ellipse only appears to "center" after I add this line. Is it because a year is 365.24 days?
ys = ys + (0.24 * one_day)

I had gotten as far as figuring out that the dates have to be offset from the epoch 1970.01.01, but I’m still trying to understand why i have to multiply by 1000. Also another things is why I have to add 0.24 days to have the ellipsoid center correctly. I’ll try to understand it using your new code and the explanation you provided. Many thanks again 