0

I am using holoviews+bokeh, and I would like to encircle my scatter plot data with a measure of standard deviation. Unfortunately I can't seem to get the orientation setting right. I am confused by the available descriptions:

Orientation in the Cartesian coordinate system, the counterclockwise angle in radians between the first axis and the horizontal and

you can set the orientation (in radians, rotating anticlockwise)

My script and data example:

def create_plot(x, y, nstd=5):
    x, y = np.asarray(x), np.asarray(y)
    cov_matrix = np.cov([x, y])
    eigenvalues, eigenvectors = np.linalg.eig(cov_matrix)
    order = eigenvalues.argsort()[0]
    angle = np.arctan2(eigenvectors[1, order], eigenvectors[1, order])

    x0 = np.mean(x)
    y0 = np.mean(y)

    x_dir = np.cos(angle) * x - np.sin(angle) * y
    y_dir = np.sin(angle) * x + np.cos(angle) * y

    w = nstd * np.std(x_dir)
    h = nstd * np.std(y_dir)

    return hv.Ellipse(x0, y0, (w, h), orientation=-angle) * hv.Scatter((x, y))

c2x = np.random.normal(loc=-2, scale=0.6, size=200)
c2y = np.random.normal(loc=-2, scale=0.1, size=200)

combined = create_plot(c2x, c2y)
combined.opts(shared_axes=False)
F_B
  • 1
  • 1

1 Answers1

0

Here is a solution, which draws Ellipse around the data. You math is just simplified.

import numpy as np
import holoviews as hv
from holoviews import opts

hv.extension('bokeh')

x = np.random.normal(loc=-2, scale=0.6, size=200)
y = np.random.normal(loc=-2, scale=0.1, size=200)

def create_plot(x, y, nstd=5):
    x, y = np.asarray(x), np.asarray(y)

    x0 = np.mean(x)
    y0 = np.mean(y)

    w = np.std(x)*nstd
    h = np.std(y)*nstd

    return hv.Ellipse(x0, y0, (w, h)) * hv.Scatter((x, y))

combined = create_plot(c2x, c2y)
combined.opts()

This gives you a plot which looks like a circle. To make it more visiable that it is a Ellipse your could genereate the plot calling

def hook(plot, element):
    plot.handles['x_range'].start = -4
    plot.handles['x_range'].end = 0
    plot.handles['y_range'].start = -2.5
    plot.handles['y_range'].end = -1

combined.opts(hooks=[hook])

which set fixed ranges and deactivates the auto focus.

In your example w and h were nearly the same, that means, you drawed a cercle. The orientation didn't have any effect. With the code above you can turn the Ellipse like

hv.Ellipse(x0, y0, (w, h), orientation=np.pi/2)

to see that it is working, but there is no need to do it anymore.

mosc9575
  • 5,618
  • 2
  • 9
  • 32