analysis of initial risk attitude distributions & population outcomes¶

Analyzing data generated from a batch run with 1000 iterations each per risk distribution

  • maximum run length of 3000 steps
  • convergence threshold at <=7% agents changing for two adjustment rounds in a row
In [1]:
import polars as pl


df = pl.read_csv("../../data/hawkdovemulti/dist_c7_3k_2024-02-27T162947_580557_model.csv")
In [2]:
total_runs = len(df)

print(f"Analyzing {total_runs} runs")
Analyzing 5000 runs

what % converged?¶

In [3]:
converged_df = df.filter(df["status"] == "converged")
len(converged_df)
Out[3]:
4594

almost all of them!

how long does it take to converge?¶

In [4]:
converged_df["Step"].describe()
Out[4]:
shape: (9, 2)
statisticvalue
strf64
"count"4594.0
"null_count"0.0
"mean"147.572921
"std"135.641494
"min"50.0
"25%"70.0
"50%"110.0
"75%"170.0
"max"2410.0
In [5]:
converged_df["Step"].plot.hist()
Out[5]:

compare different initial distributions¶

In [6]:
df["risk_distribution"].unique()
Out[6]:
shape: (5,)
risk_distribution
str
"bimodal"
"skewed left"
"normal"
"skewed right"
"uniform"

How many converged runs in each subset?

In [7]:
converged_df.group_by("risk_distribution").len().rename({"len": "count"})
Out[7]:
shape: (5, 2)
risk_distributioncount
stru32
"skewed right"987
"skewed left"963
"uniform"938
"bimodal"710
"normal"996
In [8]:
# filter converged run data into subsets by risk distribution

subset = {}

for distribution in converged_df["risk_distribution"].unique():
    subset[distribution] = converged_df.filter(pl.col("risk_distribution") == distribution)

How does initial distribution affect convergence?¶

In [9]:
status_by_dist = df.group_by("risk_distribution", "status").len().rename({"len": "count"})
status_by_dist
Out[9]:
shape: (10, 3)
risk_distributionstatuscount
strstru32
"skewed right""running"13
"bimodal""running"290
"normal""converged"996
"bimodal""converged"710
"normal""running"4
"uniform""converged"938
"skewed left""running"37
"uniform""running"62
"skewed left""converged"963
"skewed right""converged"987
In [10]:
import altair as alt

alt.Chart(status_by_dist).mark_bar().encode(
    x='risk_distribution:N',
    y='count',
    color='status:N'
).properties(title="Simulation status (converged/running) by risk distribution", width=250, height=400)
Out[10]:
In [11]:
alt.Chart(converged_df).mark_boxplot(size=20).encode(
    x='risk_distribution:N',
    y='Step',
).properties(
    title=alt.TitleParams(
        "Simulation run length by risk distribution",  
        subtitle="(converged runs only)"), 
    width=350, height=450)
Out[11]:

population categories by risk distribution¶

In [15]:
import altair as alt
from simulatingrisk.hawkdovemulti import analysis_utils


uniform_chart = analysis_utils.graph_population_risk_category(
    analysis_utils.groupby_population_risk_category(subset["uniform"])
).properties(title="Uniform")

normal_chart  = analysis_utils.graph_population_risk_category(
    analysis_utils.groupby_population_risk_category(subset["normal"])
).properties(title="Normal")

bimodal_chart  = analysis_utils.graph_population_risk_category(
    analysis_utils.groupby_population_risk_category(subset["bimodal"])
).properties(title="Bimodal")

skewedleft_chart  = analysis_utils.graph_population_risk_category(
    analysis_utils.groupby_population_risk_category(subset["skewed left"])
).properties(title="Skewed Left")

skewedright_chart  = analysis_utils.graph_population_risk_category(
    analysis_utils.groupby_population_risk_category(subset["skewed right"])
).properties(title="Skewed Right")

(uniform_chart | normal_chart | bimodal_chart | skewedleft_chart | skewedright_chart) \
.properties(title=alt.TitleParams("Distribution of Convergence States by Initial Risk Attitude Distribution", anchor="middle")).resolve_scale(y='shared')
Out[15]:
In [16]:
# display the same charts but in two rows

((uniform_chart | normal_chart | bimodal_chart ) & (skewedleft_chart | skewedright_chart)) \
.properties(
    title=alt.TitleParams("Distribution of Convergence States by Initial Risk Attitude Distribution", anchor="middle")
).resolve_scale(
    y='shared'
).configure_legend(orient="none", legendX=750, legendY=400)
Out[16]:
In [20]:
# output just the uniform/random distribution chart by itself, for inclusion in the paper

uniform_chart.properties(title="Distribution of Convergence States across Simulation Runs")
Out[20]:
In [ ]: