Repository: rootpy/rootpy

Issue:
_HistBase.__getitem__ needs improving
Currently the `_HistBase.__getitem__` check is:

``` python
if index not in range(-1, len(self) + 1):
```

This leaves enough space for under/overflow bins.. except that:

``` ipython
In [84]: h = P.Hist(10, 0, 1)

In [85]: h.Fill(-1, 9); h.Fill(0, 99); h.Fill(1-0.0001, 999); h.Fill(1, 9999)
Out[85]: -1

In [86]: h[-1], h[0], h[1], h[9], h[10]
Out[86]: (999.0, 99.0, 0.0, 999.0, 99.0)

In [87]: h[11]
IndexError
```

i.e, -1 is returning the "last" bin, and h[10] is returning the first bin.

Really, the indices 0..9 inclusive are valid.

Also, the test is rather inefficient for histograms with large numbers of bins, it should really be written:

``` python
if not 0 <= index < len(self):
```



Discussion:
This functioned correctly once upon a time... I'm submitting a PR with a fix now.


Resolving patch:
diff --git a/rootpy/plotting/hist.py b/rootpy/plotting/hist.py
--- a/rootpy/plotting/hist.py
+++ b/rootpy/plotting/hist.py
@@ -344,19 +344,13 @@ def __rsub__(self, other):
 
     def __len__(self):
 
-        return self.GetNbinsX()
+        return self.nbins(axis=1)
 
-    def __getitem__(self, index):
-
-        # TODO: Perhaps this should return a Hist object of dimension (DIM - 1)
-
-        if index not in range(-1, len(self) + 1):
-            raise IndexError("bin index %i out of range" % index)
-
-    def __setitem__(self, index):
+    def _range_check(self, index, axis=1):
 
-        if index not in range(-1, len(self) + 1):
-            raise IndexError("bin index %i out of range" % index)
+        if not 0 <= index < self.nbins(axis=axis):
+            raise IndexError("bin index %i along axis %d is out of range" %
+                             (index, axis))
 
     def __iter__(self):
 
@@ -521,7 +515,7 @@ def __getitem__(self, index):
         if type(index) is slice:
             return self._content()[index]
         """
-        _HistBase.__getitem__(self, index)
+        self._range_check(index)
         return self.y(index)
 
     def __getslice__(self, i, j):
@@ -530,7 +524,7 @@ def __getslice__(self, i, j):
 
     def __setitem__(self, index, value):
 
-        _HistBase.__setitem__(self, index)
+        self._range_check(index)
         self.SetBinContent(index + 1, value)
 
 
@@ -662,7 +656,7 @@ def __getitem__(self, index):
         if isinstance(index, tuple):
             # support indexing like h[1, 2]
             return self.z(*index)
-        _HistBase.__getitem__(self, index)
+        self._range_check(index)
         a = ObjectProxy([
             self.GetBinContent(index + 1, j)
                 for j in xrange(1, self.GetNbinsY() + 1)])
@@ -881,7 +875,7 @@ def __getitem__(self, index):
         if isinstance(index, tuple):
             # support indexing like h[1,2,1]
             return self.w(*index)
-        _HistBase.__getitem__(self, index)
+        self._range_check(index)
         out = []
         for j in xrange(1, self.GetNbinsY() + 1):
             a = ObjectProxy([


Test patch:
diff --git a/rootpy/plotting/tests/test_hist.py b/rootpy/plotting/tests/test_hist.py
--- a/rootpy/plotting/tests/test_hist.py
+++ b/rootpy/plotting/tests/test_hist.py
@@ -1,7 +1,7 @@
 # Copyright 2012 the rootpy developers
 # distributed under the terms of the GNU General Public License
 from rootpy.plotting import Hist, Hist2D, Hist3D, HistStack
-from nose.tools import raises, assert_equals
+from nose.tools import raises, assert_equals, assert_raises
 
 
 def test_init():
@@ -62,6 +62,15 @@ def test_stack():
     stack2 = stack.Clone()
     assert_equals(stack2[2].linecolor, 'green')
 
+def test_indexing():
+
+    hist = Hist(10, 0, 1)
+    hist.Fill(0.5)
+    assert_equals(hist[5], 1)
+    assert_equals(hist[9], 0)
+    assert_raises(IndexError, hist.__getitem__, -1)
+    assert_raises(IndexError, hist.__getitem__, 10)
+
 
 if __name__ == "__main__":
     import nose