# Pandas pop(): Remove and Return a Column

pandas pop is a DataFrame method that removes one column from the frame and returns that column as a Series. It edits the DataFrame in place, so the column is gone from the frame after the call. This article shows the exact syntax, a worked example with real values, and the errors you are likely to hit.

## Quick Answer

- `df.pop(label)` removes the column named `label` from `df` and returns it as a Series.
- The removal happens in place. You do not need to assign the result back to `df`.
- The returned object keeps the column name as its Series `name` and keeps the original index.
- `pop` takes exactly one column label. It does not accept a list of labels.
- If the label is missing, pandas raises a `KeyError`. There is no default argument.

## Syntax

```python
DataFrame.pop(item)
```

| Argument | Required? | Meaning |
|---|---|---|
| `item` | Yes | The column label to remove and return. Must be a single label, not a list. |

The method returns a Series when the label exists. There is no `default` parameter in the public signature, so a missing label raises `KeyError`. If you want a fallback value for a missing label, use `df.pop(label)` inside a `try` block, or check membership with `if label in df.columns` first.

## How It Works

A DataFrame stores its columns in an indexed collection. When you call `pop`, pandas looks up the label in that collection, extracts the underlying data as a Series, and then drops the entry from the collection. The row index is untouched, so the returned Series lines up with the rows of the DataFrame you just modified.

Two consequences follow from this design.

First, `pop` mutates the DataFrame. Any other variable that points to the same DataFrame sees the column disappear too, because it is the same object. If you need the original frame intact, make a copy before popping.

Second, the returned Series is a view or a copy depending on the internal block layout. In practice you should treat it as independent data and not rely on whether writes to it propagate back. If you plan to modify the returned values, assign them to a new variable and work there.

The method is the natural choice when you want to separate one column for a separate task, such as pulling a target variable out of a feature table before modeling. It is faster and clearer than `del df[label]` followed by a separate lookup, because it does both jobs in one call.

## Worked Example

The dataset is a small survey table with an id, an age, and a score for five participants.

| id | age | score |
|---|---|---|
| 101 | 24 | 88 |
| 102 | 31 | 92 |
| 103 | 45 | 75 |
| 104 | 29 | 81 |
| 105 | 38 | 95 |

Start by building the DataFrame with the columns `['id', 'age', 'score']`. Before the pop, the shape is 5 rows by 3 columns.

```python
import pandas as pd

df = pd.DataFrame({
    "id": [101, 102, 103, 104, 105],
    "age": [24, 31, 45, 29, 38],
    "score": [88, 92, 75, 81, 95],
})

scores = df.pop("score")
print(scores)
print(df.columns.tolist())
```

Output:

```
0    88
1    92
2    75
3    81
4    95
Name: score, dtype: int64
['id', 'age']
```

Walk through what happened.

1. The call `df.pop("score")` returns a Series named `score` with dtype `int64`.
2. The returned values are `[88, 92, 75, 81, 95]`, in the original row order.
3. The DataFrame shape changes from 5 rows by 3 columns to 5 rows by 2 columns.
4. The remaining columns are `['id', 'age']`.

You can also summarize the returned Series. Its sum is 431 and its mean is 86.2, computed as

$$
\bar{x} = \frac{431}{5} = 86.2
$$

The row count never changed. Only the column count dropped, which is the signature behavior of `pop` on a DataFrame.

## More Examples

**Pop and keep the frame unchanged.** Copy first when you need the original.

```python
df = pd.DataFrame({"id": [1, 2], "score": [10, 20]})
scores = df.copy().pop("score")
print(df.columns.tolist())
```

**Pop a column with a non-string label.** Labels can be integers or tuples, so the same method works on any column index.

```python
df = pd.DataFrame({0: [1, 2], 1: [3, 4]})
col = df.pop(1)
print(col.tolist())
```

**Pop inside a loop.** Popping several columns one at a time returns each as its own Series.

```python
df = pd.DataFrame({"a": [1], "b": [2], "c": [3]})
first = df.pop("a")
second = df.pop("b")
print(df.columns.tolist())
```

**Pop a column you then reuse as a target.** This is the common modeling pattern.

```python
features = pd.DataFrame({"x1": [1, 2, 3], "x2": [4, 5, 6], "y": [0, 1, 0]})
y = features.pop("y")
print(features.columns.tolist())
```

**Pop on a Series.** A Series also has a `pop` method, but it removes one element by index label and returns a single scalar value, not a Series. Do not confuse the two.

```python
s = pd.Series([10, 20, 30])
value = s.pop(1)
print(value)
```

## Errors and How to Fix Them

**KeyError: 'column_name'.** The label is not in the columns. Check the exact spelling and case, then confirm with `print(df.columns.tolist())`. Column names with trailing spaces are a frequent cause.

