diff --git a/axelrod/plot.py b/axelrod/plot.py index 0a6341a60..a69c735d5 100644 --- a/axelrod/plot.py +++ b/axelrod/plot.py @@ -1,3 +1,5 @@ +import numpy + matplotlib_installed = True try: import matplotlib.pyplot as plt @@ -11,7 +13,6 @@ class Plot(object): def __init__(self, result_set): self.result_set = result_set - # self._nplayers = self.result_set.nplayers self.matplotlib_installed = matplotlib_installed @property @@ -55,6 +56,44 @@ def boxplot(self): plt.title(self._boxplot_title) return figure + @property + def _winplot_dataset(self): + # Sort wins by median + wins = self.result_set.wins + players = self.result_set.players + medians = map(numpy.median, wins) + medians = sorted([(m, i) for (i, m) in enumerate(medians)], reverse=True) + # Reorder and grab names + wins = [wins[x[1]] for x in medians] + ranked_names = [str(players[x[1]]) for x in medians] + return wins, ranked_names + + @property + def _winplot_title(self): + return ("Distributions of wins:" + " {} turns repeated {} times ({} strategies)").format( + self.result_set.turns, + self.result_set.repetitions, + len(self.result_set.ranking)) + + def winplot(self): + if not self.matplotlib_installed: + return None + + wins, ranked_names = self._winplot_dataset + maximum = max(max(w) for w in wins) + + figure = plt.figure() + plt.boxplot(wins) + plt.xticks( + self._boxplot_xticks_locations, + ranked_names, + rotation=90) + plt.tick_params(axis='both', which='both', labelsize=7) + plt.title(self._winplot_title) + plt.ylim(-0.5, 0.5 + maximum) + return figure + def payoff(self): if not self.matplotlib_installed: diff --git a/axelrod/tests/unit/test_plot.py b/axelrod/tests/unit/test_plot.py index 0a6274b38..83728ea87 100644 --- a/axelrod/tests/unit/test_plot.py +++ b/axelrod/tests/unit/test_plot.py @@ -26,11 +26,13 @@ def setUpClass(cls): cls.expected_boxplot_xticks_labels = ['Player3', 'Player1', 'Player2'] cls.expected_boxplot_title = ('Mean score per stage game over 5 turns' ' repeated 2 times (3 strategies)') - cls.expected_payoff_dataset = [ [0.0, 3.2, 3.2], [4.2, 0.0, 2.0], [3.6, 1.8, 0.0]] + cls.expected_winplot_dataset = ([[2, 4], [0, 2], [0, 0]], + ['Player1', 'Player2', 'Player3']) + cls.expected_winplot_title = "Distributions of wins: 5 turns repeated 2 times (3 strategies)" def test_init(self): result_set = self.test_result_set @@ -67,6 +69,23 @@ def test_boxplot(self): else: self.skipTest('matplotlib not installed') + def test_winplot_dataset(self): + plot = axelrod.Plot(self.test_result_set) + self.assertSequenceEqual( + plot._winplot_dataset, + self.expected_winplot_dataset) + + def test_winplot_title(self): + plot = axelrod.Plot(self.test_result_set) + self.assertEqual(plot._winplot_title, self.expected_winplot_title) + + def test_winplot(self): + if matplotlib_installed: + plot = axelrod.Plot(self.test_result_set) + self.assertIsInstance(plot.winplot(), matplotlib.pyplot.Figure) + else: + self.skipTest('matplotlib not installed') + def test_payoff_dataset(self): plot = axelrod.Plot(self.test_result_set) self.assertSequenceEqual( diff --git a/axelrod/tournament_manager.py b/axelrod/tournament_manager.py index c12a8b406..7d00ee97b 100644 --- a/axelrod/tournament_manager.py +++ b/axelrod/tournament_manager.py @@ -126,7 +126,7 @@ def _save_plots(self, tournament, ecosystem=None, image_format="svg"): self._logger.error('The matplotlib library is not installed. ' 'No plots will be produced') return - for plot_type in ('boxplot', 'payoff'): + for plot_type in ('boxplot', 'payoff', 'winplot'): figure = getattr(plot, plot_type)() file_name = self._output_file_path( tournament.name + '_' + plot_type, image_format) diff --git a/docs/_static/usage/demo_strategies_winplot.svg b/docs/_static/usage/demo_strategies_winplot.svg new file mode 100644 index 000000000..d38fe3061 --- /dev/null +++ b/docs/_static/usage/demo_strategies_winplot.svg @@ -0,0 +1,1287 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/usage.rst b/docs/usage.rst index b4d099e29..72733e4b7 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -303,6 +303,27 @@ The resulting wins matrix is:: which shows, for example, that Cooperator had 0 wins, Defector won 8 times in each repetition and Random won 4 times in the first repetition and twice in the second. +For any tournament, a plot of the distribution of wins is created, much like the +plot for the distribution of mean scores. For the demo strategies we create the +plot as follows:: + + import axelrod + strategies = [s() for s in axelrod.demo_strategies] + tournament = axelrod.Tournament(strategies, game=Game(30, 0, 50, 10)) + results = tournament.play() + plot = axelrod.Plot(results) + p = plot.winplot() + p.show() + +This produces: + +.. image:: _static/usage/demo_strategies_winplot.svg + :width: 50% + :align: center + +In this case most of the strategies are deterministic, so there is not much +variation in the distributions. See below for a more complex example. + Noisy Tournaments ^^^^^^^^^^^^^^^^^ @@ -365,6 +386,23 @@ and accordingly to the ranking of strategies overall: | |boxplot_no_noise| | |boxplot_5_noise| | +--------------------+-------------------+ +as well as the distributions of wins: + +.. |winplot_no_noise| image:: http://axelrod-python.github.io/tournament/assets/strategies_winplot.svg + :width: 75% + :align: middle + :alt: Strategy performance without noise + +.. |winplot_5_noise| image:: http://axelrod-python.github.io/tournament/assets/strategies_winplot_noise_5.svg + :width: 75% + :align: middle + :alt: Strategy performance with 5% noise + ++--------------------+-------------------+ +| |winplot_no_noise| | |winplot_5_noise| | ++--------------------+-------------------+ + + To run a noisy tournament, just use the keyword argument `noise` when creating tournaments. Both `run_axelrod` and the utility function `run_tournaments` accept and passthrough the noise argument. To run the @@ -528,6 +566,13 @@ The results from the tournament itself (ordered by median score): :width: 50% :align: center +The distributions of head-to-head wins for each strategy: + +.. image:: http://axelrod-python.github.io/tournament/assets/strategies_winplot.svg + :width: 50% + :align: center + + The payoff matrix from that tournament: .. image:: http://axelrod-python.github.io/tournament/assets/strategies_payoff.svg