A numerical study of fixation probabilities for strategies in the Prisoners Dilemma

This notebook contains all the Python code used to generate the plots and figures for the paper.

Here are the versions of the various libraries used:


In [1]:
%matplotlib inline

import axelrod as axl
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import sympy as sym
import itertools
import csv
import os
import glob


from mpl_toolkits.axes_grid1 import make_axes_locatable
from pandas.util.testing import assert_frame_equal

import math
import imp
players = imp.load_source('players', '../src/players.py')
generate_cache = imp.load_source('players', '../src/generate_cache.py')
theoretic = imp.load_source('theoretic', '../src/theoretic.py')
abbreviations = imp.load_source('abbreviations', '../src/abbreviations.py')

assert axl.__version__ == '2.9.0' 
assert pd.__version__ == '0.19.2'
assert matplotlib.__version__ == '2.0.0'
assert sns.__version__ == '0.7.1'
assert np.__version__ == '1.12.1'
assert sym.__version__ == '1.0'

Description of the used strategies

Here are all the strategies used in this experiment:


In [2]:
from abbreviations import abbreviations

def abbreviate(player_name, abbreviations=abbreviations):
    """
    Return the abbreviated name of a play if one has been given
    """
    if isinstance(player_name, axl.Player):
        player_name = str(player_name)
    return abbreviations.get(player_name, player_name)

assert abbreviate("Tit For Tat") == "TfT"
assert abbreviate("Random: 0.5") == "Random"
assert abbreviate(axl.TitForTat()) == "TfT"

In [3]:
from players import selected_players
players_of_interest = set([p for p in selected_players() 
                           if ("length" not in p.classifier["makes_use_of"]) and
                              ("Incorrect" not in abbreviate(p))])
                      
assert len(players_of_interest) == 164

These players have been omitted from the analysis (even though their data was collected). This is because they use the match length or were entered in to the original data sweep by error (the FSM strategies do not use the correct initial state).


In [4]:
set(map(str, selected_players())) - set(map(str, players_of_interest))


Out[4]:
{"BackStabber: ('D', 'D')",
 'Champion',
 "DoubleCrosser: ('D', 'D')",
 "FSM Player: [(0, 'C', 0, 'C'), (0, 'D', 3, 'C'), (1, 'C', 5, 'D'), (1, 'D', 0, 'C'), (2, 'C', 3, 'C'), (2, 'D', 2, 'D'), (3, 'C', 4, 'D'), (3, 'D', 6, 'D'), (4, 'C', 3, 'C'), (4, 'D', 1, 'D'), (5, 'C', 6, 'C'), (5, 'D', 3, 'D'), (6, 'C', 6, 'D'), (6, 'D', 6, 'D'), (7, 'C', 7, 'D'), (7, 'D', 5, 'C')], 1, C",
 "FSM Player: [(0, 'C', 13, 'D'), (0, 'D', 12, 'D'), (1, 'C', 3, 'D'), (1, 'D', 4, 'D'), (2, 'C', 14, 'D'), (2, 'D', 9, 'D'), (3, 'C', 0, 'C'), (3, 'D', 1, 'D'), (4, 'C', 1, 'D'), (4, 'D', 2, 'D'), (5, 'C', 12, 'C'), (5, 'D', 6, 'C'), (6, 'C', 1, 'C'), (6, 'D', 14, 'D'), (7, 'C', 12, 'D'), (7, 'D', 2, 'D'), (8, 'C', 7, 'D'), (8, 'D', 9, 'D'), (9, 'C', 8, 'D'), (9, 'D', 0, 'D'), (10, 'C', 2, 'C'), (10, 'D', 15, 'C'), (11, 'C', 7, 'D'), (11, 'D', 13, 'D'), (12, 'C', 3, 'C'), (12, 'D', 8, 'D'), (13, 'C', 7, 'C'), (13, 'D', 10, 'D'), (14, 'C', 10, 'D'), (14, 'D', 7, 'D'), (15, 'C', 15, 'C'), (15, 'D', 11, 'D')], 1, C",
 "FSM Player: [(0, 'C', 7, 'C'), (0, 'D', 1, 'C'), (1, 'C', 11, 'D'), (1, 'D', 11, 'D'), (2, 'C', 8, 'D'), (2, 'D', 8, 'C'), (3, 'C', 3, 'C'), (3, 'D', 12, 'D'), (4, 'C', 6, 'C'), (4, 'D', 3, 'C'), (5, 'C', 11, 'C'), (5, 'D', 8, 'D'), (6, 'C', 13, 'D'), (6, 'D', 14, 'C'), (7, 'C', 4, 'D'), (7, 'D', 2, 'D'), (8, 'C', 14, 'D'), (8, 'D', 8, 'D'), (9, 'C', 0, 'C'), (9, 'D', 10, 'D'), (10, 'C', 8, 'C'), (10, 'D', 15, 'C'), (11, 'C', 6, 'D'), (11, 'D', 5, 'D'), (12, 'C', 6, 'D'), (12, 'D', 9, 'D'), (13, 'C', 9, 'D'), (13, 'D', 8, 'D'), (14, 'C', 8, 'D'), (14, 'D', 13, 'D'), (15, 'C', 4, 'C'), (15, 'D', 5, 'C')], 1, C",
 'Knowledgeable Worse and Worse',
 'Meta Majority Memory One: 32 players',
 'Meta Winner Memory One: 32 players',
 'NMWE Memory One: 32 players',
 'Stalker: D'}

