9.3 Bivariate Distribution Visualization

Code
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import multivariate_normal

def bivar(graphtype, mupos=None, sigpos=None, rhopos=None, muneg=None, signeg=None, rhoneg=None):

    if mupos is None:
        mupos = np.array([4, 4])
        sigpos = np.array([1, 1])
        rhopos = 0
        muneg = np.array([2, 2])
        sigmaneg = np.array([[1, -0.6], [-0.6, 1]])  
    else:
        sigmapos = np.array([[sigpos[0], rhopos * np.sqrt(sigpos[0] * sigpos[1])],
                             [rhopos * np.sqrt(sigpos[0] * sigpos[1]), sigpos[1]]])

        if signeg is None:
            sigmaneg = np.array([[1, -0.6], [-0.6, 1]])  
        else:
            sigmaneg = signeg

    Npos = 100
    Nneg = 100


    pos = np.random.multivariate_normal(mupos, sigmapos, Npos)
    neg = np.random.multivariate_normal(muneg, sigmaneg, Nneg)

    mn = np.min(np.concatenate((pos, neg), axis=0))
    mx = np.max(np.concatenate((pos, neg), axis=0))
    
    x = np.arange(mn, mx, 0.1)
    y = np.arange(mn, mx, 0.1)
    X, Y = np.meshgrid(x, y)

    Ppos = np.zeros_like(X)
    Pneg = np.zeros_like(X)
    L = np.zeros_like(X)

    for i in range(len(x)):
        for j in range(len(y)):
            Ppos[j, i] = getprob(x[i], y[j], mupos, sigmapos)
            Pneg[j, i] = getprob(x[i], y[j], muneg, sigmaneg)
            L[j, i] = Pneg[j, i] / Ppos[j, i]

    Pb = (1 / (2 * np.pi * np.sqrt(np.linalg.det(sigmapos)))) * np.exp(-1 / 2)
    Ps = (1 / (2 * np.pi * np.sqrt(np.linalg.det(sigmaneg)))) * np.exp(-1 / 2)


    plt.figure(figsize=(8, 8))
    if graphtype == 2:

        plt.axis('square')
        plt.axis([mn, mx, mn, mx])

        plt.scatter(pos[:, 0], pos[:, 1], color='r', label='Positive')
        plt.scatter(neg[:, 0], neg[:, 1], color='b', label='Negative')

        plt.contour(X, Y, Ppos, levels=[Pb], colors='r')
        plt.contour(X, Y, Pneg, levels=[Ps], colors='b')
        plt.contour(X, Y, L, levels=[1], colors='k')

        plt.plot([mupos[0], muneg[0]], [mupos[1], muneg[1]], color='k', linestyle=':')

    elif graphtype == 3:

        from mpl_toolkits.mplot3d import Axes3D

        fig = plt.figure(figsize=(8, 8))
        ax = fig.add_subplot(111, projection='3d')

        ax.plot_surface(X, Y, Ppos, cmap='Reds', edgecolor='none', alpha=0.6)
        ax.plot_surface(X, Y, Pneg, cmap='Blues', edgecolor='none', alpha=0.6)

        ax.contour3D(X, Y, Ppos, levels=[Pb], cmap='Reds')
        ax.contour3D(X, Y, Pneg, levels=[Ps], cmap='Blues')

    plt.show()


def getprob(x, y, mu, sigma):
    vec = np.array([x, y])
    E = 2 * np.pi * np.sqrt(np.linalg.det(sigma))
    P = (1 / E) * np.exp(-0.5 * np.dot(np.dot((vec - mu), np.linalg.inv(sigma)), (vec - mu).T))
    return P

bivar(2, mupos=[4, 3], sigpos=[1, 1], rhopos=0, muneg=[2, 2], signeg=[[1, -0.6], [-0.6, 1]], rhoneg=0)