In [17]:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

Steve and Dave

June 2019

Let Steve and Dave be two souls who occupy the same body. This means that they receive identical sensory input, represented here by the function environment. We want to study how Steve and Dave’s emotional philosophies help or hurt them in coping with their environment’s ups and downs.

Think about environment as a composite of all the good and bad things that happen to Steve/Dave. In real life, we are capable of differentiating between different kinds of good and bad, so we recognize a difference between (say) feeling mildly upset in a Prius and crying desparately in a Maserati. But to the extent that these goods and bads balance each other out, we can say that quality of our environment from day to day is basically a random function. Hence, I set up the environment below by simply pulling 4000 values from a normal distribution.

The graph below shows just thirty consecutive samples of environment. You can think of these as thirty days, weeks, months, or even years; it doesn’t really matter, as the scale would affect only the variance of the normal distribution and not its underlying randomness.

I won’t even be labeling the y-axis, since it is indexing a “high dimensional space” (to borrow Robin Hanson’s nerdy phrase) that encompasses all the various things that might contribute to a broad sense of quality.

In [18]:
np.random.seed(55555)

environment = np.random.randn(4000)

# Portion to graph
d1 = 50
d2 = 80
t = np.arange(d1,d2)

fig = plt.figure(figsize=(10,8))
ax = fig.add_subplot(111,xlabel="Time",ylabel="")
ax.plot(t,environment[d1:d2],color="black",label="Quality of environment")
ax.legend()
Out[18]:
<matplotlib.legend.Legend at 0xc096c30>

The environment function shown above represents the objective quality of Steve/Dave’s environment at a given moment in time.

But Steve and Dave’s subjective experiences of their environment—their moods—also depend on how their environment is changing. If things seem to be getting better, their moods pick up; if the quality of the environment consistently decreases while staying above above neutral, Steve and Dave will still feel disappointment.

In this experiment the environment is randomly determined, so regardless of whatever hopes Steve and Dave may have, they cannot predict how the environment will perform in the future. The best they can do is compare how they are feeling now with how they’ve been feeling recently.

We’ll perform this comparison the mood function. It takes two inputs: t, the time; and d, the number of recent observations to compare the current environment to.

mood computes the (signed) difference between the current environment and the average of environment over the previous d observations.

In [19]:
def mood(t,d): return environment[t] - np.mean(environment[t-d:t])

ExampleMoods = np.vectorize(mood)(t,3)

fig = plt.figure(figsize=(10,8))
ax = fig.add_subplot(111,xlabel="Time",ylabel="")
ax.plot(t,environment[d1:d2],color="black",label="environment")
ax.plot(t,ExampleMoods,color="crimson",label="mood, d = 3",ls="dashed")
ax.legend()
Out[19]:
<matplotlib.legend.Legend at 0xc2cf710>

Perhaps you can see that mood is just a really shitty estimate for the derivative of environment, one biased highly by the value of environment itself. This reflects the fact that people are really bad at assessing their own prospects.

There’s another problem with mood, though. It needs to account for the fact that humans are more sensitive to losses than gains, by a factor that I’m going to ballpark as 3. Let’s fix that.

In [20]:
def mood(t,d): 
    a = environment[t] - np.mean(environment[t-d:t])
    if a < 0: return 3 * a
    else: return a

ExampleMoods2 = np.vectorize(mood)(t,3)    
    
fig = plt.figure(figsize=(10,8))
ax = fig.add_subplot(111,xlabel="Time",ylabel="")
ax.plot(t,environment[d1:d2],color="black",label="environment")
ax.plot(t,ExampleMoods,color="crimson",label="mood, d = 3",ls="dashed")
ax.plot(t,ExampleMoods2,color="crimson",label="mood, d = 3 (with loss aversion)")
ax.legend()
Out[20]:
<matplotlib.legend.Legend at 0xc111090>