In [5]:
with open("../data/reference_keys.csv", "r") as f:
    reader = csv.reader(f)
    reference_keys = {player: eval(keys) for player, keys in reader}
    
assert reference_keys['ALLCorALLD'] == ['axelrodproject']
assert reference_keys['Cooperator'] == ['Axelrod1984', 'Mittal2009', 'Press2012']

In [6]:
with open("../tex/list_of_players.tex", "w") as f:
    for player in sorted(players_of_interest, key=str):
        latex_name = "{}".format(player).replace("_", "\_")
        f.write("\item {}".format(latex_name))
        abbreviation = abbreviate(player)
        if abbreviation != player.name:
            f.write("(\\textbf{{{}}})".format(abbreviation))
        if player.classifier["stochastic"]:
            f.write(" - \\textit{Stochastic}")
        else:
            f.write(" - \\textit{Deterministic}")
        try:
            mem = int(player.classifier["memory_depth"])
        except OverflowError:
            mem = "\(\infty\)"
        if player.name == "Grudger":
            mem = 1  # Overwrite incorrect classification
        f.write(" - \\textit{{Memory depth}}: {}".format(mem))
        try:
            f.write(". \cite{{{}}}\n".format(", ".join(sorted(reference_keys[str(player)]))))
        except KeyError:
            f.write(".\n")

Here are some summary information about the strategies


In [7]:
def clean_mem(n):
    try:
        return int(n)
    except OverflowError:
        return -1

player_info = pd.DataFrame([[abbreviate(p), p.classifier["stochastic"], clean_mem(p.classifier['memory_depth'])] 
                        for p in players_of_interest], 
                       columns=["Player", "Stochastic", "Memory Depth"])

In [8]:
temp_df = pd.DataFrame(player_info.groupby("Stochastic")["Player"].count()).reset_index().rename(columns={"Player": "Count"})
for bool in [True, False]:
    filename = "../tex/num_stochastic.tex" if bool else "../tex/num_deterministic.tex"
    with open(filename, "w") as f:
        num = temp_df[temp_df["Stochastic"] == bool]["Count"].iloc[0]
        print(num, bool)
        f.write(str(num))
        
with open("../tex/num_strategies.tex", "w") as f:
    num = len(players_of_interest)
    f.write(str(num))
    print(num)
    
with open("../tex/num_strategies_axelrod.tex", "w") as f:
    num = len(axl.strategies)
    f.write(str(num))
    print(num)


43 True
121 False
164
186

In [9]:
mem_df = pd.DataFrame(player_info.groupby("Memory Depth")["Player"].count()).transpose()
mem_df.rename(index={"Player": "Count"}, inplace=True)
cols = mem_df.columns.tolist()
mem_df = mem_df[cols[1:] + [cols[0]]]
mem_df


Out[9]:
Memory Depth 0 1 2 3 4 5 6 9 10 11 12 16 20 40 200 -1
Count 3 29 12 8 2 6 1 1 5 1 1 2 2 2 1 88

In [10]:
def clean_latex(string):
    """Replace some special carachters"""
    string = string.replace("textbackslashpi", "pi")
    string = string.replace("textbackslashphi", "phi")
    string = string.replace("\\$", "$")
    string = string.replace("\\$", "$")
    string = string.replace("\\textasciicircum", "^")
    string = string.replace("\_", "_")
    string = string.replace("2_2_2", "2\_2\_2")
    string = string.replace("1_1_1", "1\_1\_1")
    for i in range(1, 4):
        string = string.replace("TF{}".format(i), "\\textbf{{TF{}}}".format(i))
    return string

with open("../tex/memory_depth_count.tex", "w") as f:
    string = clean_latex(mem_df.to_latex()).replace("-1", "\(\infty\)")
    f.write(string)

