Repository: N-Wouda/ALNS

Issue:
Fix Result.plot_operator_counts plot looking 'weird'
E.g. when not plotting all possible outcome types, the axes are still fixed as if one did. Like so:

![image](https://user-images.githubusercontent.com/16272507/74932339-d684dd00-53e1-11ea-8fb2-8c6a08e098ea.png)


Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/N-Wouda/ALNS/pull/21?src=pr&el=h1) Report
> Merging [#21](https://codecov.io/gh/N-Wouda/ALNS/pull/21?src=pr&el=desc) into [master](https://codecov.io/gh/N-Wouda/ALNS/commit/c669e907808fa02138a55a68877d02021272af30&el=desc) will **not change** coverage by `%`.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/N-Wouda/ALNS/pull/21/graphs/tree.svg?width=650&height=150&src=pr&token=xtNEr42uZZ)](https://codecov.io/gh/N-Wouda/ALNS/pull/21?src=pr&el=tree)

```diff
@@            Coverage Diff            @@
##            master       #21   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           16        16           
  Lines          287       271   -16     
=========================================
- Hits           287       271   -16     
```


| [Impacted Files](https://codecov.io/gh/N-Wouda/ALNS/pull/21?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [alns/ALNS.py](https://codecov.io/gh/N-Wouda/ALNS/pull/21/diff?src=pr&el=tree#diff-YWxucy9BTE5TLnB5) | `100.00% <0.00%> (ø)` | :arrow_up: |
| [alns/Result.py](https://codecov.io/gh/N-Wouda/ALNS/pull/21/diff?src=pr&el=tree#diff-YWxucy9SZXN1bHQucHk=) | `100.00% <0.00%> (ø)` | :arrow_up: |
| [alns/Statistics.py](https://codecov.io/gh/N-Wouda/ALNS/pull/21/diff?src=pr&el=tree#diff-YWxucy9TdGF0aXN0aWNzLnB5) | `100.00% <0.00%> (ø)` | :arrow_up: |
| [alns/criteria/SimulatedAnnealing.py](https://codecov.io/gh/N-Wouda/ALNS/pull/21/diff?src=pr&el=tree#diff-YWxucy9jcml0ZXJpYS9TaW11bGF0ZWRBbm5lYWxpbmcucHk=) | `100.00% <0.00%> (ø)` | :arrow_up: |
| [alns/criteria/RecordToRecordTravel.py](https://codecov.io/gh/N-Wouda/ALNS/pull/21/diff?src=pr&el=tree#diff-YWxucy9jcml0ZXJpYS9SZWNvcmRUb1JlY29yZFRyYXZlbC5weQ==) | `100.00% <0.00%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/N-Wouda/ALNS/pull/21?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/N-Wouda/ALNS/pull/21?src=pr&el=footer). Last update [c669e90...b3d5084](https://codecov.io/gh/N-Wouda/ALNS/pull/21?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/alns/Result.py b/alns/Result.py
index a788279..beec688 100644
--- a/alns/Result.py
+++ b/alns/Result.py
@@ -165,9 +165,9 @@ class Result:
         operator_names = list(operator_counts.keys())
 
         operator_counts = np.array(list(operator_counts.values()))
-        cumulative_counts = operator_counts.cumsum(axis=1)
+        cumulative_counts = operator_counts[:, :num_types].cumsum(axis=1)
 
-        ax.set_xlim(right=np.sum(operator_counts, axis=1).max())
+        ax.set_xlim(right=cumulative_counts[:, -1].max())
 
         for idx in range(num_types):
             widths = operator_counts[:, idx]
diff --git a/setup.py b/setup.py
index 7c2d8cb..adb3f2b 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@ import setuptools
 
 MAJOR = 1
 MINOR = 2
-MAINTENANCE = 0
+MAINTENANCE = 1
 MODIFIER = ""
 
 VERSION = "{0}.{1}.{2}{3}".format(MAJOR, MINOR, MAINTENANCE, MODIFIER)


Test patch:
diff --git a/alns/tests/test_result.py b/alns/tests/test_result.py
index 7f309f2..bc7c663 100644
--- a/alns/tests/test_result.py
+++ b/alns/tests/test_result.py
@@ -13,7 +13,7 @@ from .states import Sentinel
 try:
     from matplotlib.testing.decorators import check_figures_equal
 except ImportError:
-    def check_figures_equal(*args, **kwargs):       # placeholder
+    def check_figures_equal(*args, **kwargs):  # placeholder
         return check_figures_equal
 
 
@@ -69,19 +69,21 @@ def get_objective_plot(ax, *args, **kwargs):
     ax.set_xlabel("Iteration (#)")
 
 
-def get_operator_plot(figure, destroy, repair, title=None, **kwargs):
+def get_operator_plot(figure, destroy, repair, legend=None, suptitle=None,
+                      **kwargs):
     """
     Helper method.
     """
-    def _helper(ax, operator_counts, title, **kwargs):
+
+    def _helper(ax, operator_counts, title):
         operator_names = list(operator_counts.keys())
 
         operator_counts = np.array(list(operator_counts.values()))
-        cumulative_counts = operator_counts.cumsum(axis=1)
+        cumulative_counts = operator_counts[:, :len(legend)].cumsum(axis=1)
 
-        ax.set_xlim(right=np.sum(operator_counts, axis=1).max())
+        ax.set_xlim(right=cumulative_counts[:, -1].max())
 
-        for idx in range(4):
+        for idx in range(len(legend)):
             widths = operator_counts[:, idx]
             starts = cumulative_counts[:, idx] - widths
 
@@ -94,16 +96,19 @@ def get_operator_plot(figure, destroy, repair, title=None, **kwargs):
         ax.set_xlabel("Iterations where operator resulted in this outcome (#)")
         ax.set_ylabel("Operator")
 
-    if title is not None:
-        figure.suptitle(title)
+    if suptitle is not None:
+        figure.suptitle(suptitle)
+
+    if legend is None:
+        legend = ["Best", "Better", "Accepted", "Rejected"]
 
     d_ax, r_ax = figure.subplots(nrows=2)
 
-    _helper(d_ax, destroy, "Destroy operators", **kwargs)
-    _helper(r_ax, repair, "Repair operators", **kwargs)
+    _helper(d_ax, destroy, "Destroy operators")
+    _helper(r_ax, repair, "Repair operators")
 
-    figure.legend(["Best", "Better", "Accepted", "Rejected"],
-                  ncol=4,
+    figure.legend(legend,
+                  ncol=len(legend),
                   loc="lower center")
 
 
@@ -161,7 +166,7 @@ def test_plot_objectives_kwargs(fig_test, fig_ref):
     correctly passed to the ``plot`` method.
     """
     result = get_result(Sentinel())
-    kwargs = dict(lw=5, marker='*', title="Test title")
+    kwargs = dict(lw=5, marker='*')
 
     # Tested plot
     result.plot_objectives(fig_test.subplots(), **kwargs)
@@ -241,7 +246,7 @@ def test_plot_operator_counts_title(fig_test, fig_ref):
     get_operator_plot(fig_ref,
                       result.statistics.destroy_operator_counts,
                       result.statistics.repair_operator_counts,
-                      title="A random test title")
+                      suptitle="A random test title")
 
 
 @pytest.mark.matplotlib
@@ -278,3 +283,24 @@ def test_plot_operator_counts_kwargs(fig_test, fig_ref):
                       result.statistics.destroy_operator_counts,
                       result.statistics.repair_operator_counts,
                       **kwargs)
+
+
+@pytest.mark.matplotlib
+@pytest.mark.skipif(sys.version_info < (3, 5),
+                    reason="Plot testing is not reliably available for Py3.4")
+@check_figures_equal(extensions=['png'])
+def test_plot_operator_counts_legend_length(fig_test, fig_ref):
+    """
+    Tests if the length of the passed-in legend is used to determine which
+    counts to show.
+    """
+    result = get_result(Sentinel())
+
+    # Tested plot
+    result.plot_operator_counts(fig_test, legend=["Best"])
+
+    # Reference plot
+    get_operator_plot(fig_ref,
+                      result.statistics.destroy_operator_counts,
+                      result.statistics.repair_operator_counts,
+                      legend=["Best"])