Repository: pandas-dev/pandas

Issue:
DataFrame.reset_index deletes index, does not all for ints as level arg
#### Code Sample, a copy-pastable example if possible
```python
import pandas as pd
import numpy as np
pd.__version__
```
  u'0.20.1'

```python
data = pd.DataFrame(np.random.randn(4,4), columns=['A', 'B', 'C', 'D']) 
data
```

<div>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>A</th>
      <th>B</th>
      <th>C</th>
      <th>D</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>-0.134549</td>
      <td>2.352525</td>
      <td>0.481132</td>
      <td>-1.919506</td>
    </tr>
    <tr>
      <th>1</th>
      <td>1.980074</td>
      <td>0.720437</td>
      <td>0.410702</td>
      <td>-0.703470</td>
    </tr>
    <tr>
      <th>2</th>
      <td>-3.063166</td>
      <td>-0.781255</td>
      <td>0.270469</td>
      <td>-0.539081</td>
    </tr>
    <tr>
      <th>3</th>
      <td>-1.125265</td>
      <td>0.308374</td>
      <td>-0.166085</td>
      <td>-1.253959</td>
    </tr>
  </tbody>
</table>
</div>

```python
data.set_index(['A'], inplace=True)
data
```
<div>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>B</th>
      <th>C</th>
      <th>D</th>
    </tr>
    <tr>
      <th>A</th>
      <th></th>
      <th></th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>-0.134549</th>
      <td>2.352525</td>
      <td>0.481132</td>
      <td>-1.919506</td>
    </tr>
    <tr>
      <th>1.980074</th>
      <td>0.720437</td>
      <td>0.410702</td>
      <td>-0.703470</td>
    </tr>
    <tr>
      <th>-3.063166</th>
      <td>-0.781255</td>
      <td>0.270469</td>
      <td>-0.539081</td>
    </tr>
    <tr>
      <th>-1.125265</th>
      <td>0.308374</td>
      <td>-0.166085</td>
      <td>-1.253959</td>
    </tr>
  </tbody>
</table>
</div>

```python
data.reset_index(level=['A'], inplace=True)
data
```
<div>

<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>B</th>
      <th>C</th>
      <th>D</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>2.352525</td>
      <td>0.481132</td>
      <td>-1.919506</td>
    </tr>
    <tr>
      <th>1</th>
      <td>0.720437</td>
      <td>0.410702</td>
      <td>-0.703470</td>
    </tr>
    <tr>
      <th>2</th>
      <td>-0.781255</td>
      <td>0.270469</td>
      <td>-0.539081</td>
    </tr>
    <tr>
      <th>3</th>
      <td>0.308374</td>
      <td>-0.166085</td>
      <td>-1.253959</td>
    </tr>
  </tbody>
</table>
</div>


#### Problem description

between v0.19.2 and v0.20.1, the behavior of DataFrame.reset_index changed. 
With a single set index:
* It does not attempt to keep the column (essentially making drop=True always on)
* level=int no longer works (iterables work)

#### Expected Output

v0.19.2 results:

```python
data.reset_index(level=['A'], inplace=True)
data
```

<div>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>A</th>
      <th>B</th>
      <th>C</th>
      <th>D</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>0.100442</td>
      <td>-0.620740</td>
      <td>-2.018020</td>
      <td>1.059871</td>
    </tr>
    <tr>
      <th>1</th>
      <td>-0.530272</td>
      <td>0.402598</td>
      <td>-1.453445</td>
      <td>-0.729623</td>
    </tr>
    <tr>
      <th>2</th>
      <td>-1.040126</td>
      <td>-0.536687</td>
      <td>-1.136123</td>
      <td>-0.748891</td>
    </tr>
    <tr>
      <th>3</th>
      <td>-0.269727</td>
      <td>0.182250</td>
      <td>0.847344</td>
      <td>0.785692</td>
    </tr>
  </tbody>
</table>
</div>


#### Output of ``pd.show_versions()``

<details>

```python
import pandas as pd
import numpy as np
pd.show_versions()
```

    INSTALLED VERSIONS
    ------------------
    commit: None
    python: 2.7.13.final.0
    python-bits: 64
    OS: Darwin
    OS-release: 15.6.0
    machine: x86_64
    processor: i386
    byteorder: little
    LC_ALL: None
    LANG: en_US.UTF-8
    LOCALE: None.None
    
    pandas: 0.19.2
    nose: None
    pip: 9.0.1
    setuptools: 35.0.2
    Cython: None
    numpy: 1.12.1
    scipy: None
    statsmodels: None
    xarray: None
    IPython: 5.3.0
    sphinx: None
    patsy: None
    dateutil: 2.6.0
    pytz: 2017.2
    blosc: None
    bottleneck: None
    tables: None
    numexpr: None
    matplotlib: None
    openpyxl: None
    xlrd: None
    xlwt: None
    xlsxwriter: None
    lxml: None
    bs4: None
    html5lib: 0.999999999
    httplib2: None
    apiclient: None
    sqlalchemy: None
    pymysql: None
    psycopg2: None
    jinja2: 2.9.6
    boto: None
    pandas_datareader: None

</details>


Discussion:

Resolving patch:
diff --git a/pandas/core/frame.py b/pandas/core/frame.py
--- a/pandas/core/frame.py
+++ b/pandas/core/frame.py
@@ -3012,12 +3012,12 @@ def _maybe_casted_values(index, labels=None):
             return values
 
         new_index = _default_index(len(new_obj))
