I'd like someone to check my understanding on the wrapped cauchy function in Scipy... From Wikipedia "a wrapped Cauchy distribution is a wrapped probability distribution that results from the "wrapping" of the Cauchy distribution around the unit circle." It's similar to the Von Mises distribution in that way.
I use the following bits of code to calculate a couple thousand random variates, get a histogram and plot it.
from scipy.stats import wrapcauchy, vonmises
import plotly.graph_objects as go
import numpy as np
def plot_cauchy(c, loc = 0, scale = 1, size = 100000):
'''
rvs(c, loc=0, scale=1, size=1, random_state=None)
'''
rvses = vonmises.rvs(c,
loc = loc,
scale = scale,
size = size)
# rvses = wrapcauchy.rvs(c,
# loc = loc,
# scale = scale,
# size = size)
y,x = np.histogram(rvses,
bins = 200,
range = [-np.pi,np.pi],
density = True)
return x,y
fig = go.Figure()
loc = -3
x,y = plot_cauchy(0.5, loc = loc)
fig.add_trace(go.Scatter(x=x, y=y,
mode='lines+markers',
name= f'Centered on {loc}'))
loc = 1.5
x,y = plot_cauchy(0.5, loc = loc)
fig.add_trace(go.Scatter(x=x, y=y,
mode='lines+markers',
name= f'Centered on {loc}'))
loc = 0
x,y = plot_cauchy(0.5, loc = loc)
fig.add_trace(go.Scatter(x=x, y=y,
mode='lines+markers',
name=f'Centered on {loc}'))
fig.show()
When plotting this using the Von Mises distribution I get a couple of distributions that are wrapped from -pi to pi and centered on "loc":
When I replace the vonmises distribution with the wrapcauchy distribution I get a "non-wrapped" result, that to my eye just looks wrong. To plot this completely I have to adjust the ranges for the histogram
This is with Scipy version '1.15.2'.
Is there a way to correctly "wrap" the outputs of a the Scipy call, or another library that correctly wraps the output from -pi to pi?
Is there a way to correctly "wrap" the outputs of a the Scipy call
You can use the modulo operator. The operation number % x
wraps all output to the range [0, x)
. If you want the range to begin at a value other than 0, you can add and subtract a constant before and after the modulo operation to center it somewhere else. If you want the range to begin at -pi, you can do (array + pi) % (2 * pi) - pi
.
For example, this is how SciPy internally wraps the vonmises result.
return np.mod(rvs + np.pi, 2*np.pi) - np.pi
You could do something similar with the result of scipy.stats.wrapcauchy()
.
Here is how you could modify your code to do this:
from scipy.stats import wrapcauchy, vonmises
import plotly.graph_objects as go
import numpy as np
def plot_cauchy_or_vm(c, loc = 0, scale = 1, kind="vonmises", size = 100000):
'''
rvs(c, loc=0, scale=1, size=1, random_state=None)
'''
if kind == "vonmises":
rvses = vonmises.rvs(c,
loc = loc,
scale = scale,
size = size)
elif kind == "cauchy":
rvses = wrapcauchy.rvs(c,
loc = loc,
scale = scale,
size = size)
rvses = ((rvses + np.pi) % (2 * np.pi)) - np.pi
else:
raise Exception("Unknown kind")
y,x = np.histogram(rvses,
bins = 200,
range = [-np.pi,np.pi],
density = True)
return x,y
for kind in ["vonmises", "cauchy"]:
fig = go.Figure()
loc = -3
x,y = plot_cauchy_or_vm(0.5, kind=kind, loc = loc)
fig.add_trace(go.Scatter(x=x, y=y,
mode='lines+markers',
name= f'Centered on {loc}'))
loc = 1.5
x,y = plot_cauchy_or_vm(0.5, kind=kind, loc = loc)
fig.add_trace(go.Scatter(x=x, y=y,
mode='lines+markers',
name= f'Centered on {loc}'))
loc = 0
x,y = plot_cauchy_or_vm(0.5, kind=kind, loc = loc)
fig.add_trace(go.Scatter(x=x, y=y,
mode='lines+markers',
name=f'Centered on {loc}'))
fig.show()
Output:
Cauchy Plot