# Normal Distribution Joyplots

Joyplots are partially overlapping line plots that create the impression of a mountain range. The name joyplot is in reference to the iconic cover art for Joy Divisionâ€™s album Unknown Pleasures. They are a thing of beauty, just look at this one I made:
We are going to use joyplots to visualize a fundamental fact about Normal Distribution:

The general Normal distribution has two parameters, denoted $\mu$ and $\sigma^2$, which correspond to the mean and variance (so the standard Normal is the special case where $\mu = 0$ and $\sigma^2 = 1$). Starting with a standard Normal r.v. Z $\sim \mathcal{N}(0,1)$, we can get a Normal r.v. with any mean and variance by a location-scale transformation(shifting and scaling).

## Location Transformation (Shifting)

In [1]:
%matplotlib inline
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

standard_normal_rv = np.random.normal(loc = 0, scale = 1, size = 2000)
nrv_1_1 = np.random.normal(loc = 1, scale = 1, size = 2000)
nrv_2_1 = np.random.normal(loc = 2, scale = 1, size = 2000)
nrv_3_1 = np.random.normal(loc = 3, scale = 1, size = 2000)
nrv_4_1 = np.random.normal(loc = 4, scale = 1, size = 2000)

dist_labels = np.tile(["N(0,1)", "N(1,1)", "N(2,1)", "N(3,1)", "N(4,1)"], 400)
dist_aggr = []

for dist_label, rv_0, rv_1, rv_2, rv_3, rv_4 in zip(dist_labels, standard_normal_rv, nrv_1_1, nrv_2_1, nrv_3_1, nrv_4_1):
if dist_label == "N(0,1)":
dist_aggr.append(rv_0)
elif dist_label == "N(1,1)":
dist_aggr.append(rv_1)
elif dist_label == "N(2,1)":
dist_aggr.append(rv_2)
elif dist_label == "N(3,1)":
dist_aggr.append(rv_3)
else:
dist_aggr.append(rv_4)

df = pd.DataFrame(dict(l = dist_labels, dist = dist_aggr))

def joyplot(df, font_size, xtick_labelsize, xlim_range, ylim_range, label_fontsize, xlabel_text):

sns.set(style = "white", rc = {"axes.facecolor": (0, 0, 0, 0),
"font.family": "mononoki",
"font.size": font_size,
"xtick.labelsize": xtick_labelsize,
"figure.facecolor": "fffff8"})

pal = ['#30a2da', '#DC143C', '#FFC400', '#6d904f', '#8A2BE2']
g = sns.FacetGrid(df, row = "l", hue = "l", aspect = 15, size = .65, palette = pal)

g.map(sns.kdeplot, "dist", clip_on = False, shade = True, alpha = 1, lw = .1)
g.map(sns.kdeplot, "dist", clip_on = False, color = "w", lw = 2)

def label(x, color, label):
ax = plt.gca()
ax.text(-.05, .2, label, color = color,
ha = "left", va = "center", transform = ax.transAxes)

g.map(label, "dist")

g.set_titles("")
g.set(yticks=[])
g.set(xlim = xlim_range)
g.set(ylim = ylim_range)
g.despine(bottom=True, left=True)
label_font = {'family': 'ETBembo', 'size' : label_fontsize, 'style' : 'italic'}

joyplot(df = df, font_size = 13, xtick_labelsize = 12, xlim_range = (-5,5),
ylim_range = (0,.07), label_fontsize = 18, xlabel_text = "Location Transformation (Shifting)")

## Scale Transformation (Scaling)

In [2]:
standard_normal_rv = np.random.normal(loc = 0, scale = 1, size = 2000)
nrv_0_2 = np.random.normal(loc = 0, scale = 2, size = 2000)
nrv_0_3 = np.random.normal(loc = 0, scale = 3, size = 2000)
nrv_0_4 = np.random.normal(loc = 0, scale = 4, size = 2000)
nrv_0_5 = np.random.normal(loc = 0, scale = 5, size = 2000)

dist_labels = np.tile(["N(0,1)", "N(0,2)", "N(0,3)", "N(0,4)", "N(0,5)"], 400)
dist_aggr = []

for dist_label, rv_1, rv_2, rv_3, rv_4, rv_5 in zip(dist_labels, standard_normal_rv, nrv_0_2, nrv_0_3, nrv_0_4, nrv_0_5):
if dist_label == "N(0,1)":
dist_aggr.append(rv_1)
elif dist_label == "N(0,2)":
dist_aggr.append(rv_2)
elif dist_label == "N(0,3)":
dist_aggr.append(rv_3)
elif dist_label == "N(0,4)":
dist_aggr.append(rv_4)
else:
dist_aggr.append(rv_5)

df = pd.DataFrame(dict(l = dist_labels, dist = dist_aggr))

joyplot(df = df, font_size = 13, xtick_labelsize = 12, xlim_range = (-19,19),
ylim_range = (0,.02), label_fontsize = 18, xlabel_text = "Scale Transformation (Scaling)")

## Location-Scale Transformation (Shifting and Scaling)

In [3]:
standard_normal_rv = np.random.normal(loc = 0, scale = 1, size = 2000)
nrv_2_2 = np.random.normal(loc = 2, scale = 2, size = 2000)
nrv_4_3 = np.random.normal(loc = 4, scale = 3, size = 2000)
nrv_6_4 = np.random.normal(loc = 6, scale = 4, size = 2000)
nrv_8_5 = np.random.normal(loc = 8, scale = 5, size = 2000)

dist_labels = np.tile(["N(0,1)", "N(2,2)", "N(4,3)", "N(6,4)", "N(8,5)"], 400)
dist_aggr = []

for dist_label, rv_0, rv_2, rv_4, rv_6, rv_8 in zip(dist_labels, standard_normal_rv, nrv_2_2, nrv_4_3, nrv_6_4, nrv_8_5):
if dist_label == "N(0,1)":
dist_aggr.append(rv_0)
elif dist_label == "N(2,2)":
dist_aggr.append(rv_2)
elif dist_label == "N(4,3)":
dist_aggr.append(rv_4)
elif dist_label == "N(6,4)":
dist_aggr.append(rv_6)
else:
dist_aggr.append(rv_8)

df = pd.DataFrame(dict(l = dist_labels, dist = dist_aggr))

joyplot(df = df, font_size = 13, xtick_labelsize = 12, xlim_range = (-19,19),
ylim_range = (0,.02), label_fontsize = 18, xlabel_text = "Location-Scale Transformation (Shifting and Scaling)")