OK, now we can talk about the differences between Steve and Dave’s emotional philosophies. Steve is a sensitive guy. It doesn’t take much to pick him up after a bad day. Dave, for his part, is emotionally disciplined. He’s suspicious of quick rushes and derives happiness only from sustained increases in the quality of his environment—ironic, given that unbeknownst to him, the quality of his environment is determined entirely by chance.

Both Steve and Dave respond primarily to their environment. If the environmental quality is good, both will generally feel good. But their subjective feelings are also affecting by their mood.

Since Steve is sensitive, a mild positive stimuli yields a generous response in his emotional state. Likewise, a negative interaction that Dave might take in stride can tank Steve’s mood for the rest of the day. In other words, Steve doesn’t search very widely in assessing his mood. He just compares how he’s feeling now with the most previous observation.

Dave, by contrast, is emotionally disciplined. In his mood function, he takes a longer view than Steve by setting the search paramater to d = 10. That is, Dave compares his current mood to the average of his ten most recent moods.

We multiply these adjusted mood values by 0.8 to make the graph pretty and add the result to the quality of the environment to get Steve and Dave’s subjective SteveFeelings and DaveFeelings. Hashtag mood.

In [21]:
def Steve(t): return environment[t] + 0.8 * mood(t,d = 1)
def Dave(t): return environment[t] + 0.8 * mood(t,d = 10)

SteveFeelings = np.vectorize(Steve)(t)
DaveFeelings = np.vectorize(Dave)(t)
    
fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(111,xlabel="Time",ylabel="")
ax.plot(t,environment[d1:d2],color="black",label="environment")
ax.plot(t,ExampleMoods2,color="crimson",label="mood, d = 3 (with loss aversion)")
ax.plot(t,SteveFeelings,color="seagreen",label="Steve’s feelings")
ax.plot(t,DaveFeelings,color="cornflowerblue",label="Dave’s feelings")
ax.legend()
Out[21]:
<matplotlib.legend.Legend at 0x318c870>

As you’d expect, Steve experiences wilder mood swings. His highs are higher and his lows are lower.

But that’s not all. While both Steve and Dave experience loss aversion, Steve’s loss aversion is compounded by his overall moodiness, which means that his higher highs don’t cancel out his lower lows. In fact, as the scatter plot below shows, he ends up feeling a lot worse on average.

(Not that Dave is particularly cheery either. The phenomenon of loss aversion guarantees that we’ll always feel bad unless the quality of the environment trends upward over time. This helped a lot with evolution.)

In [22]:
# This time we'll include all 4000 observations.
# You can do even more if you want. It converges on similar averages.

d1 = 11
d2 = 4000

t = np.arange(d1,d2)

SteveFeelings = np.vectorize(Steve)(t)
DaveFeelings = np.vectorize(Dave)(t)

fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(111,xlabel="Environment",ylabel="Steve and Dave’s feelings")
ax.plot(environment[d1:d2],DaveFeelings,color="cornflowerblue",
        label="Dave’s average: {}".format(np.mean(DaveFeelings)),
        ls="none",markersize=5,marker="+",alpha=0.6)
ax.axhline(y=np.mean(DaveFeelings),color="cornflowerblue")
ax.plot(environment[d1:d2],SteveFeelings,color="seagreen",
        label="Steve’s average: {}".format(np.mean(SteveFeelings)),
        ls="none",markersize=5,marker="+",alpha=0.6)
ax.axhline(y=np.mean(SteveFeelings),color="seagreen")
ax.legend()
Out[22]:
<matplotlib.legend.Legend at 0xc610f50>

The upshot is that if you just ask who was feeling better, they are neck and neck. Steve doesn’t have more bad days than Dave. His bad days are just worse.

In [23]:
# Proportion of observations for which Dave is happier than Steve
sum((DaveFeelings - SteveFeelings) > 0) / len(SteveFeelings)
Out[23]:
0.500877412885435

Get the Jupyter Notebook version of this document to play with the code yourself.

I’m at maxkapur.com.

Creative Commons License