A Fragment of the Sphinx

jupyter
Author

Gabriel Stechschulte

Published

July 25, 2022

This code complements the figures and methods described in Chapter 4: A Fragment of the Sphinx of the book Shape: The Hidden Geometry of Everything by Jordan Ellenberg. In 1897, Sir Ronald Ross had discovered that maleria was carried by the bite of the anopheles mosquito. In the late summer of 1904, he gave a lecture in Missouri, United States titled “The Logical Basis of Sanitary Policy of Mosquito Reduction”. This lecture contained the first glimmer into a new geometric theory that would explode into physics, finance, and many other areas of science and engineering: the random walk.

Ross proposed you eliminate propogation of mosquitos in a circular region by draining the pools where they breed. That doesn’t eliminate all potentially malarial mosquitoes from the region, because others may be born outside the circle and fly in. But, the mosquitoes’ life is brief and lacks ambition; it won’t set a course straight for the center and stick to it. So, some region around the center of the circle would hopefully be malaria free, as long as the circle is large enough. How large is large enough? That depends on how far a mosquito is likely to travel.

“Suppose that a mosquito is born at a given point, and that during its life it wanders about, to or fro, to left or to right, where it wills…After a time it will die. What are the probabilities that its dead body will be found at a given distance from its birthplace?”

One-Dimensional

Ross was only able to handle the simple case where the mosquito is fixed to a straight line, choosing merely whether to flit northeast or southwest. To deal with the one-dimensional case on pg.66, we need a notion of distance and space. The outcome, defined as a choice to fly either northeast or eouthwest over the course of 10 days, can be described using a Binomial distribution parameterized by \(n =\) trials and \(p =\) probability of success. Setting \(n=1\) and \(p=0.5\) indicates an equal probability of observing \(1\) or \(0\) and is equivalent to a Beroulli distribution. For the one-dimensional case, \(1\) is encoded as moving \(1\) unit northeast and \(0\) as moving \(-1\) units southwest.

Code
def plot_walk_1d(direction):

    direction_df = pd.DataFrame.from_dict(
    direction, orient='index', columns=['move'])
    direction_df.index = direction_df.index.set_names(['step'])

    shift = direction_df['move'].shift(periods=1, fill_value=0)
    direction_df['running_tally'] = direction_df['move'].cumsum()
    direction_df = direction_df.reset_index()

    x_min, x_max = direction_df['running_tally'].min(), \
    direction_df['running_tally'].max()

    fig = px.scatter(
            direction_df, 
            x='running_tally', y=np.zeros(len(direction_df)), 
            animation_frame='step',
            range_x=[-10, 10],
            labels={
                'y': 'Northeast - Southwest'
            },
            title='Mosquito Distance Traveled'
            )

    fig.update_yaxes(showgrid=False, 
                    zeroline=True, zerolinecolor='grey', zerolinewidth=1,
                    showticklabels=False)
    fig.update_layout(
        xaxis = dict(
            tickmode='linear'
        )
    )
    
    fig.show()
outcome = np.random.binomial(1, 0.5, 20)

direction = {}
direction[0] = 0
cnt = 0
for step in outcome:
    cnt += 1
    if step == 0:
        direction[cnt] = 1
    else:
        direction[cnt] = -1

plot_walk_1d(direction)
Unable to display output for mime type(s): application/vnd.plotly.v1+json

Two-Dimensional

On pg.66, Ellenberg shows a two-dimensional representation of a hypothetical mosquito flitting about, to or fro, to left or to right, where it wills. The code below achieves a similar simulation. However, instead of explicitly using a Binomial distribution, the np.random.choice() function is used and is parameterized by ([-1, 1], n_steps). This is equivalent to using a Bernoulli distribution using the same parameters as in the 1d simulation, with the exception of either \(-1\) or \(1\) being returned instead of \(1\) or \(0\).

For the notion of space, the x-y plane is used. North and south are encoded as the y-axis whereas west and east are encoded as the x-axis. By calling the np.random.choice() function twice, one for each unit step on the 2d coordinate plane, it can take the following directions: - north and east: \((+, +)\) - north and west: \((+, -)\) - south and west: \((-, -)\) - south and east: \((-, +)\)

Code
def plot_2d_walk(df):

    fig = go.Figure(
    layout=go.Layout(
        xaxis=dict(range=[-80, 80], autorange=False),
        yaxis=dict(range=[-80, 80], autorange=False),
        width=1000, height=650,
        xaxis_title='Units (East or West)',
        yaxis_title='Units (North or South)',
        title="Mosquito Random Walk (2d)",
        updatemenus=[dict(
            type="buttons",
            buttons=[dict(label="Play",
                            method="animate",
                            args=[None, {
                                'frame': {'duration': 0.5}, 
                                'transition': {'duration': 0}
                                }])])]
        )
    )

    fig.add_trace(
        go.Scatter(
            x=df.x[:1],
            y=df.y[:1]
        )
    )

    fig.update(frames=[
        go.Frame(data=[go.Scatter(x=df.x[:k], y=df.y[:k])])
            for k in range(1, len(df) + 1)
        ]
    )

    fig.show()
def mosquito_random_walk_2d(n_steps):

    x_steps = np.random.choice([-1, 1], n_steps)
    y_steps = np.random.choice([-1, 1], n_steps)
    x_pos, y_pos = np.cumsum(x_steps), np.cumsum(y_steps)


    df = pd.DataFrame({
        'step': np.arange(0, n_steps),
        'x': x_pos,
        'y': y_pos
    })

    plot_2d_walk(df)
mosquito_random_walk_2d(n_steps=1500)
Unable to display output for mime type(s): application/vnd.plotly.v1+json