Repository: pandas-dev/pandas
Pull request: BUG: select_as_multiple doesn't respect start/stop kwargs GH16209
 - [x] closes #16209
 - [x] tests added / passed
 - [x] passes ``git diff upstream/master --name-only -- '*.py' | flake8 --diff``
 - [x] whatsnew entry


Problem statement:
BUG: HDFStore.select_as_multiple doesn't respect start/stop kwargs

```python
import pandas as pd

df = pd.DataFrame({"foo": [1, 2], "bar": [1, 2]})

with pd.HDFStore("foo.h5", 'w') as store:
    store.append_to_multiple({'selector': ['foo'], 'data': None}, df, selector='selector')
    single_row = store.select_as_multiple(['selector', 'data'], selector='selector', start=0, stop=1)
    assert len(single_row) == 1, "requested 1 row, got back {}".format(len(single_row))


```
Currently select_as_multiple returns the entire table. I would expect, if start=0 and stop=1, for just one row to be returned



<details>
INSTALLED VERSIONS
------------------
commit: None
python: 2.7.12.final.0
python-bits: 64
OS: Linux
OS-release: 2.6.32-642.15.1.el6.centos.plus.x86_64
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_US.UTF-8
LOCALE: None.None

pandas: 0.19.2
nose: None
pip: 9.0.1
setuptools: 32.3.1.post20170108
Cython: 0.25.2
numpy: 1.11.3
scipy: 0.18.1
statsmodels: 0.8.0
xarray: 0.8.2
IPython: 5.1.0
sphinx: 1.5.3
patsy: 0.4.1
dateutil: 2.6.0
pytz: 2016.10
blosc: None
bottleneck: 1.2.0
tables: 3.4.0
numexpr: 2.6.1
matplotlib: None
openpyxl: None
xlrd: 1.0.0
xlwt: None
xlsxwriter: 0.9.6
lxml: None
bs4: 4.5.3
html5lib: 0.999
httplib2: None
apiclient: None
sqlalchemy: 1.1.5
pymysql: 0.7.9.None
psycopg2: None
jinja2: 2.8
boto: None
pandas_datareader: None


</details>

Hints from issue discussion:
yep, looks like https://github.com/pandas-dev/pandas/blob/master/pandas/io/pytables.py#L829 needs to pass ``start/stop`` as well.

pull-requests welcome!
Hmm, so I'm attempting to submit a PR. I've added a new test that mirrors the example from above, but when I insert start/stop into line 829, I get the following test failure. I've spent some time digging around to see why this isn't working, but I can't quite figure out why. It looks like this exception is raised [here](https://github.com/pandas-dev/pandas/blob/master/pandas/io/pytables.py#L4653), which means the 'where' variable (an int64index) is passed to self.generate. I don't know enough about the code to know if that's expected or not.
```
self = [0, 1]

    def evaluate(self):
        """ create and return the numexpr condition and filter """
    
        try:
            self.condition = self.terms.prune(ConditionBinOp)
        except AttributeError:
            raise ValueError("cannot process expression [{0}], [{1}] is not a "
>                            "valid condition".format(self.expr, self))
E           ValueError: cannot process expression [[0 1]], [[0, 1]] is not a valid condition

```
you have to step thru. This is an evaluation tree which can go pretty deep. However I think that all you need to do is make the change I suggested above.
Ahh, I think I figured it out. I also needed to pass self.start/stop to this function call here:  https://github.com/pandas-dev/pandas/blob/master/pandas/io/pytables.py#L1423

I'll submit a PR soon!

Commit message: BUG: select_as_multiple doesn't respect start/stop kwargs GH16209

Patch to review:
diff --git a/doc/source/whatsnew/v0.21.0.txt b/doc/source/whatsnew/v0.21.0.txt
index 36dffc3d3378b..bb9ad95856875 100644
--- a/doc/source/whatsnew/v0.21.0.txt
+++ b/doc/source/whatsnew/v0.21.0.txt
@@ -82,6 +82,7 @@ Indexing
 I/O
 ^^^
 
+- Bug in ``HDFStore.select_as_multiple()`` where start/stop arguments were not respected (:issue:`16209`)
 
 
 Plotting
diff --git a/pandas/io/pytables.py b/pandas/io/pytables.py
index 17bedd016f617..3187d6fb31177 100644
--- a/pandas/io/pytables.py
+++ b/pandas/io/pytables.py
@@ -826,8 +826,8 @@ def func(_start, _stop, _where):
 
             # retrieve the objs, _where is always passed as a set of
             # coordinates here
-            objs = [t.read(where=_where, columns=columns, **kwargs)
-                    for t in tbls]
+            objs = [t.read(where=_where, columns=columns, start=_start,
+                           stop=_stop, **kwargs) for t in tbls]
 
             # concat and return
             return concat(objs, axis=axis,
@@ -1420,7 +1420,8 @@ def get_result(self, coordinates=False):
 
         # if specified read via coordinates (necessary for multiple selections
         if coordinates:
-            where = self.s.read_coordinates(where=self.where)
+            where = self.s.read_coordinates(where=self.where, start=self.start,
+                                            stop=self.stop)
         else:
             where = self.where
 
diff --git a/pandas/tests/io/test_pytables.py b/pandas/tests/io/test_pytables.py
index 873bb20b3bba9..ed9ab2aeb2a33 100644
--- a/pandas/tests/io/test_pytables.py
+++ b/pandas/tests/io/test_pytables.py
@@ -4186,6 +4186,20 @@ def test_start_stop_table(self):
             expected = df.loc[30:40, ['A']]
             tm.assert_frame_equal(result, expected)
 
+    def test_start_stop_multiple(self):
+
+        with ensure_clean_store(self.path) as store:
+
+            df = DataFrame({"foo": [1, 2], "bar": [1, 2]})
+
+            store.append_to_multiple({'selector': ['foo'], 'data': None}, df,
+                                     selector='selector')
+            result = store.select_as_multiple(['selector', 'data'],
+                                              selector='selector', start=0,
+                                              stop=1)
+            expected = df[['foo', 'bar']].iloc[[0]]
+            tm.assert_frame_equal(result, expected)
+
     def test_start_stop_fixed(self):
 
         with ensure_clean_store(self.path) as store:


Code review comments:
diff_hunk: @@ -82,6 +82,7 @@ Indexing
 I/O
 ^^^
 
+- Bug in ``HDFStore.select_as_multiple()`` where start/stop arguments were not respected (:issue:`16209`)
path: doc/source/whatsnew/v0.21.0.txt
text: @user1:
you can put this in 0.20.2

@user1:
can you move this

diff_hunk: @@ -4186,6 +4186,20 @@ def test_start_stop_table(self):
             expected = df.loc[30:40, ['A']]
             tm.assert_frame_equal(result, expected)
 
+    def test_start_stop_multiple(self):
+
+        with ensure_clean_store(self.path) as store:
+
+            df = DataFrame({"foo": [1, 2], "bar": [1, 2]})
+
+            store.append_to_multiple({'selector': ['foo'], 'data': None}, df,
+                                     selector='selector')
+            result = store.select_as_multiple(['selector', 'data'],
+                                              selector='selector', start=0,
+                                              stop=1)
+            expected = df[['foo', 'bar']].iloc[[0]]
path: pandas/tests/io/test_pytables.py
text: @user1:
``df.loc[[0], ['foo', 'bar']]`` is more idiomatic