-        if isinstance(self.index, MultiIndex):
-            if level is not None:
-                if not isinstance(level, (tuple, list)):
-                    level = [level]
-                level = [self.index._get_level_number(lev) for lev in level]
-                if len(level) < len(self.index.levels):
+        if level is not None:
+            if not isinstance(level, (tuple, list)):
+                level = [level]
+            level = [self.index._get_level_number(lev) for lev in level]
+            if isinstance(self.index, MultiIndex):
+                if len(level) < self.index.nlevels:
                     new_index = self.index.droplevel(level)
 
         if not drop:
@@ -3033,6 +3033,8 @@ def _maybe_casted_values(index, labels=None):
 
             multi_col = isinstance(self.columns, MultiIndex)
             for i, (lev, lab) in reversed(list(enumerate(to_insert))):
+                if not (level is None or i in level):
+                    continue
                 name = names[i]
                 if multi_col:
                     col_name = (list(name) if isinstance(name, tuple)
@@ -3049,11 +3051,9 @@ def _maybe_casted_values(index, labels=None):
                     missing = self.columns.nlevels - len(name_lst)
                     name_lst += [col_fill] * missing
                     name = tuple(name_lst)
-
                 # to ndarray and maybe infer different dtype
                 level_values = _maybe_casted_values(lev, lab)
-                if level is None or i in level:
-                    new_obj.insert(0, name, level_values)
+                new_obj.insert(0, name, level_values)
 
         new_obj.index = new_index
         if not inplace:


Test patch:
diff --git a/pandas/tests/frame/test_alter_axes.py b/pandas/tests/frame/test_alter_axes.py
--- a/pandas/tests/frame/test_alter_axes.py
+++ b/pandas/tests/frame/test_alter_axes.py
@@ -641,6 +641,43 @@ def test_reset_index(self):
         xp = xp.set_index(['B'], append=True)
         assert_frame_equal(rs, xp, check_names=False)
 
+    def test_reset_index_level(self):
+        df = pd.DataFrame([[1, 2, 3, 4], [5, 6, 7, 8]],
+                          columns=['A', 'B', 'C', 'D'])
+
+        for levels in ['A', 'B'], [0, 1]:
+            # With MultiIndex
+            result = df.set_index(['A', 'B']).reset_index(level=levels[0])
+            tm.assert_frame_equal(result, df.set_index('B'))
+
+            result = df.set_index(['A', 'B']).reset_index(level=levels[:1])
+            tm.assert_frame_equal(result, df.set_index('B'))
+
+            result = df.set_index(['A', 'B']).reset_index(level=levels)
+            tm.assert_frame_equal(result, df)
+
+            result = df.set_index(['A', 'B']).reset_index(level=levels,
+                                                          drop=True)
+            tm.assert_frame_equal(result, df[['C', 'D']])
+
+            # With single-level Index (GH 16263)
+            result = df.set_index('A').reset_index(level=levels[0])
+            tm.assert_frame_equal(result, df)
+
+            result = df.set_index('A').reset_index(level=levels[:1])
+            tm.assert_frame_equal(result, df)
+
+            result = df.set_index(['A']).reset_index(level=levels[0],
+                                                     drop=True)
+            tm.assert_frame_equal(result, df[['B', 'C', 'D']])
+
+        # Missing levels - for both MultiIndex and single-level Index:
+        for idx_lev in ['A', 'B'], ['A']:
+            with tm.assert_raises_regex(KeyError, 'Level E '):
+                df.set_index(idx_lev).reset_index(level=['A', 'E'])
+            with tm.assert_raises_regex(IndexError, 'Too many levels'):
+                df.set_index(idx_lev).reset_index(level=[0, 1, 2])
+
     def test_reset_index_right_dtype(self):
         time = np.arange(0.0, 10, np.sqrt(2) / 2)
         s1 = Series((9.81 * time ** 2) / 2,
diff --git a/pandas/tests/series/test_alter_axes.py b/pandas/tests/series/test_alter_axes.py
--- a/pandas/tests/series/test_alter_axes.py
+++ b/pandas/tests/series/test_alter_axes.py
@@ -141,6 +141,45 @@ def test_reset_index(self):
         tm.assert_index_equal(rs.index, Index(index.get_level_values(1)))
         assert isinstance(rs, Series)
 
+    def test_reset_index_level(self):
+        df = pd.DataFrame([[1, 2, 3], [4, 5, 6]],
+                          columns=['A', 'B', 'C'])
+
+        for levels in ['A', 'B'], [0, 1]:
+            # With MultiIndex
+            s = df.set_index(['A', 'B'])['C']
+
+            result = s.reset_index(level=levels[0])
+            tm.assert_frame_equal(result, df.set_index('B'))
+
+            result = s.reset_index(level=levels[:1])
+            tm.assert_frame_equal(result, df.set_index('B'))
+
+            result = s.reset_index(level=levels)
+            tm.assert_frame_equal(result, df)
+
+            result = df.set_index(['A', 'B']).reset_index(level=levels,
+                                                          drop=True)
+            tm.assert_frame_equal(result, df[['C']])
+
+            with tm.assert_raises_regex(KeyError, 'Level E '):
+                s.reset_index(level=['A', 'E'])
+
+            # With single-level Index
+            s = df.set_index('A')['B']
+
+            result = s.reset_index(level=levels[0])
+            tm.assert_frame_equal(result, df[['A', 'B']])
+
+            result = s.reset_index(level=levels[:1])
+            tm.assert_frame_equal(result, df[['A', 'B']])
+
+            result = s.reset_index(level=levels[0], drop=True)
+            tm.assert_series_equal(result, df['B'])
+
+            with tm.assert_raises_regex(IndexError, 'Too many levels'):
+                s.reset_index(level=[0, 1, 2])
+
     def test_reset_index_range(self):
         # GH 12071
         s = pd.Series(range(2), name='A', dtype='int64')