Illustrating the Approximate moran process


In [11]:
cached_outcomes = pd.read_csv("../data/outcomes.csv", header=None, 
                              names=["Player 1", "Player 2", "Score 1", "Score 2", "Iteration"])

cached_outcomes["Player 1"] = cached_outcomes["Player 1"].apply(lambda x: abbreviate(x))
cached_outcomes["Player 2"] = cached_outcomes["Player 2"].apply(lambda x: abbreviate(x))

cached_outcomes = cached_outcomes[cached_outcomes["Player 1"].isin(player_info["Player"]) & 
                                  cached_outcomes["Player 2"].isin(player_info["Player"])]

assert len(cached_outcomes.index) == 1169001

In [12]:
cached_outcomes.head()


Out[12]:
Player 1 Player 2 Score 1 Score 2 Iteration
0 Alternator Hunter Prober 2 2.985 3.010 1
1 Alternator Hunter Prober 3 0.015 4.990 1
2 Adaptive Adaptive 2.950 2.950 1
3 Alternator Hunter Prober 4 0.150 4.900 1
4 Adaptive Adaptive Tit For Tat 2.955 2.955 1

In [13]:
is_stochastic = dict(zip(player_info["Player"], player_info["Stochastic"]))

In [14]:
temp_df = pd.DataFrame(cached_outcomes.groupby(["Player 1", "Player 2"]).count()).reset_index()
temp_df["Outcome count"] = temp_df["Score 1"]
temp_df.drop(["Score 1", "Score 2", "Iteration"], axis=1, inplace=True)

In [15]:
temp_df["Stochastic"] = ((temp_df["Player 1"].map(lambda p: is_stochastic[p]) | 
                          temp_df["Player 2"].map(lambda p: is_stochastic[p])) & 
                         (temp_df["Outcome count"] > 1))

In [16]:
temp_df.head()


Out[16]:
Player 1 Player 2 Outcome count Stochastic
0 $\phi$ $\phi$ 1 False
1 $\phi$ $\pi$ 1 False
2 $\phi$ $e$ 1 False
3 $\phi$ 2TfT 1 False
4 $\phi$ Gradual 1 False

Validating the model

Here we create a number of plots comparing the theoretic fixation probability with the observed fixation probability for a number of initial starting populations.


In [17]:
validation = pd.read_csv("../data/fixation_validation.csv", index_col=False)

In [18]:
validation.tail()


Out[18]:
Repetitions N i Player 1 Player 2 Theoretic Simulated
1059 1000 20 10 Calculator Random: 0.5 0.085562 0.084
1060 1000 20 19 Calculator Random: 0.5 0.700176 0.712
1061 1000 20 1 Cooperator Tit For Tat 0.050000 0.049
1062 1000 20 10 Cooperator Tit For Tat 0.500000 0.502
1063 1000 20 19 Cooperator Tit For Tat 0.950000 0.952

In [19]:
markers = itertools.cycle(('X', '+', 'o')) 
marker_size = 50
fontsize=16

for names, df in validation.groupby(["Player 1", "Player 2"]):
    df.sort_values("N", inplace=True)
    
    repetitions = df["Repetitions"].iloc[0]
    assert all(df["Repetitions"] == repetitions)
    
    # Get names instead of repr (to drop some parameters)
    names = [abbreviate(name) for name in names]
    title = "{} and {} over {} repetitions".format(*names, repetitions)
    filename = "{}_v_{}".format(*names)

    for substr in [": ", ".", ":", " "]:
        filename = filename.replace(substr, "_")
    
    plt.figure()

    labels = ["1", "N / 2", "N - 1"]
    index_sets = [df["i"] == 1, df["i"]  == df["N"] / 2, df["i"] == df["N"] - 1]
    for label, index in zip(labels, index_sets):
        d = df[index]
        plt.plot(d["N"], d["Theoretic"], label="")
        plt.scatter(d["N"], d["Simulated"], marker=next(markers), 
                        label="$x_{{{}}}$".format(label), s=marker_size)
    plt.xticks(d["N"])
    plt.title(title, fontsize=fontsize)
    plt.ylim(0, 1.1)
    plt.ylabel("{} fixation probability".format(names[0]))
    plt.xlabel("$N$")
    plt.legend(fontsize=15)
    plt.savefig("../img/{}.pdf".format(filename))


