Repository: pandas-dev/pandas

Issue:
DEPR: DataFrame dropna accepting multiple axes
#### Code Sample, a copy-pastable example if possible

This is the relevant code extract from pandas source:
```python
if isinstance(axis, (tuple, list)):
       result = self
       for ax in axis:
           result = result.dropna(how=how, thresh=thresh, subset=subset, axis=ax)
```

This is the output from the function call:
```python
In [7]: df
Out[7]: 
     A    B   C  D
0  NaN  2.0 NaN  0
1  3.0  4.0 NaN  1
2  NaN  NaN NaN  5

In [8]: df.dropna(axis=[0, 1], subset=['A', 'C'])
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-8-4c4a476386f3> in <module>()
----> 1 df.dropna(axis=[0, 1], subset=['A', 'C'])

~/dev/pandas/pandas/core/frame.py in dropna(self, axis, how, thresh, subset, inplace)
   4265             for ax in axis:
   4266                 result = result.dropna(how=how, thresh=thresh, subset=subset,
-> 4267                                        axis=ax)
   4268         else:
   4269             axis = self._get_axis_number(axis)

~/dev/pandas/pandas/core/frame.py in dropna(self, axis, how, thresh, subset, inplace)
   4276                 check = indices == -1
   4277                 if check.any():
-> 4278                     raise KeyError(list(np.compress(check, subset)))
   4279                 agg_obj = self.take(indices, axis=agg_axis)
   4280 

KeyError: ['A', 'C']
```
#### Problem description

Subset selects columns/rows from the "other" axis. Passing in the same subset of labels to both axis calls does not really make sense. Unless the subset labels are present on both axes, the function will always throw a `KeyError`.

I would expect that subset can take in a list of subsets for each axis, when axis `is_list_like`. If this is agreeable, I'm happy to submit a PR with this change.

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit: bd4332f4bff135d4119291f66e98f76cc5f9a80e
python: 3.6.3.final.0
python-bits: 64
OS: Darwin
OS-release: 17.5.0
machine: x86_64
processor: i386
byteorder: little
LC_ALL: None
LANG: en_US.UTF-8
LOCALE: en_US.UTF-8

pandas: 0.23.0rc2+13.gbd4332f4b
pytest: 3.2.1
pip: 9.0.1
setuptools: 36.5.0.post20170921
Cython: 0.26.1
numpy: 1.13.3
scipy: 0.19.1
pyarrow: None
xarray: None
IPython: 6.1.0
sphinx: 1.6.3
patsy: 0.4.1
dateutil: 2.6.1
pytz: 2017.2
blosc: None
bottleneck: 1.2.1
tables: 3.4.2
numexpr: 2.6.2
feather: None
matplotlib: 2.1.0
openpyxl: 2.4.8
xlrd: 1.1.0
xlwt: 1.2.0
xlsxwriter: 1.0.2
lxml: 4.1.0
bs4: 4.6.0
html5lib: 0.999999999
sqlalchemy: 1.1.13
pymysql: None
psycopg2: None
jinja2: 2.9.6
s3fs: None
fastparquet: None
pandas_gbq: None
pandas_datareader: None

</details>



Discussion:
rather than change this, let's deprecate passing multiple axes, we don't do this for any other pandas functions. 

Resolving patch:
diff --git a/pandas/core/frame.py b/pandas/core/frame.py
--- a/pandas/core/frame.py
+++ b/pandas/core/frame.py
@@ -4168,14 +4168,15 @@ def dropna(self, axis=0, how='any', thresh=None, subset=None,
 
         Parameters
         ----------
-        axis : {0 or 'index', 1 or 'columns'}, or tuple/list thereof
+        axis : {0 or 'index', 1 or 'columns'}, default 0
             Determine if rows or columns which contain missing values are
             removed.
 
             * 0, or 'index' : Drop rows which contain missing values.
             * 1, or 'columns' : Drop columns which contain missing value.
 
-            Pass tuple or list to drop on multiple axes.
+            .. deprecated:: 0.23.0: Pass tuple or list to drop on multiple
+            axes.
         how : {'any', 'all'}, default 'any'
             Determine if row or column is removed from DataFrame, when we have
             at least one NA or all NA.
@@ -4259,6 +4260,11 @@ def dropna(self, axis=0, how='any', thresh=None, subset=None,
         """
         inplace = validate_bool_kwarg(inplace, 'inplace')
         if isinstance(axis, (tuple, list)):
+            # GH20987
+            msg = ("supplying multiple axes to axis is deprecated and "
+                   "will be removed in a future version.")
+            warnings.warn(msg, FutureWarning, stacklevel=2)
+
             result = self
             for ax in axis:
                 result = result.dropna(how=how, thresh=thresh, subset=subset,


Test patch:
diff --git a/pandas/tests/frame/test_missing.py b/pandas/tests/frame/test_missing.py
--- a/pandas/tests/frame/test_missing.py
+++ b/pandas/tests/frame/test_missing.py
@@ -174,8 +174,12 @@ def test_dropna_multiple_axes(self):
                         [np.nan, np.nan, np.nan, np.nan],
                         [7, np.nan, 8, 9]])
         cp = df.copy()
-        result = df.dropna(how='all', axis=[0, 1])
-        result2 = df.dropna(how='all', axis=(0, 1))
+
+        # GH20987
+        with tm.assert_produces_warning(FutureWarning):
+            result = df.dropna(how='all', axis=[0, 1])
+        with tm.assert_produces_warning(FutureWarning):
+            result2 = df.dropna(how='all', axis=(0, 1))
         expected = df.dropna(how='all').dropna(how='all', axis=1)
 
         assert_frame_equal(result, expected)
@@ -183,7 +187,8 @@ def test_dropna_multiple_axes(self):
         assert_frame_equal(df, cp)
 
         inp = df.copy()
-        inp.dropna(how='all', axis=(0, 1), inplace=True)
+        with tm.assert_produces_warning(FutureWarning):
+            inp.dropna(how='all', axis=(0, 1), inplace=True)
         assert_frame_equal(inp, expected)
 
     def test_dropna_tz_aware_datetime(self):