model_wrapper.py#

Initialize#

# Run dependencies
%run ./model_python_lib_utils.ipynb
%run ./model_python_lib_event_counts.ipynb
%run ./model_python_lib_decision_functions.ipynb
%run ./python_lib_visualization.ipynb
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
File /var/folders/vc/0d4071bd7rbd6q1btkk05jcw0000gn/T/ipykernel_3371/3777295251.py:9, in <cell line: 9>()
      7 import numpy as np
      8 import pandas as pd
----> 9 from scipy import stats
     10 import seaborn as sns
     12 from utils import N_ROUNDS

ModuleNotFoundError: No module named 'scipy'
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Input In [1], in <cell line: 5>()
      3 get_ipython().run_line_magic('run', './model_python_lib_event_counts.ipynb')
      4 get_ipython().run_line_magic('run', './model_python_lib_decision_functions.ipynb')
----> 5 get_ipython().run_line_magic('run', './python_lib_visualization.ipynb')

File /Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/IPython/core/interactiveshell.py:2305, in InteractiveShell.run_line_magic(self, magic_name, line, _stack_depth)
   2303     kwargs['local_ns'] = self.get_local_scope(stack_depth)
   2304 with self.builtin_trap:
-> 2305     result = fn(*args, **kwargs)
   2306 return result

File /Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/IPython/core/magics/execution.py:717, in ExecutionMagics.run(self, parameter_s, runner, file_finder)
    715     with preserve_keys(self.shell.user_ns, '__file__'):
    716         self.shell.user_ns['__file__'] = filename
--> 717         self.shell.safe_execfile_ipy(filename, raise_exceptions=True)
    718     return
    720 # Control the response to exit() calls made by the script being run

File /Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/IPython/core/interactiveshell.py:2811, in InteractiveShell.safe_execfile_ipy(self, fname, shell_futures, raise_exceptions)
   2809 result = self.run_cell(cell, silent=True, shell_futures=shell_futures)
   2810 if raise_exceptions:
-> 2811     result.raise_error()
   2812 elif not result.success:
   2813     break

File /Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/IPython/core/interactiveshell.py:251, in ExecutionResult.raise_error(self)
    249     raise self.error_before_exec
    250 if self.error_in_exec is not None:
--> 251     raise self.error_in_exec

    [... skipping hidden 1 frame]

File /var/folders/vc/0d4071bd7rbd6q1btkk05jcw0000gn/T/ipykernel_3371/3777295251.py:9, in <cell line: 9>()
      7 import numpy as np
      8 import pandas as pd
----> 9 from scipy import stats
     10 import seaborn as sns
     12 from utils import N_ROUNDS

ModuleNotFoundError: No module named 'scipy'

Read data#

