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

Add initial play summary to the results set. #797

Merged
merged 2 commits into from
Jan 2, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 38 additions & 5 deletions axelrod/result_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ def _build_empty_metrics(self, keep_interactions=False):
self.cooperation = [[0 for opponent in plist] for player in plist]
self.normalised_cooperation = [[[] for opponent in plist]
for player in plist]
self.initial_cooperation_count = [0 for player in plist]
self.state_distribution = [[Counter() for opponent in plist]
for player in plist]
self.good_partner_matrix = [[0 for opponent in plist]
Expand Down Expand Up @@ -532,6 +533,23 @@ def _update_cooperation(self, p1, p2, cooperations):
self.cooperation[p1][p2] += cooperations[0]
self.cooperation[p2][p1] += cooperations[1]

def _update_initial_cooperation_count(self, p1, p2, initial_cooperations):
"""
During a read of the data, update the initial cooperation count
attribute

Parameters
----------

p1, p2 : int
The indices of the first and second player
initial_cooperations : tuple
A 2 tuple with a 0 or 1 indicating if the initial move of each
player was cooperation (0).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about:

A 2 tuple with a 0 or 1 indicating if the initial move of each player was Cooperation (0) or Defection (1), e.g. (0, 1) for a round (C, D)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done :)

"""
self.initial_cooperation_count[p1] += initial_cooperations[0]
self.initial_cooperation_count[p2] += initial_cooperations[1]

def _update_state_distribution(self, p1, p2, counter):
"""
During a read of the data, update the state_distribution attribute
Expand Down Expand Up @@ -606,6 +624,16 @@ def _build_good_partner_rating(self):
max(1, float(self.total_interactions[player]))
for player in range(self.nplayers)]

@update_progress_bar
def _build_initial_cooperation_rate(self):
"""
At the end of a read of the data, build the normalised initial
cooperation rate attribute
"""
return [self.initial_cooperation_count[player] /
max(1, float(self.total_interactions[player]))
for player in range(self.nplayers)]

def _build_score_related_metrics(self, progress_bar=False,
keep_interactions=False):
"""
Expand Down Expand Up @@ -653,11 +681,14 @@ def _build_score_related_metrics(self, progress_bar=False,
self._update_normalised_scores(repetition, p1, p2,
scores_per_turn)
self._update_cooperation(p1, p2, cooperations)
initial_coops = iu.compute_cooperations(interaction[:1])
self._update_initial_cooperation_count(p1, p2,
initial_coops)
self._update_state_distribution(p1, p2, state_counter)
self._update_good_partner_matrix(p1, p2, cooperations)

if progress_bar:
self.progress_bar = tqdm.tqdm(total=11 + 2 * self.nplayers,
self.progress_bar = tqdm.tqdm(total=12 + 2 * self.nplayers,
desc="Finishing")
self._summarise_normalised_scores()
self._summarise_normalised_cooperation()
Expand All @@ -670,6 +701,7 @@ def _build_score_related_metrics(self, progress_bar=False,
self.payoff_diffs_means = self._build_payoff_diffs_means()
self.vengeful_cooperation = self._build_vengeful_cooperation()
self.cooperating_rating = self._build_cooperating_rating()
self.initial_cooperation_rate = self._build_initial_cooperation_rate()
self.good_partner_rating = self._build_good_partner_rating()
self.eigenjesus_rating = self._build_eigenjesus_rating()
self.eigenmoses_rating = self._build_eigenmoses_rating()
Expand Down Expand Up @@ -739,8 +771,8 @@ def summarise(self):

self.player = namedtuple("Player", ["Rank", "Name", "Median_score",
"Cooperation_rating", "Wins",
"CC_rate", "CD_rate", "DC_rate",
"DD_rate"])
"Initial_C_rate", "CC_rate",
"CD_rate", "DC_rate", "DD_rate"])

states = [(C, C), (C, D), (D, C), (D, D)]
state_prob = []
Expand All @@ -756,7 +788,8 @@ def summarise(self):
state_prob.append(counts)

summary_measures = list(zip(self.players, median_scores,
self.cooperating_rating, median_wins))
self.cooperating_rating, median_wins,
self.initial_cooperation_rate))