/home/vince/anaconda3/envs/moran/lib/python3.6/site-packages/ipykernel/__main__.py:6: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
/home/vince/anaconda3/envs/moran/lib/python3.6/site-packages/matplotlib/pyplot.py:524: RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface (`matplotlib.pyplot.figure`) are retained until explicitly closed and may consume too much memory. (To control this warning, see the rcParam `figure.max_open_warning`).
  max_open_warning, RuntimeWarning)

Carry out analysis of Moran processes


In [20]:
main = pd.read_csv("../data/main.csv")
main["player"] = main["player"].apply(lambda x: abbreviate(x))
main["opponent"] = main["opponent"].apply(lambda x: abbreviate(x))
main = main[main["player"].isin(player_info["Player"]) &
            main["opponent"].isin(player_info["Player"])]

# Test known values
expected = pd.DataFrame([["Cooperator", "Defector", 10, 0, 0.019, 0.500],
                         ["TfT", "Defector", 10, 0.262, 0.915, 0.999]],
                        columns=main.columns)
assert_frame_equal(expected, main[((main["player"] == "TfT") | (main["player"] == "Cooperator")) & 
                                  (main["opponent"] == "Defector") & 
                                  (main["N"] == 10)].round(3).reset_index(drop=True))

In [21]:
# Check number of completed  matches: 164 strategies implies 13366 pairs. 
# Each pair is considered twice (invader/resistor)
for N, df in main.groupby("N"):
    num_p_1 = len(df["$p_1$"].dropna().index)
    num_p_n_over_2 = len(df["$p_{N/2}$"].dropna().index)
    num_p_n_minus_1 = len(df["$p_{N-1}$"].dropna().index)
    if N % 2 == 1:
        assert (num_p_1 == num_p_n_minus_1 == 13366 * 2)
        assert num_p_n_over_2 == 0
    else:
        assert num_p_1 == num_p_n_over_2  == num_p_n_minus_1 == 13366 * 2

In [22]:
main.head()


Out[22]:
player opponent N $p_1$ $p_{N/2}$ $p_{N-1}$
0 $\phi$ $\pi$ 2 0.849 0.849 0.855
1 $\phi$ $e$ 2 0.849 0.849 0.855
2 $\phi$ ALLCorALLD 2 0.594 0.594 0.582
3 $\phi$ Adaptive 2 0.027 0.027 0.040
4 $\phi$ Adaptive Pavlov 2006 2 0.479 0.479 0.467

In [23]:
main[(main["player"] == "TfT") & (main["opponent"] == "Defector")]


Out[23]:
player opponent N $p_1$ $p_{N/2}$ $p_{N-1}$
27004 TfT Defector 2 0.497 0.497 0.486
57454 TfT Defector 3 0.403 NaN 0.795
87904 TfT Defector 4 0.355 0.679 0.911
118354 TfT Defector 5 0.331 NaN 0.954
148804 TfT Defector 6 0.316 0.802 0.981
179254 TfT Defector 7 0.289 NaN 0.984
209704 TfT Defector 8 0.260 0.879 0.994
240154 TfT Defector 9 0.261 NaN 0.997
270604 TfT Defector 10 0.262 0.915 0.999
301054 TfT Defector 11 0.261 NaN 0.998
331504 TfT Defector 12 0.251 0.960 1.000
361954 TfT Defector 13 0.239 NaN 1.000
392404 TfT Defector 14 0.221 0.965 1.000

This shows:

  • $p_1$: fixation probability of 1 player in population of N-1 opponents
  • $p_{N/2}$: fixation probability of N/2 players in population of N/2 opponents
  • $p_{N - 1}$: fixation probability of N - 1 players in population of 1 opponents

Mean fixation probability for each strategy


In [24]:
plot_file_labels = ["invade", "resist", "coexist"]

In [25]:
main.head()


Out[25]:
player opponent N $p_1$ $p_{N/2}$ $p_{N-1}$
0 $\phi$ $\pi$ 2 0.849 0.849 0.855
1 $\phi$ $e$ 2 0.849 0.849 0.855
2 $\phi$ ALLCorALLD 2 0.594 0.594 0.582
3 $\phi$ Adaptive 2 0.027 0.027 0.040
4 $\phi$ Adaptive Pavlov 2006 2 0.479 0.479 0.467

In [26]:
plt.rcParams['figure.figsize'] = 8, 30
fontsize = 20


