Repository: pandas-dev/pandas

Issue:
read_csv with iterator=True and engine = "python" -> error in  `if  self.usecols` 
#### Code Sample, a copy-pastable example if possible

Following example returns error if run with `engine = "python"`:

```
  ite = pd.read_csv(infile, sep = '\t', index_col = False, 
              # dtype=pd.np.float32, na_filter=False,low_memory=False,
                usecols = pd.np.arange(nindexcolumns, ncol), 
                iterator = True,   chunksize = chunksize  , engine = "python" )
 print(list(ite)[0])
```
#### Error

```
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-6-e39439e21b50> in <module>()
     17                     iterator = True,   chunksize = chunksize  , engine = "python" )
     18 
---> 19 print(list(ite)[0])

/home/.usr/py3/lib/pandas-0.18.0rc1+80.g820e110.dirty-py3.3-linux-x86_64.egg/pandas/io/parsers.py in __next__(self)
    739 
    740     def __next__(self):
--> 741         return self.get_chunk()
    742 
    743     def _make_engine(self, engine='c'):

/home/.usr/py3/lib/pandas-0.18.0rc1+80.g820e110.dirty-py3.3-linux-x86_64.egg/pandas/io/parsers.py in get_chunk(self, size)
    780         if size is None:
    781             size = self.chunksize
--> 782         return self.read(nrows=size)
    783 
    784 

/home/.usr/py3/lib/pandas-0.18.0rc1+80.g820e110.dirty-py3.3-linux-x86_64.egg/pandas/io/parsers.py in read(self, nrows)
    759                 raise ValueError('skip_footer not supported for iteration')
    760 
--> 761         ret = self._engine.read(nrows)
    762 
    763         if self.options.get('as_recarray'):

/home/.usr/py3/lib/pandas-0.18.0rc1+80.g820e110.dirty-py3.3-linux-x86_64.egg/pandas/io/parsers.py in read(self, rows)
   1617             content = content[1:]
   1618 
-> 1619         alldata = self._rows_to_cols(content)
   1620         data = self._exclude_implicit_index(alldata)
   1621 

/home/.usr/py3/lib/pandas-0.18.0rc1+80.g820e110.dirty-py3.3-linux-x86_64.egg/pandas/io/parsers.py in _rows_to_cols(self, content)
   1997             raise ValueError(msg)
   1998 
-> 1999         if self.usecols:
   2000             if self._implicit_index:
   2001                 zipped_content = [

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
```
#### output of `pd.show_versions()`
## INSTALLED VERSIONS

commit: None
python: 3.3.2.final.0
python-bits: 64
OS: Linux
OS-release: 2.6.32-573.12.1.el6.x86_64
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_US.UTF-8

pandas: 0.18.0rc1+80.g820e110.dirty
nose: 1.3.0
pip: 8.0.2
setuptools: 0.9.8
Cython: 0.23.4
numpy: 1.10.4
scipy: 0.12.1
statsmodels: None
xarray: None
IPython: 4.1.0-dev
sphinx: None
patsy: None
dateutil: 2.4.2
pytz: 2015.7
blosc: None
bottleneck: None
tables: 3.2.2
numexpr: 2.4.7.dev0
matplotlib: 1.5.1
openpyxl: None
xlrd: None
xlwt: None
xlsxwriter: None
lxml: None
bs4: None
html5lib: None
httplib2: None
apiclient: None
sqlalchemy: None
pymysql: None
psycopg2: None
jinja2: 2.8
boto: None



Discussion:
can you show using supplied data and `StringIO`,  e.g. a copy-pastable example

The cause if the problem is, as far as I know, that **use_cols** takes a python's list as a value, and the semantics of `if self.use_cols:` in this case is to check for **emptiness**. Now in the supplied scant example the user passes a **numpy.array** instead, which has different (ambiguous) semantics when put as the condition of an **if** statement, and it definitely does not check for emptiness. So, passing anything but a list is **incorrect usage** according to the current code. However, in the [documentation](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.read_csv.html) for **read_csv** it states

> usecols : array-like, default None

Which actually make this use case plausible and correct according to docs.

I think we should either change the documentation to **list** and add a strict type check, or inject something in the lines of

``` python
use_cols = np.asarray(use_cols, dtype=int).ravel().tolist()
```

Into **_parser_f** of [read_csv](https://github.com/pydata/pandas/blob/master/pandas/io/parsers.py#L480)

Here's how we can reproduce this:

``` python
>>> from pandas import read_csv
>>> from pandas.compat import StringIO
>>> import numpy as np
>>> usecols = np.array(['a', 'b'])
>>> data = 'a,b,c\n1,2,3'
>>> read_csv(StringIO(data), usecols=usecols, engine='python')
...
ValueError: The truth value of an array with more than one element is ambiguous
. Use a.any() or a.all()
```

Note that the C engine is perfectly happy.


Resolving patch:
diff --git a/pandas/io/parsers.py b/pandas/io/parsers.py
--- a/pandas/io/parsers.py
+++ b/pandas/io/parsers.py
@@ -972,6 +972,7 @@ def _validate_usecols_arg(usecols):
                                  'string', 'unicode'):
             raise ValueError(msg)
 
+        return set(usecols)
     return usecols
 
 


Test patch:
diff --git a/pandas/io/tests/parser/usecols.py b/pandas/io/tests/parser/usecols.py
--- a/pandas/io/tests/parser/usecols.py
+++ b/pandas/io/tests/parser/usecols.py
@@ -8,6 +8,7 @@
 from datetime import datetime
 import nose
 
+import numpy as np
 import pandas.util.testing as tm
 
 from pandas import DataFrame
@@ -361,3 +362,12 @@ def test_empty_usecols(self):
         expected = DataFrame()
         result = self.read_csv(StringIO(data), usecols=set([]))
         tm.assert_frame_equal(result, expected)
+
+    def test_np_array_usecols(self):
+        # See gh-12546
+        data = 'a,b,c\n1,2,3'
+        usecols = np.array(['a', 'b'])
+
+        expected = DataFrame([[1, 2]], columns=usecols)
+        result = self.read_csv(StringIO(data), usecols=usecols)
+        tm.assert_frame_equal(result, expected)