summary_data = []
for rank, i in enumerate(self.ranking):
Expand All @@ -769,7 +802,7 @@ def write_summary(self, filename):
"""
Write a csv file containing summary data of the results of the form:

"Rank", "Name", "Median-score-per-turn", "Cooperation-rating", "Wins", "CC-Rate", "CD-Rate", "DC-Rate", "DD-rate"
"Rank", "Name", "Median-score-per-turn", "Cooperation-rating", "Initial_C_Rate", "Wins", "CC-Rate", "CD-Rate", "DC-Rate", "DD-rate"

Parameters
----------
Expand Down
41 changes: 38 additions & 3 deletions axelrod/tests/unit/test_resultset.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ def setUpClass(cls):
[0, 0, 0],
]

cls.expected_initial_cooperation_count = [6, 6, 0]
cls.expected_initial_cooperation_rate = [1, 1, 0]

cls.expected_state_distribution = [
[], [], []
]
Expand Down Expand Up @@ -223,13 +226,13 @@ def test_init_with_different_game(self):
def test_with_progress_bar(self):
rs = axelrod.ResultSet(self.players, self.interactions)
self.assertTrue(rs.progress_bar)
self.assertEqual(rs.progress_bar.total, 11 + 2 * rs.nplayers)
self.assertEqual(rs.progress_bar.total, 12 + 2 * rs.nplayers)
self.assertEqual(rs.progress_bar.n, rs.progress_bar.total)

rs = axelrod.ResultSet(self.players, self.interactions,
progress_bar=True)
self.assertTrue(rs.progress_bar)
self.assertEqual(rs.progress_bar.total, 11 + 2 * rs.nplayers)
self.assertEqual(rs.progress_bar.total, 12 + 2 * rs.nplayers)
self.assertEqual(rs.progress_bar.n, rs.progress_bar.total)

def test_match_lengths(self):
Expand Down Expand Up @@ -345,6 +348,14 @@ def test_cooperation(self):
self.assertEqual(len(rs.cooperation), rs.nplayers)
self.assertEqual(rs.cooperation, self.expected_cooperation)

def test_initial_cooperation_count(self):
rs = axelrod.ResultSet(self.players, self.interactions,
progress_bar=False)
self.assertIsInstance(rs.initial_cooperation_count, list)
self.assertEqual(len(rs.initial_cooperation_count), rs.nplayers)
self.assertEqual(rs.initial_cooperation_count,
self.expected_initial_cooperation_count)

def test_normalised_cooperation(self):
rs = axelrod.ResultSet(self.players, self.interactions,
progress_bar=False)
Expand All @@ -353,6 +364,14 @@ def test_normalised_cooperation(self):
self.assertEqual(rs.normalised_cooperation,
self.expected_normalised_cooperation)

def test_initial_cooperation_rate(self):
rs = axelrod.ResultSet(self.players, self.interactions,
progress_bar=False)
self.assertIsInstance(rs.initial_cooperation_rate, list)
self.assertEqual(len(rs.initial_cooperation_rate), rs.nplayers)
self.assertEqual(rs.initial_cooperation_rate,
self.expected_initial_cooperation_rate)

def test_state_distribution(self):
rs = axelrod.ResultSet(self.players, self.interactions,
progress_bar=False)
Expand Down Expand Up @@ -459,6 +478,12 @@ def test_summarise(self):
self.assertEqual([float(player.Wins) for player in sd],
ranked_median_wins)

ranked_initial_coop_rates = [self.expected_initial_cooperation_rate[i]
for i in rs.ranking]
self.assertEqual([float(player.Initial_C_rate)
for player in sd],
ranked_initial_coop_rates)

for player in sd:
self.assertEqual(player.CC_rate + player.CD_rate + player.DC_rate + player.DD_rate, 1)

Expand All @@ -471,7 +496,7 @@ def test_write_summary(self):
csvreader = csv.reader(csvfile)
for row in csvreader:
ranked_names.append(row[1])
self.assertEqual(len(row), 9)
self.assertEqual(len(row), 10)
self.assertEqual(ranked_names[0], "Name")
self.assertEqual(ranked_names[1:], rs.ranked_names)

Expand Down Expand Up @@ -809,6 +834,9 @@ def setUpClass(cls):
[0, 0, 0],
]

cls.expected_initial_cooperation_count = [6, 3, 0]
cls.expected_initial_cooperation_rate = [1, 1, 0]

cls.expected_vengeful_cooperation = [[2 * element - 1 for element in row]
for row in cls.expected_normalised_cooperation]

Expand Down Expand Up @@ -1026,6 +1054,9 @@ def setUpClass(cls):
[0.0, 0.0, mean([5 / 5.0 for _ in range(3)]), 0.0]
]

cls.expected_initial_cooperation_count = [3.0, 3.0, 0, 3.0]
cls.expected_initial_cooperation_rate = [1.0, 1.0, 0, 1.0]

cls.expected_vengeful_cooperation = [[2 * element - 1 for element in row]
for row in cls.expected_normalised_cooperation]

Expand Down Expand Up @@ -1188,6 +1219,9 @@ def setUpClass(cls):
[0.0, 0.0, 0.0, mean([5 / 5.0 for _ in range(3)])]
]

cls.expected_initial_cooperation_count = [0, 0, 0, 0]
cls.expected_initial_cooperation_rate = [0, 0, 0, 0]

cls.expected_vengeful_cooperation = [[2 * element - 1 for element in row]
for row in cls.expected_normalised_cooperation]

Expand Down Expand Up @@ -1265,3 +1299,4 @@ def test_summarise_without_failure(self, tournament):
total_rate = round(player.CC_rate + player.CD_rate +
player.DC_rate + player.DD_rate, 3)
self.assertTrue(total_rate in [0, 1])
self.assertTrue(0 <= player.Initial_C_rate <= 1)
46 changes: 35 additions & 11 deletions docs/tutorials/getting_started/tournament_results.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ This tutorial will show you how to access the various results of a tournament:
- State distribution: the count of each type of state of a match
- Normalised state distribution: the normalised count of each type of state of a
match
- Initial cooperation count: the count of initial cooperation by each player.
- Initial cooperation rate: the rate of initial cooperation by each player.
- Cooperation rating: cooperation rating of each player
- Vengeful cooperation: a morality metric from the literature (see
:ref:`morality-metrics`).
Expand Down Expand Up @@ -63,8 +65,8 @@ This gives the length of the matches played by each player::
[[10, 10, 10, 10], [10, 10, 10, 10], [10, 10, 10, 10], [10, 10, 10, 10]],
[[10, 10, 10, 10], [10, 10, 10, 10], [10, 10, 10, 10], [10, 10, 10, 10]]]

Every player plays 200 turns against every other player for every repetition of
the tournament.
Every player plays 10 turns against every other player (including themselves)
for every repetition of the tournament.

Scores
------
Expand Down Expand Up @@ -267,6 +269,28 @@ the second the action of the opponent::
Counter({('C', 'C'): 1.0}),
Counter()]]


Initial cooperation counts
--------------------------

This gives the count of cooperations made by each player during the first turn
of every match::

>>> results.initial_cooperation_count
[9, 0, 9, 9]

Each player plays an opponent a total of 9 times (3 opponents and 3
repetitions). Apart from the :code:`Defector`, they all cooperate on the first
turn.

Initial cooperation rates
-------------------------

This gives the rate of which a strategy cooperates during the first turn::

>>> results.initial_cooperation_rate
[1.0, 0.0, 1.0, 1.0]

Morality Metrics
----------------

Expand Down Expand Up @@ -299,10 +323,10 @@ that summarises the results of the tournament::

>>> summary = results.summarise()
>>> pprint.pprint(summary)
[Player(Rank=0, Name='Defector', Median_score=2.6..., Cooperation_rating=0.0, Wins=3.0, CC_rate=...),
Player(Rank=1, Name='Tit For Tat', Median_score=2.3..., Cooperation_rating=0.7, Wins=0.0, CC_rate=...),
Player(Rank=2, Name='Grudger', Median_score=2.3..., Cooperation_rating=0.7, Wins=0.0, CC_rate=...),
Player(Rank=3, Name='Cooperator', Median_score=2.0..., Cooperation_rating=1.0, Wins=0.0, CC_rate=...)]
[Player(Rank=0, Name='Defector', Median_score=2.6..., Cooperation_rating=0.0, Wins=3.0, Initial_C_rate=0.0, CC_rate=...),
Player(Rank=1, Name='Tit For Tat', Median_score=2.3..., Cooperation_rating=0.7, Wins=0.0, Initial_C_rate=1.0, CC_rate=...),
Player(Rank=2, Name='Grudger', Median_score=2.3..., Cooperation_rating=0.7, Wins=0.0, Initial_C_rate=1.0, CC_rate=...),
Player(Rank=3, Name='Cooperator', Median_score=2.0..., Cooperation_rating=1.0, Wins=0.0, Initial_C_rate=1.0, CC_rate=...)]

It is also possible to write this data directly to a csv file using the
`write_summary` method::
Expand All @@ -313,8 +337,8 @@ It is also possible to write this data directly to a csv file using the
... csvreader = csv.reader(outfile)
... for row in csvreader:
... print(row)
['Rank', 'Name', 'Median_score', 'Cooperation_rating', 'Wins', 'CC_rate', 'CD_rate', 'DC_rate', 'DD_rate']
['0', 'Defector', '2.6...', '0.0', '3.0', '0.0', '0.0', '0.4...', '0.6...']
['1', 'Tit For Tat', '2.3...', '0.7', '0.0', '0.66...', '0.03...', '0.0', '0.3...']
['2', 'Grudger', '2.3...', '0.7', '0.0', '0.66...', '0.03...', '0.0', '0.3...']
['3', 'Cooperator', '2.0...', '1.0', '0.0', '0.66...', '0.33...', '0.0', '0.0']
['Rank', 'Name', 'Median_score', 'Cooperation_rating', 'Wins', 'Initial_C_rate', 'CC_rate', 'CD_rate', 'DC_rate', 'DD_rate']
['0', 'Defector', '2.6...', '0.0', '3.0', '0.0', '0.0', '0.0', '0.4...', '0.6...']
['1', 'Tit For Tat', '2.3...', '0.7', '0.0', '1.0', '0.66...', '0.03...', '0.0', '0.3...']
['2', 'Grudger', '2.3...', '0.7', '0.0', '1.0', '0.66...', '0.03...', '0.0', '0.3...']
['3', 'Cooperator', '2.0...', '1.0', '0.0', '1.0', '0.66...', '0.33...', '0.0', '0.0']