for N in range(2, 14 + 1):
    
    temp_df = main[main["N"] == N].dropna(axis=1)
    if N == 2:
        values = ['$p_1$']
    else:
        values = ['$p_1$', '$p_{N-1}$']
    for value, plot_file_label in zip(values, plot_file_labels):

            fig, ax = plt.subplots()
            data, labels = [], []

            for player, df in temp_df.groupby("player"):

                data.append(list(df[value].dropna()))
                labels.append(player)

            mean_fixation = [np.mean(ele) for ele in data]
            labels, data, _ = zip(*sorted(list(zip(labels, data, mean_fixation)), 
                                          key=lambda x: x[2]))
            # Plot the mean scores
            ax.boxplot(data, vert=False)

            xs = sorted(mean_fixation)
            ys = range(1, len(xs) + 1)
            ax.plot(xs, ys, linewidth=3)

            # Plot the neutral fixation
            if value == '$p_1$':
                ax.axvline(1 / N, linestyle="dashed", linewidth=3, label="$\\frac{1}{N}$", color="black")
            elif value == '$p_{N/2}$':
                ax.axvline(1 / 2, linestyle="dashed", linewidth=3, label="$\\frac{1}{2}$", color="black")
            else:
                ax.axvline((N - 1) / N, linestyle="dashed", linewidth=3, label="$\\frac{N - 1}{N}$", color="black")

            ax.set_yticklabels(labels)
            ax.set_title("$N={}$ {}".format(N, value))
            ax.set_xlim((0, 1))
            ax.grid(b=False)
            ax.legend(fontsize=fontsize)

            fig.tight_layout()
            fig.savefig("../img/boxplot_{}_{}.pdf".format(N, plot_file_label))


/home/vince/anaconda3/envs/moran/lib/python3.6/site-packages/matplotlib/pyplot.py:524: RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface (`matplotlib.pyplot.figure`) are retained until explicitly closed and may consume too much memory. (To control this warning, see the rcParam `figure.max_open_warning`).
  max_open_warning, RuntimeWarning)

In [27]:
def average_rank_df(col):
    
    df = pd.DataFrame(main.groupby(["player", "N"])[col].mean()).reset_index()
    return df.pivot(index="player", columns="N", values=col).rank(ascending=False).reset_index()

In [28]:
def rank_summary_df(value, N=2, number=5):
    data = []
    temp_df = main[main["player"].isin(average_rank_df(value).sort_values(N).head(number)["player"]) & 
                   (main["N"] == N)]
    for player, df in temp_df.groupby("player"):
        data.append([player, np.mean(df[value].dropna())])
    df = pd.DataFrame(data, columns=["Player", "Mean {}".format(value)])
    return df.sort_values("Mean {}".format(value), ascending=False)

In [29]:
number = int(len(players_of_interest) / 10)  # Consider top 10%

for N in range(2, 14 + 1):
    
    temp_df = main[main["N"] == N].dropna(axis=1)
    if N == 2:
        values = ['$p_1$']
    else:
        values = ['$p_1$', '$p_{N-1}$']
    for value, plot_file_label in zip(values, plot_file_labels):
    
        temp_df = main[main["N"] == N].dropna(axis=1)
        if N == 2:
            values = ['$p_1$']
        elif N % 2 == 0:
            values = ['$p_1$','$p_{N-1}$', '$p_{N/2}$']
        else:
            values = ['$p_1$', '$p_{N-1}$']
        for value, plot_file_label in zip(values, plot_file_labels):

            temp_df = rank_summary_df(value=value, N=N, number=number).round(4)
            temp_df.index = range(1, number + 1)
            with open("../tex/summary_top_{}_{}.tex".format(N, plot_file_label), "w") as f:
                f.write(clean_latex(temp_df.to_latex().replace("-1", "\(\infty\)").replace("\{N\(\infty\)\}", "{N-1}")))

In [30]:
def plot_ranks( value):
    rank_df = average_rank_df(value).dropna(axis=1)
    Ns = list(rank_df.columns[1:])
    
    fig, ax1 = plt.subplots()    
    
    sorted_df = rank_df.sort_values(Ns[0], ascending=False)
    ranks, labels = list(zip(*enumerate(sorted_df["player"])))
    ranks = [r + 1 for r in ranks]
    sorted_df = rank_df.sort_values(Ns[-1], ascending=False)
    last_labels = list(sorted_df["player"])
        
    for ax, labels in zip([ax1, ax1.twinx()], [labels, last_labels]):
        ax.set_ylim([min(ranks) - 3, max(ranks) + 3])
        ax.set_yticks(ranks)
        ax.set_yticklabels(labels)  
    ax.grid(b=False)
    
    sorted_df = rank_df.sort_values(Ns[0], ascending=False)
    color_indices = np.linspace(0, 1, len(labels))
    
    for color_index, (_, row) in enumerate(sorted_df.iterrows()):
        plot_color = matplotlib.cm.viridis(color_indices[color_index])
        ax1.plot([len(ranks) + 1 - r for r in list(row)[1:]], c=plot_color)

    ax1.set_xticks(range(len(Ns)))
    ax1.set_xticklabels(list(Ns))
    ax1.set_xlim(0, len(Ns) - 1)
    ax1.set_title("Ranks of Players for {}".format(value))
    ax1.grid(b=False)
    fig.tight_layout()
    return fig