**InvalidIndexError when passing a list.** Passing `df.pop(["a", "b"])` fails because `pop` takes one label. Call it once per column, or use `df.drop(columns=["a", "b"])` when you do not need the returned data.

**AttributeError: 'numpy.ndarray' object has no attribute 'pop'.** You called `pop` on an array instead of a DataFrame. Convert with `pd.DataFrame(arr)` or index the array directly.

**Unexpected KeyError after an earlier pop.** If you pop the same column twice, the second call fails because the column is already gone. Track which columns you have removed, or guard each call with a membership check.

**Silent data loss.** `pop` mutates the frame. If you pop a column and never store the return value, the data is discarded. Always assign the result to a variable.

## Common Mistakes

- **Forgetting that pop mutates the DataFrame.** The column disappears from the frame. Fix: call `df.copy().pop(label)` when you need the original intact.
- **Discarding the return value.** `df.pop("score")` with no assignment throws the Series away. Fix: always bind it, as in `scores = df.pop("score")`.
- **Passing a list of columns.** `pop` accepts one label only. Fix: loop over the labels, or use `drop` when you do not need the values back.
- **Confusing DataFrame pop with Series pop.** DataFrame `pop` returns a Series by label. Series `pop` returns a scalar by index label. Fix: check the object type before calling.
- **Assuming pop works on rows.** It removes columns only. Fix: use `df.drop(index=label)` to remove a row.
- **Relying on the returned Series being a view.** Whether it shares memory depends on internal layout. Fix: treat the result as independent data and copy it if you plan to modify it.

## Limitations

`pop` removes exactly one column per call and only by label. It cannot drop rows, cannot drop several columns at once, and cannot drop by position. For those tasks you need `drop`, which returns a new DataFrame and leaves the original alone unless you pass `inplace=True`.

Because `pop` mutates in place, it can surprise you in pipelines where the same DataFrame is reused downstream. It also gives no warning when the popped column was the only one, which leaves an empty DataFrame with the original row index. If you need a record of what was removed, capture the returned Series and its name before moving on.

## Frequently Asked Questions

### Does pandas pop remove the column permanently?

Yes. `pop` removes the column from the DataFrame in place. After the call, the column is no longer in `df.columns`. If you need it back, you must keep the returned Series and reassign it, as in `df["score"] = scores`.

### What does pandas pop return?

It returns the removed column as a pandas Series. The Series keeps the original row index and takes the column label as its `name`. In the worked example, popping `score` returned a Series named `score` with dtype `int64` and values `[88, 92, 75, 81, 95]`.

### Can I pop more than one column at a time?

No. `pop` takes a single label. To remove several columns and keep their data, pop them one at a time in a loop. To remove several columns without keeping the data, use `df.drop(columns=[...])`.

### Can pandas pop remove a row?

No. DataFrame `pop` works on columns only. To remove a row, use `df.drop(index=label)`. To remove a row and get it back, select it with `df.loc[label]` first, then drop it.

### What happens if I pop a column that does not exist?

pandas raises a `KeyError` naming the missing label. There is no default argument to suppress it. Check with `if label in df.columns` before popping, or wrap the call in a `try` block and handle the exception.

## References

This article draws on the standard references listed under Further Reading.

## Further Reading

- [Harris CR, Millman KJ, van der Walt SJ et al. (2020). Array programming with NumPy. Nature](https://doi.org/10.1038/s41586-020-2649-2)
- [McKinney W (2010). Data Structures for Statistical Computing in Python. Proceedings of the Python in Science Conference](https://doi.org/10.25080/majora-92bf1922-00a)
- [The Python Tutorial](https://docs.python.org/3/tutorial/index.html)
- [Wilson G, Bryan J, Cranston K et al. (2017). Good enough practices in scientific computing. PLOS Computational Biology](https://doi.org/10.1371/journal.pcbi.1005510)
- [Virtanen P, Gommers R, Oliphant TE et al. (2020). SciPy 1.0: fundamental algorithms for scientific computing in Python. Nature Methods](https://doi.org/10.1038/s41592-019-0686-2)
- [pandas User Guide](https://pandas.pydata.org/docs/user_guide/index.html)

## Related Articles

- [Python enumerate() Function: Syntax and Examples](/blog/data-analysis/python-enumerate-function)
- [Python map() Function: Syntax and Examples](/blog/data-analysis/python-map-function-syntax-examples)
- [Python For Loop: Syntax, Examples and Common Patterns](/blog/data-analysis/python-for-loop-syntax-examples)
- [NumPy arange: How to Create Arrays of Numbers in Python](/blog/data-analysis/numpy-arange)
- [Python Modulo Operator: How to Use % and divmod](/blog/data-analysis/python-modulo-operator)