Back to Article
satellite.ipynb
Download Notebook
In [1]:
import altair as alt
import numpy as np
import pandas as pd

Parameters:

In [2]:
x_mean, y_mean = 4, 4
sigma_X, sigma_Y = 3, 3
sigma_D = 0.5
dAverage = np.sqrt(x_mean**2 + y_mean**2)

2D Gaussian function f(x, y):

In [3]:
def f_xy(x, y):
    return (
        1
        / (2 * np.pi * sigma_X * sigma_Y)
        * np.exp(
            -(
                ((x - x_mean) ** 2) / (2 * sigma_X**2)
                + ((y - y_mean) ** 2) / (2 * sigma_Y**2)
            )
        )
    )

Distance Gaussian f_D(d, x, y):

In [4]:
def fD(d, x, y):
    return (
        1
        / (np.sqrt(2 * np.pi) * sigma_D)
        * np.exp(-((d - np.sqrt(x**2 + y**2)) ** 2) / (2 * sigma_D**2))
    )

Combined function f(x, y, d):

In [5]:
def f_xy_d(x, y, d):
    return fD(d, x, y) * f_xy(x, y)

Interactive plotting function: one panel for f(x, y), the other for f(x, y, d) with a circle of radius d.

In [6]:
def generate_common_components(font_size=12, domain=[-10, 10]):
    axis_cfg = alt.Axis(
        grid=False,
        labelFont="Times",
        labelFontSize=font_size,
        labelFontStyle="normal",
        titleFont="Times",
        titleFontSize=font_size,
        titleFontStyle="italic",
    )

    xs = np.linspace(domain[0], domain[1], 70)
    X, Y = np.meshgrid(xs, xs)
    step = xs[1] - xs[0]

    df_grid = pd.DataFrame(
        {
            "x": X.ravel(),
            "y": Y.ravel(),
            "x2": (X + step).ravel(),
            "y2": (Y + step).ravel(),
            "center_x": (X + step / 2).ravel(),
            "center_y": (Y + step / 2).ravel(),
            "value_uncond": f_xy(X, Y).ravel(),
        }
    )

    d_slider = alt.param(
        "d",
        bind=alt.binding_range(
            min=float(dAverage - 2), max=float(dAverage + 2), step=0.1, name="𝑑"
        ),
        value=float(dAverage),
    )

    encode_x = alt.X("x:Q", title="x", scale=alt.Scale(domain=domain), axis=axis_cfg)
    encode_y = alt.Y("y:Q", title="y", scale=alt.Scale(domain=domain), axis=axis_cfg)

    return {
        "axis_cfg": axis_cfg,
        "df_grid": df_grid,
        "d_slider": d_slider,
        "encode_x": encode_x,
        "encode_y": encode_y,
    }


def plot_unconditional(components):
    chart = (
        alt.Chart(components["df_grid"])
        .mark_rect()
        .encode(
            x=components["encode_x"],
            x2="x2:Q",
            y=components["encode_y"],
            y2="y2:Q",
            color=alt.Color("value_uncond:Q", legend=None),
        )
        .properties(width=300, height=300)
    )
    return chart


def plot_conditional(components):
    expr = (
        "datum.value_uncond / (sqrt(2 * PI) * {sd}) * "
        "exp(- (pow(d - sqrt(pow(datum.center_x, 2) + pow(datum.center_y, 2)), 2)) / (2 * {sd} * {sd}))"
    ).format(sd=sigma_D)

    density_chart = (
        alt.Chart(components["df_grid"])
        .transform_calculate(value_cond=expr)
        .mark_rect()
        .encode(
            x=components["encode_x"],
            x2="x2:Q",
            y=components["encode_y"],
            y2="y2:Q",
            color=alt.Color("value_cond:Q", legend=None),
        )
        .properties(width=300, height=300)
        .add_params(components["d_slider"])
    )

    theta = np.linspace(0, 2 * np.pi, 100)
    circle_df = pd.DataFrame({"theta": theta, "x": np.cos(theta), "y": np.sin(theta)})

    circle = (
        alt.Chart(circle_df)
        .transform_calculate(
            x_scaled="datum.x * d",
            y_scaled="datum.y * d",
        )
        .mark_line(color="gray", strokeWidth=1)
        .encode(
            x=alt.X(
                "x_scaled:Q",
                scale=alt.Scale(domain=[-10, 10]),
                axis=components["axis_cfg"],
            ),
            y=alt.Y(
                "y_scaled:Q",
                scale=alt.Scale(domain=[-10, 10]),
                axis=components["axis_cfg"],
            ),
            order=alt.Order("theta:Q"),
        )
        .add_params(components["d_slider"])
    )

    return density_chart + circle

Create the interactive slider for d

In [7]:
components = generate_common_components(font_size=12)
In [11]:
plot_unconditional(components)
In [12]:
plot_conditional(components)