In [31]:
plt.rcParams['figure.figsize'] = 25, 30

for value, plot_file_label in zip(['$p_1$','$p_{N-1}$', '$p_{N/2}$'], plot_file_labels):
    p = plot_ranks(value=value)
    p.savefig("../img/average_rank_vs_population_size_{}.pdf".format(plot_file_label))


IOPub data rate exceeded.
The notebook server will temporarily stop sending output
to the client in order to avoid crashing it.
To change this limit, set the config variable
`--NotebookApp.iopub_data_rate_limit`.

In [32]:
def get_correlation_coefficients(rank_df):
    temp_df = rank_df.dropna(axis=1).select_dtypes(include=['float64'])
    numeric_data = np.array(temp_df).transpose()
    return pd.DataFrame(np.corrcoef(numeric_data), 
                        columns=temp_df.columns, 
                        index=list(temp_df.columns)).round(2)

In [33]:
plt.rcParams['figure.figsize'] = 8, 8

for value, plot_file_label in zip(['$p_1$','$p_{N-1}$', '$p_{N/2}$'], plot_file_labels):
    
    temp_df = get_correlation_coefficients(average_rank_df(value))
    temp_df.to_csv("../data/correlation_coefficient_{}.csv".format(plot_file_label))
    
    plt.figure()

    ax = plt.gca()
    im = plt.imshow(temp_df, cmap="viridis")
    divider = make_axes_locatable(ax)
    cax = divider.append_axes("right", size=1, pad=0.4)
    plt.colorbar(im, cax=cax)

    ax.set_yticks(range(len(temp_df.index)))
    ax.set_yticklabels(temp_df.index)

    ax.set_xticks(range(len(temp_df.index)))
    ax.set_xticklabels(temp_df.index)

    ax.set_title("{}".format(value))
    ax.grid(b=False)
    ax.patch.set_facecolor('None')

    plt.tight_layout()
    plt.savefig("../img/correlation_heatmap_{}.pdf".format(plot_file_label))


Some particular considerations

Investigate the change of rank:


In [34]:
def change_of_rank_df(N_init=2, value="$p_1$", last_N=14):
        
    if value == "$p_{N/2}$" :
        Ns = range(2, last_N + 1, 2)
    else:
        Ns = range(2, last_N + 1)
    
#    if N_init == 2:
#        value = "$p_1$"
    
    df = pd.DataFrame(rank_summary_df(value, N=N_init)["Player"])
    for N in Ns:
        df[N] = list(main[main["N"] == N].groupby(["player"])[value].mean().rank(numeric_only=True, ascending=False)[df["Player"]])
    return df

In [35]:
last_N = 14

ZD_players = [abbreviate(p) for p in players_of_interest if "ZD" in str(p)]
ZD_players

for plot_file_label, value in zip(plot_file_labels, ["$p_1$", "$p_{N-1}$", "$p_{N/2}$"]):
    zd_df = average_rank_df(value)
    zd_df = zd_df[zd_df["player"].isin(ZD_players)].sort_values(2).dropna(axis=1)
    zd_df.rename(columns={"player":"Player"}, inplace=True)
    
    tf_df = average_rank_df(value)
    tf_df = tf_df[tf_df["player"].isin(["TF1", "TF2", "TF3"])].sort_values(2).dropna(axis=1)
    tf_df.rename(columns={"player":"Player"}, inplace=True) 

    df_2 = change_of_rank_df(N_init=2, value=value, last_N=last_N)
    df_last = change_of_rank_df(N_init=last_N, value=value, last_N=last_N)
    df = pd.concat([df_2, df_last, tf_df, zd_df])
    df.drop_duplicates(inplace=True)
    df.rename(columns={"Player": "Size"}, inplace=True)
    print(df)
    
    latex_list = clean_latex(df.to_latex(index=False)).splitlines()
    latex_list.insert(4 + len(df_2.index), "\\midrule")
    
    num_ranking_tf = sum(pd.concat([df_2, df_last])["Player"].isin(["TF1", "TF2", "TF3"]))
    num_extra_tf =  3 - num_ranking_tf
    latex_list.insert(-(2 + len(zd_df.index) + num_extra_tf), "\\midrule")
    latex_list.insert(-(2 + len(zd_df.index)), "\\midrule")
    with open("../tex/change_of_rank_{}.tex".format(plot_file_label), "w") as f:
        f.write('\n'.join(latex_list).replace(".0", ""))


