Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scipy's WrappedCauchy function wrong?

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":

enter image description here

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?

like image 284
RedM Avatar asked Sep 15 '25 12:09

RedM


1 Answers

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

Source.

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

Cauchy Plot

like image 161
Nick ODell Avatar answered Sep 17 '25 02:09

Nick ODell