df = read_rps_data(os.path.join("data", DEFAULT_FILE))
df.head()
game_id version is_sona_autocredit sona_experiment_id sona_credit_token sona_survey_code round_index player_id is_bot bot_strategy bot_move_probabilities round_begin_ts player_move player_rt player_outcome player_outcome_viewtime player_points player_total
0 30aea025-4c7b-41c0-bd03-363cc73bf583 2 1 1768 19421bc286424246b6b1e873e7a55a8e 32028 1 c82739a2-9e24-4c1b-840f-9eb00d10cd9b 0 outcome_transition_dual_dependency {'win': {'0': {'rock': {'rock': 0.049999999999... 1583904283047 rock 2091 tie 3951 0 0
1 30aea025-4c7b-41c0-bd03-363cc73bf583 2 1 1768 19421bc286424246b6b1e873e7a55a8e 32028 1 f1c3a029-e381-4f79-afbe-677ef9416f04 1 outcome_transition_dual_dependency {'win': {'0': {'rock': {'rock': 0.049999999999... 1583904283047 rock 0 tie 0 0 0
2 30aea025-4c7b-41c0-bd03-363cc73bf583 2 1 1768 19421bc286424246b6b1e873e7a55a8e 32028 2 c82739a2-9e24-4c1b-840f-9eb00d10cd9b 0 outcome_transition_dual_dependency {'win': {'0': {'rock': {'rock': 0.049999999999... 1583904289171 paper 2886 loss 1741 -1 0
3 30aea025-4c7b-41c0-bd03-363cc73bf583 2 1 1768 19421bc286424246b6b1e873e7a55a8e 32028 2 f1c3a029-e381-4f79-afbe-677ef9416f04 1 outcome_transition_dual_dependency {'win': {'0': {'rock': {'rock': 0.049999999999... 1583904289171 scissors 0 win 0 3 0
4 30aea025-4c7b-41c0-bd03-363cc73bf583 2 1 1768 19421bc286424246b6b1e873e7a55a8e 32028 3 c82739a2-9e24-4c1b-840f-9eb00d10cd9b 0 outcome_transition_dual_dependency {'win': {'0': {'rock': {'rock': 0.049999999999... 1583904293847 scissors 1676 tie 2040 0 -1

Add bot predictors#

Initialize columns

# TODO may want to move these to `utils.py` as globals so that functions in `event_counts.py` can reference them there
# Add columns corresponding to opponent move counts
supplementary_cols = [
    'opponent_move', 
    'previous_move', 'opponent_previous_move', 
    'previous_outcome', 'opponent_previous_outcome',
    'previous_transition', 'opponent_previous_transition',
    'current_transition',
    'opponent_prev2_transition'
]

event_counts = [
    # transition counts
    'up_transition_count', 'down_transition_count', 'stay_transition_count',
    'cournot_up_transition_count', 'cournot_down_transition_count', 'cournot_stay_transition_count',
    # outcome-dependent transition counts
    'win_up_count', 'win_down_count', 'win_stay_count', 
    'loss_up_count', 'loss_down_count', 'loss_stay_count',
    'tie_up_count', 'tie_down_count', 'tie_stay_count', 
    # dual transition outcome counts
    'up_win_up_count', 'up_win_down_count', 'up_win_stay_count', 
    'up_loss_up_count', 'up_loss_down_count', 'up_loss_stay_count',
    'up_tie_up_count', 'up_tie_down_count', 'up_tie_stay_count',
    'down_win_up_count', 'down_win_down_count', 'down_win_stay_count',
    'down_loss_up_count', 'down_loss_down_count', 'down_loss_stay_count',
    'down_tie_up_count', 'down_tie_down_count', 'down_tie_stay_count',
    'stay_win_up_count', 'stay_win_down_count', 'stay_win_stay_count',
    'stay_loss_up_count', 'stay_loss_down_count', 'stay_loss_stay_count',
    'stay_tie_up_count', 'stay_tie_down_count', 'stay_tie_stay_count'
]

opponent_counts = [
    # base move counts
    'opponent_rock_count', 'opponent_paper_count', 'opponent_scissors_count',
    # transition counts
    'opponent_up_transition_count', 'opponent_down_transition_count', 'opponent_stay_transition_count', 
    'opponent_cournot_up_transition_count', 'opponent_cournot_down_transition_count', 'opponent_cournot_stay_transition_count',
    # outcome-dependent transition counts   
    'opponent_win_up_count', 'opponent_win_down_count', 'opponent_win_stay_count',
    'opponent_loss_up_count', 'opponent_loss_down_count', 'opponent_loss_stay_count',
    'opponent_tie_up_count', 'opponent_tie_down_count', 'opponent_tie_stay_count',
    # dual transition outcome counts
    'opponent_up_win_up_count', 'opponent_up_win_down_count', 'opponent_up_win_stay_count', 
    'opponent_up_loss_up_count', 'opponent_up_loss_down_count', 'opponent_up_loss_stay_count',
    'opponent_up_tie_up_count', 'opponent_up_tie_down_count', 'opponent_up_tie_stay_count',
    'opponent_down_win_up_count', 'opponent_down_win_down_count', 'opponent_down_win_stay_count',
    'opponent_down_loss_up_count', 'opponent_down_loss_down_count', 'opponent_down_loss_stay_count',
    'opponent_down_tie_up_count', 'opponent_down_tie_down_count', 'opponent_down_tie_stay_count',
    'opponent_stay_win_up_count', 'opponent_stay_win_down_count', 'opponent_stay_win_stay_count',
    'opponent_stay_loss_up_count', 'opponent_stay_loss_down_count', 'opponent_stay_loss_stay_count',
    'opponent_stay_tie_up_count', 'opponent_stay_tie_down_count', 'opponent_stay_tie_stay_count'
]


df = add_col(df, supplementary_cols, value = '')
df = add_col(df, event_counts, value = 1)
df = add_col(df, opponent_counts, value = 1)

# df.head()

Populate new columns with counts

game_split = separate_df(df)
game_split = get_event_counts(df, game_split)
df = pd.concat(game_split).reset_index(drop = True)

df.head(8)
game_id version is_sona_autocredit sona_experiment_id sona_credit_token sona_survey_code round_index player_id is_bot bot_strategy ... opponent_down_tie_stay_count opponent_stay_win_up_count opponent_stay_win_down_count opponent_stay_win_stay_count opponent_stay_loss_up_count opponent_stay_loss_down_count opponent_stay_loss_stay_count opponent_stay_tie_up_count opponent_stay_tie_down_count opponent_stay_tie_stay_count
0 30aea025-4c7b-41c0-bd03-363cc73bf583 2 1 1768 19421bc286424246b6b1e873e7a55a8e 32028 1 c82739a2-9e24-4c1b-840f-9eb00d10cd9b 0 outcome_transition_dual_dependency ... 1 1 1 1 1 1 1 1 1 1
1 30aea025-4c7b-41c0-bd03-363cc73bf583 2 1 1768 19421bc286424246b6b1e873e7a55a8e 32028 1 f1c3a029-e381-4f79-afbe-677ef9416f04 1 outcome_transition_dual_dependency ... 1 1 1 1 1 1 1 1 1 1
2 30aea025-4c7b-41c0-bd03-363cc73bf583 2 1 1768 19421bc286424246b6b1e873e7a55a8e 32028 2 c82739a2-9e24-4c1b-840f-9eb00d10cd9b 0 outcome_transition_dual_dependency ... 1 1 1 1 1 1 1 1 1 1
3 30aea025-4c7b-41c0-bd03-363cc73bf583 2 1 1768 19421bc286424246b6b1e873e7a55a8e 32028 2 f1c3a029-e381-4f79-afbe-677ef9416f04 1 outcome_transition_dual_dependency ... 1 1 1 1 1 1 1 1 1 1
4 30aea025-4c7b-41c0-bd03-363cc73bf583 2 1 1768 19421bc286424246b6b1e873e7a55a8e 32028 3 c82739a2-9e24-4c1b-840f-9eb00d10cd9b 0 outcome_transition_dual_dependency ... 1 1 1 1 1 1 1 1 1 1
5 30aea025-4c7b-41c0-bd03-363cc73bf583 2 1 1768 19421bc286424246b6b1e873e7a55a8e 32028 3 f1c3a029-e381-4f79-afbe-677ef9416f04 1 outcome_transition_dual_dependency ... 1 1 1 1 1 1 1 1 1 1
6 30aea025-4c7b-41c0-bd03-363cc73bf583 2 1 1768 19421bc286424246b6b1e873e7a55a8e 32028 4 c82739a2-9e24-4c1b-840f-9eb00d10cd9b 0 outcome_transition_dual_dependency ... 1 1 1 1 1 1 1 1 1 1
7 30aea025-4c7b-41c0-bd03-363cc73bf583 2 1 1768 19421bc286424246b6b1e873e7a55a8e 32028 4 f1c3a029-e381-4f79-afbe-677ef9416f04 1 outcome_transition_dual_dependency ... 1 1 1 1 1 1 1 1 1 1

8 rows × 114 columns

Compute probabilities#

# TODO make these programmatic (esp. final set)

# Move baserate probabilities
df = add_prob(
    df, 
    'opponent_rock_count', 'opponent_paper_count', 'opponent_scissors_count', 
    'p_opponent_rock', 'p_opponent_paper', 'p_opponent_scissors'
)
# Transition probabilities
df = add_prob(
    df, 
    'opponent_up_transition_count', 'opponent_down_transition_count', 'opponent_stay_transition_count', 
    'p_opponent_transition_up', 'p_opponent_transition_down', 'p_opponent_transition_stay'
)
df = add_prob(
    df, 
    'opponent_cournot_up_transition_count', 'opponent_cournot_down_transition_count', 'opponent_cournot_stay_transition_count', 
    'p_opponent_cournot_transition_up', 'p_opponent_cournot_transition_down', 'p_opponent_cournot_transition_stay'
)
# Outcome-transition probabilities
df = add_prob(
    df,
    'opponent_win_up_count', 'opponent_win_down_count', 'opponent_win_stay_count',
    'p_opponent_win_up', 'p_opponent_win_down', 'p_opponent_win_stay'
)
df = add_prob(
    df,
    'opponent_loss_up_count', 'opponent_loss_down_count', 'opponent_loss_stay_count',
    'p_opponent_loss_up', 'p_opponent_loss_down', 'p_opponent_loss_stay'
)
df = add_prob(
    df,
    'opponent_tie_up_count', 'opponent_tie_down_count', 'opponent_tie_stay_count',
    'p_opponent_tie_up', 'p_opponent_tie_down', 'p_opponent_tie_stay'
)

# Dual transition outcome probabilities
df = add_prob(
    df,
    'opponent_up_win_up_count', 'opponent_up_win_down_count', 'opponent_up_win_stay_count',
    'p_opponent_up_win_up', 'p_opponent_up_win_down', 'p_opponent_up_win_stay'
)
df = add_prob(
    df,
    'opponent_up_loss_up_count', 'opponent_up_loss_down_count', 'opponent_up_loss_stay_count',
    'p_opponent_up_loss_up', 'p_opponent_up_loss_down', 'p_opponent_up_loss_stay'
)
df = add_prob(
    df,
    'opponent_up_tie_up_count', 'opponent_up_tie_down_count', 'opponent_up_tie_stay_count',
    'p_opponent_up_tie_up', 'p_opponent_up_tie_down', 'p_opponent_up_tie_stay'
)

df = add_prob(
    df,
    'opponent_down_win_up_count', 'opponent_down_win_down_count', 'opponent_down_win_stay_count',
    'p_opponent_down_win_up', 'p_opponent_down_win_down', 'p_opponent_down_win_stay'
)
df = add_prob(
    df,
    'opponent_down_loss_up_count', 'opponent_down_loss_down_count', 'opponent_down_loss_stay_count',
    'p_opponent_down_loss_up', 'p_opponent_down_loss_down', 'p_opponent_down_loss_stay'
)
df = add_prob(
    df,
    'opponent_down_tie_up_count', 'opponent_down_tie_down_count', 'opponent_down_tie_stay_count',
    'p_opponent_down_tie_up', 'p_opponent_down_tie_down', 'p_opponent_down_tie_stay'
)

df = add_prob(
    df,
    'opponent_stay_win_up_count', 'opponent_stay_win_down_count', 'opponent_stay_win_stay_count',
    'p_opponent_stay_win_up', 'p_opponent_stay_win_down', 'p_opponent_stay_win_stay'
)
df = add_prob(
    df,
    'opponent_stay_loss_up_count', 'opponent_stay_loss_down_count', 'opponent_stay_loss_stay_count',
    'p_opponent_stay_loss_up', 'p_opponent_stay_loss_down', 'p_opponent_stay_loss_stay'
)
df = add_prob(
    df,
    'opponent_stay_tie_up_count', 'opponent_stay_tie_down_count', 'opponent_stay_tie_stay_count',
    'p_opponent_stay_tie_up', 'p_opponent_stay_tie_down', 'p_opponent_stay_tie_stay'
)


# df.head(8)

Calculate Expected Value#

Filter bot rows, any rows without interpretable values for previous move, etc.

# Filter out non-nan, agent-only rows and add expected value calculations
df_agent = drop_bot_rows(df)


df_agent = df_agent.dropna(subset=['previous_move']) # TODO why is this necessary?
# NB: need this for cournot transition tracking
df_agent = df_agent[df_agent['previous_move'] != 'none']
df_agent = df_agent[df_agent['previous_move'] != '']
df_agent = df_agent[df_agent['opponent_previous_move'] != '']

# outcome-transition
df_agent = df_agent[df_agent['opponent_previous_transition'] != '']
df_agent = df_agent[df_agent['opponent_prev2_transition'] != '']


# df_agent.head(25)
# df_agent.shape

Compute expected value

# EV for move base rates
df_agent = ev_move_baserate(df_agent)

# EV for transitions
df_agent = ev_transitions(df_agent)
df_agent = ev_cournot(df_agent)

# EV for outcome-transitions
df_agent = ev_previous_outcome(df_agent)

# EV for dual outcome-transitions
df_agent = ev_previous_outcome_previous_transition(df_agent)


# df_agent.head(6)
# df_agent.shape
<ipython-input-1-a2bbd7e510ee>:75: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df_agent['filler_1'] = points_lookup(agent_move, transition_move_lookup("transition_up", df_agent[move])).tolist()
<ipython-input-1-a2bbd7e510ee>:76: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df_agent['filler_2'] = points_lookup(agent_move, transition_move_lookup("transition_down", df_agent[move])).tolist()
<ipython-input-1-a2bbd7e510ee>:77: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df_agent['filler_3'] = points_lookup(agent_move, transition_move_lookup("transition_stay", df_agent[move])).tolist()
<ipython-input-1-a2bbd7e510ee>:78: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df_agent['prod_1'] = df_agent[prob1] * df_agent.filler_1
<ipython-input-1-a2bbd7e510ee>:79: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df_agent['prod_2'] = df_agent[prob2] * df_agent.filler_2
<ipython-input-1-a2bbd7e510ee>:80: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df_agent['prod_3'] = df_agent[prob3] * df_agent.filler_3
<ipython-input-1-a2bbd7e510ee>:81: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df_agent['ev_transitions'] = df_agent['prod_1'] + df_agent['prod_2'] + df_agent['prod_3']
<ipython-input-1-a2bbd7e510ee>:83: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df_agent[ev_col_name] = df_agent['ev_transitions']
# Test
# model7 = df_agent.copy()


# model7['ev_rock'] = model7['ev_outcome_dual_depend_rock']
# model7['ev_paper'] = model7['ev_outcome_dual_depend_paper']
# model7['ev_scissors'] = model7['ev_outcome_dual_depend_scissors']

# # Compute softmax distribution (1 min.)
# m7_softmax = get_softmax_probabilities(
#     model7, 
#     ['ev_rock', 'ev_paper', 'ev_scissors']
# )

# # Select agent move based on softmax computed above (1 min.)
# model7 = pick_move(model7, m7_softmax)

# # Evaluate outcome of agent move choices in simulation above
# model7 = assign_agent_outcomes(model7)
# plot_summary_coarse = win_summary(groupby_f_data(model7, 'agent_outcome', 5), 'agent_outcome')
# plot_summary_fine = win_summary(groupby_f_data(model7, 'agent_outcome', 60), 'agent_outcome')
# plot_summary_fine = plot_summary_fine[plot_summary_fine['bin'] <= '50']
# plot_win_rates(plot_summary_coarse)
# plot_win_rates(plot_summary_fine)
Created in deepnote.com Created in Deepnote