N                    Size      2      3      4      5      6      7      8  \
1                      CS    1.0    1.0    2.0   11.0    9.0   11.0   13.0   
2                Defector    2.0   43.0   80.0   91.0   89.0   87.0   87.0   
0              Aggravater    3.0   50.0   89.0   99.0  102.0  103.0  108.0   
4                Predator    4.0    8.0   24.0   35.0   28.0   33.0   31.0   
3               Handshake    5.0   17.0   40.0   46.0   43.0   46.0   46.0   
2          Evolved FSM 16   31.0   11.0    6.0    2.0    1.0    1.0    1.0   
4       PSO Gambler 2_2_2   29.0   14.0   10.0    6.0    4.0    2.0    2.0   
3    EvolvedLookerUp2_2_2   33.0   18.0   11.0    9.0   10.0    6.0    6.0   
0             Evolved ANN   20.0   10.0    8.0    7.0    8.0    5.0    3.0   
1           Evolved ANN 5   21.0    9.0    7.0    8.0    7.0    4.0    5.0   
137                   TF1    7.0   13.0   33.0   38.0   30.0   39.0   42.0   
138                   TF2    9.0   19.0   29.0   33.0   19.0   28.0   29.0   
139                   TF3   14.0    4.0    5.0    5.0    6.0    9.0   11.0   
160           ZD-Extort-4   16.0   81.0  107.0  120.0  135.0  136.0  142.0   
159        ZD-Extort-2 v2   41.0  105.0  126.0  140.0  152.0  152.0  153.0   
158           ZD-Extort-2   43.0  107.0  125.0  139.0  151.0  151.0  152.0   
163              ZD-SET-2  100.0  111.0  117.0  117.0  122.0  127.0  131.0   
162             ZD-GTFT-2  112.0   92.0   82.0   80.0   81.0   82.0   84.0   
161              ZD-GEN-2  113.0   96.0   87.0   83.0   85.0   88.0   90.0   

N        9     10     11     12     13     14  
1     21.0   16.0   22.0   17.0   25.0   23.0  
2    103.0   97.0  105.0   94.0  103.0  101.0  
0    113.0  114.0  115.0  115.0  116.0  117.0  
4     43.0   36.0   43.0   34.0   45.0   35.0  
3     49.0   48.0   49.0   47.0   50.0   49.0  
2      1.0    1.0    1.0    1.0    1.0    1.0  
4      2.0    2.0    2.0    2.0    2.0    2.0  
3      5.0    3.0    5.0    3.0    3.0    3.0  
0      3.0    4.0    3.0    4.0    4.0    4.0  
1      4.0    5.0    4.0    5.0    5.0    5.0  
137   46.0   42.0   46.0   41.0   46.0   46.0  
138   38.0   27.0   34.0   26.0   32.0   30.0  
139   11.0   12.0   14.0   13.0   13.0   16.0  
160  140.0  142.0  142.0  144.0  144.0  145.0  
159  152.0  153.0  153.0  153.0  152.0  153.0  
158  153.0  152.0  152.0  152.0  153.0  152.0  
163  128.0  131.0  131.0  130.0  132.0  131.0  
162   72.0   81.0   71.0   78.0   72.0   70.0  
161   82.0   87.0   82.0   86.0   83.0   91.0  
N              Size      2      3      4      5      6      7      8      9  \
1                CS    1.0    1.0    1.0    1.0    1.0    1.0    1.0    1.0   
2          Defector    2.0   29.0   55.0   79.0   94.0   97.0   98.0   98.0   
0        Aggravater    3.0   42.0   71.0   97.0  101.0  106.0  107.0  111.0   
4          Predator    4.0    2.0    3.0    3.0    3.0    4.0    4.0    4.0   
3         Handshake    5.0    4.0    5.0    5.0    5.0    5.0    5.0    6.0   
3               TF1    7.0    3.0    2.0    2.0    2.0    2.0    2.0    2.0   
4               TF2   10.0    5.0    4.0    4.0    4.0    3.0    3.0    3.0   
2          Prober 4    6.0    6.0    6.0    6.0    6.0    6.0    6.0    5.0   
139             TF3   13.0    9.0   10.0   11.0   11.0   11.0   13.0   14.0   
160     ZD-Extort-4   19.0   68.0   98.0  106.0  108.0  114.0  115.0  115.0   
159  ZD-Extort-2 v2   49.0   98.0  111.0  121.0  123.0  124.0  124.0  130.0   
158     ZD-Extort-2   50.0   97.0  112.0  123.0  124.0  125.0  123.0  126.0   
163        ZD-SET-2  108.0  105.0  104.0  104.0  103.0  103.0  100.0  100.0   
162       ZD-GTFT-2  112.0   95.0   88.0   84.0   75.0   72.0   71.0   73.0   
161        ZD-GEN-2  114.0   96.0   89.0   86.0   77.0   75.0   72.0   74.0   

