Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite the result set #1166

Merged
merged 11 commits into from
Feb 6, 2018
11 changes: 10 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ cache.txt
test.csv
summary.csv
basic_tournament.csv
test_outputs/*csv
test_outputs/*csv.summary
test_outputs/*svg
test_outputs/*cache

Expand Down Expand Up @@ -118,3 +118,12 @@ docker-compose.yml

# Mypy files
.mypy_cache/

test_outputs/stochastic_tournament_0.csv
test_outputs/stochastic_tournament_1.csv
test_outputs/test_fingerprint.csv
test_outputs/test_fingerprint_tmp.csv
test_outputs/test_results_from_file.csv
test_outputs/test_results_from_file_tmp.csv
test_outputs/test_tournament.csv
test_outputs/tran_fin.csv
2 changes: 1 addition & 1 deletion axelrod/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from .deterministic_cache import DeterministicCache
from .match_generator import *
from .tournament import Tournament
from .result_set import ResultSet, ResultSetFromFile
from .result_set import ResultSet
from .ecosystem import Ecosystem
from .fingerprint import AshlockFingerprint, TransitiveFingerprint

8 changes: 4 additions & 4 deletions axelrod/ecosystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def __init__(self, results: ResultSet,
population: List[int] = None) -> None:

self.results = results
self.nplayers = self.results.nplayers
self.num_players = self.results.num_players
self.payoff_matrix = self.results.payoff_matrix
self.payoff_stddevs = self.results.payoff_stddevs

Expand All @@ -27,15 +27,15 @@ def __init__(self, results: ResultSet,
if min(population) < 0:
raise TypeError(
"Minimum value of population vector must be non-negative")
elif len(population) != self.nplayers:
elif len(population) != self.num_players:
raise TypeError(
"Population vector must be same size as number of players")
else:
norm = sum(population)
self.population_sizes = [[p / norm for p in population]]
else:
self.population_sizes = [
[1 / self.nplayers for _ in range(self.nplayers)]]
[1 / self.num_players for _ in range(self.num_players)]]

# This function is quite arbitrary and probably only influences the
# kinetics for the current code.
Expand All @@ -47,7 +47,7 @@ def __init__(self, results: ResultSet,
def reproduce(self, turns: int):

for iturn in range(turns):
plist = list(range(self.nplayers))
plist = list(range(self.num_players))
pops = self.population_sizes[-1]

# The unit payoff for each player in this turn is the sum of the
Expand Down
42 changes: 21 additions & 21 deletions axelrod/fingerprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import matplotlib.pyplot as plt
import numpy as np
import tqdm
import dask.dataframe as dd
import dask as da
from mpl_toolkits.axes_grid1 import make_axes_locatable

import axelrod as axl
Expand Down Expand Up @@ -266,7 +268,7 @@ def construct_tournament_elements(self, step: float,

def fingerprint(
self, turns: int = 50, repetitions: int = 10, step: float = 0.01,
processes: int=None, filename: str = None, in_memory: bool = False,
processes: int=None, filename: str = None,
progress_bar: bool = True
) -> dict:
"""Build and play the spatial tournament.
Expand All @@ -290,10 +292,7 @@ def fingerprint(
The number of processes to be used for parallel processing
filename: str, optional
The name of the file for self.spatial_tournament's interactions.
if None and in_memory=False, will auto-generate a filename.
in_memory: bool
Whether self.spatial_tournament keeps interactions_dict in memory or
in a file.
if None, will auto-generate a filename.
progress_bar : bool
Whether or not to create a progress bar which will be updated

Expand All @@ -305,7 +304,7 @@ def fingerprint(
"""

temp_file_descriptor = None
if not in_memory and filename is None:
if filename is None:
temp_file_descriptor, filename = mkstemp()

edges, tourn_players = self.construct_tournament_elements(
Expand All @@ -318,13 +317,10 @@ def fingerprint(
self.spatial_tournament.play(build_results=False,
filename=filename,
processes=processes,
in_memory=in_memory,
progress_bar=progress_bar)
if in_memory:
self.interactions = self.spatial_tournament.interactions_dict
else:
self.interactions = read_interactions_from_file(
filename, progress_bar=progress_bar)

self.interactions = read_interactions_from_file(
filename, progress_bar=progress_bar)

if temp_file_descriptor is not None:
os.close(temp_file_descriptor)
Expand Down Expand Up @@ -483,17 +479,21 @@ def analyse_cooperation_ratio(filename):
opponent in each turn. The ith row corresponds to the ith opponent
and the jth column the jth turn.
"""
did_c = np.vectorize(lambda action: int(action == 'C'))
did_c = np.vectorize(lambda actions: [int(action == 'C')
for action in actions])

cooperation_rates = {}
with open(filename, "r") as f:
reader = csv.reader(f)
for row in reader:
opponent_index, player_history = int(row[1]), list(row[4])
if opponent_index in cooperation_rates:
cooperation_rates[opponent_index].append(did_c(player_history))
else:
cooperation_rates[opponent_index] = [did_c(player_history)]
df = dd.read_csv(filename)
# We ignore the actions of all opponents. So we filter the dataframe to
# only include the results of the player with index `0`.
df = df[df["Player index"] == 0][["Opponent index", "Actions"]]

for _, row in df.iterrows():
opponent_index, player_history = row["Opponent index"], row["Actions"]
if opponent_index in cooperation_rates:
cooperation_rates[opponent_index].append(did_c(player_history))
else:
cooperation_rates[opponent_index] = [did_c(player_history)]

for index, rates in cooperation_rates.items():
cooperation_rates[index] = np.mean(rates, axis=0)
Expand Down
37 changes: 12 additions & 25 deletions axelrod/interaction_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
This is used by both the Match class and the ResultSet class which analyse
interactions.
"""
from collections import Counter
from collections import Counter, defaultdict
import csv
import tqdm
import pandas as pd

from axelrod.action import Action, str_to_actions
from .game import Game
Expand Down Expand Up @@ -239,36 +240,22 @@ def compute_sparklines(interactions, c_symbol='█', d_symbol=' '):
sparkline(histories[1], c_symbol, d_symbol))


def read_interactions_from_file(filename, progress_bar=True,
num_interactions=False):
def read_interactions_from_file(filename, progress_bar=True):
"""
Reads a file and returns a dictionary mapping tuples of player pairs to
lists of interactions
"""
df = pd.read_csv(filename)[["Interaction index", "Player index",
"Opponent index", "Actions"]]
groupby = df.groupby("Interaction index")
if progress_bar:
if not num_interactions:
with open(filename) as f:
num_interactions = sum(1 for line in f)
progress_bar = tqdm.tqdm(total=num_interactions, desc="Loading")

pairs_to_interactions = {}
with open(filename, 'r') as f:
for row in csv.reader(f):
index_pair = (int(row[0]), int(row[1]))
p1_actions = str_to_actions(row[4])
p2_actions = str_to_actions(row[5])
interaction = list(zip(p1_actions, p2_actions))

try:
pairs_to_interactions[index_pair].append(interaction)
except KeyError:
pairs_to_interactions[index_pair] = [interaction]

if progress_bar:
progress_bar.update()
groupby = tqdm.tqdm(groupby)

if progress_bar:
progress_bar.close()
pairs_to_interactions = defaultdict(list)
for _, d in tqdm.tqdm(groupby):
key = tuple(d[["Player index", "Opponent index"]].iloc[0])
value = list(map(str_to_actions, zip(*d["Actions"])))
pairs_to_interactions[key].append(value)
return pairs_to_interactions


Expand Down
16 changes: 8 additions & 8 deletions axelrod/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def default_cmap(version: str = "2.0") -> str:
class Plot(object):
def __init__(self, result_set: ResultSet) -> None:
self.result_set = result_set
self.nplayers = self.result_set.nplayers
self.num_players = self.result_set.num_players
self.players = self.result_set.players

def _violinplot(
Expand All @@ -40,16 +40,16 @@ def _violinplot(
ax = ax

figure = ax.get_figure()
width = max(self.nplayers / 3, 12)
width = max(self.num_players / 3, 12)
height = width / 2
spacing = 4
positions = spacing * arange(1, self.nplayers + 1, 1)
positions = spacing * arange(1, self.num_players + 1, 1)
figure.set_size_inches(width, height)
ax.violinplot(data, positions=positions, widths=spacing / 2,
showmedians=True, showextrema=False)
ax.set_xticks(positions)
ax.set_xticklabels(names, rotation=90)
ax.set_xlim([0, spacing * (self.nplayers + 1)])
ax.set_xlim([0, spacing * (self.num_players + 1)])
ax.tick_params(axis='both', which='both', labelsize=8)
if title:
ax.set_title(title)
Expand Down Expand Up @@ -175,14 +175,14 @@ def _payoff_heatmap(
ax = ax

figure = ax.get_figure()
width = max(self.nplayers / 4, 12)
width = max(self.num_players / 4, 12)
height = width
figure.set_size_inches(width, height)
matplotlib_version = matplotlib.__version__
cmap = default_cmap(matplotlib_version)
mat = ax.matshow(data, cmap=cmap)
ax.set_xticks(range(self.result_set.nplayers))
ax.set_yticks(range(self.result_set.nplayers))
ax.set_xticks(range(self.result_set.num_players))
ax.set_yticks(range(self.result_set.num_players))
ax.set_xticklabels(names, rotation=90)
ax.set_yticklabels(names)
ax.tick_params(axis='both', which='both', labelsize=16)
Expand Down Expand Up @@ -246,7 +246,7 @@ def stackplot(
ticks = []
for i, n in enumerate(self.result_set.ranked_names):
x = -0.01
y = (i + 0.5) * 1 / self.result_set.nplayers
y = (i + 0.5) * 1 / self.result_set.num_players
ax.annotate(
n, xy=(x, y), xycoords=trans, clip_on=False, va='center',
ha='right', fontsize=5)
Expand Down
Loading