N       10     11     12     13     14  
1      1.0    1.0    1.0    1.0    1.0  
2    102.0  101.0  103.0  100.0  102.0  
0    113.0  113.0  116.0  115.0  115.0  
4      4.0    4.0    4.0    4.0    4.0  
3      6.0    6.0    6.0    6.0    6.0  
3      2.0    2.0    2.0    2.0    2.0  
4      3.0    3.0    3.0    3.0    3.0  
2      5.0    5.0    5.0    5.0    5.0  
139   13.0   13.0   13.0   13.0   13.0  
160  118.0  118.0  117.0  118.0  117.0  
159  130.0  132.0  134.0  132.0  134.0  
158  131.0  131.0  132.0  133.0  133.0  
163  101.0   99.0   98.0   98.0   98.0  
162   71.0   71.0   67.0   68.0   68.0  
161   72.0   72.0   68.0   69.0   69.0  
N              Size      2      4      6      8     10     12     14
1                CS    1.0    1.0    1.0    1.0    1.0    1.0    2.0
2          Defector    2.0   78.0   99.0  106.0  110.0  113.0  120.0
0        Aggravater    3.0   91.0  105.0  111.0  122.0  125.0  128.0
4          Predator    4.0    2.0    4.0    4.0    4.0    4.0    4.0
3         Handshake    5.0    6.0    5.0    6.0    6.0    6.0    6.0
4               TF2    9.0    4.0    3.0    2.0    2.0    2.0    1.0
3               TF1    7.0    3.0    2.0    3.0    3.0    3.0    3.0
2          Prober 4    6.0    5.0    6.0    5.0    5.0    5.0    5.0
139             TF3   14.0    8.0    8.0    8.0    8.0    8.0    8.0
160     ZD-Extort-4   16.0  102.0  117.0  129.0  141.0  143.0  145.0
159  ZD-Extort-2 v2   41.0  118.0  135.0  151.0  152.0  152.0  153.0
158     ZD-Extort-2   43.0  117.0  136.0  149.0  151.0  151.0  152.0
163        ZD-SET-2  100.0  110.0  110.0  108.0  106.0  106.0  108.0
162       ZD-GTFT-2  112.0   82.0   80.0   77.0   75.0   75.0   74.0
161        ZD-GEN-2  113.0   85.0   81.0   82.0   79.0   77.0   76.0

Draw cooperation heatmaps


In [36]:
player_names = list(map(abbreviate, sorted([str(p) for p in selected_players() 
                           if ("length" not in p.classifier["makes_use_of"]) and
                              ("Incorrect" not in abbreviate(p))])))

def cooperation_heatmap(filename, player_names=player_names):
    
    matrix = np.array(pd.read_csv(filename, header=None))
    number_of_players = len(player_names)
    assert matrix.shape == (number_of_players, 200)
    
    fig, ax = plt.subplots()
    width = number_of_players / 4
    height = width
    spacing = 4 
    fig.set_size_inches(width, height) 

    mat = ax.matshow(matrix, cmap="plasma")

    ax.set_yticks(range(number_of_players))
    ax.set_yticklabels(player_names)
    ax.set_xlabel("Rounds", fontsize=40)
    ax.tick_params(axis='both', which='both', labelsize=16) 

    divider = make_axes_locatable(ax)
    cax = divider.append_axes("right", size="5%", pad=0.2)
    cax.tick_params(labelsize=40) 
    cbar = fig.colorbar(mat, cax=cax)
    fig.tight_layout()
    
    base = os.path.basename(filename)
    plot_file_name, _ = os.path.splitext(base)
    plot_file_name = plot_file_name.replace(" ", "_")
    plot_file_name = plot_file_name.replace(".", "-")
    plot_file_name = plot_file_name.replace(",", "")
    plot_file_name = plot_file_name.replace(":", "")
    plot_file_name = plot_file_name.replace("__", "_")
    fig.savefig("../img/{}.pdf".format(plot_file_name))

In [37]:
cooperation_files = glob.glob("../data/cooperation_*_array.gz")

for file in cooperation_files:
    cooperation_heatmap(file)