DEPR: deprecate .add_prefix and .add_suffix
State: closed

- [x ] xref #18262
- [ x] tests added / passed
- [ x] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`
- [ x] whatsnew entry

Deprecate ``.add_prefix`` and ``.add_suffix`` as per #18262.

An issue is that ``.add_prefix`` and ``.add_suffix`` call the like-named methods in ``internals.py::BlockManager``. Should ``BlockManager.add_prefix/add_suffix`` be changed somehow, e.g. have a warning as well?

EDIT: This is my first deprecation PR, so I'll be more than appreciative of criticism, so I understand these better in the future.

EDIT 2: With ``.add_prefix``/``add_postfix`` deprecated, I think it would be good to show in the doc strings for ``.rename`` how to do similar things, so I've changed its doc string a bit.

Labels:
name: Deprecate
description: Functionality to remove in pandas

Comments:
# [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/18347?src=pr&el=h1) Report
> Merging [#18347](https://codecov.io/gh/pandas-dev/pandas/pull/18347?src=pr&el=desc) into [master](https://codecov.io/gh/pandas-dev/pandas/commit/cdebcf374598e9141df7dc23bc75c9528e92c004?src=pr&el=desc) will **decrease** coverage by `0.02%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/pandas-dev/pandas/pull/18347/graphs/tree.svg?height=150&width=650&token=eZ4WkYLtcO&src=pr)](https://codecov.io/gh/pandas-dev/pandas/pull/18347?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master   #18347      +/-   ##
==========================================
- Coverage   91.59%   91.57%   -0.03%     
==========================================
  Files         150      150              
  Lines       48959    48961       +2     
==========================================
- Hits        44843    44835       -8     
- Misses       4116     4126      +10
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `89.93% <100%> (-0.03%)` | :arrow_down: |
| #single | `41.13% <33.33%> (-0.01%)` | :arrow_down: |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/18347?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/core/generic.py](https://codecov.io/gh/pandas-dev/pandas/pull/18347/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZ2VuZXJpYy5weQ==) | `95.92% <100%> (ø)` | :arrow_up: |
| [pandas/plotting/\_converter.py](https://codecov.io/gh/pandas-dev/pandas/pull/18347/diff?src=pr&el=tree#diff-cGFuZGFzL3Bsb3R0aW5nL19jb252ZXJ0ZXIucHk=) | `65.22% <0%> (-1.74%)` | :arrow_down: |
| [pandas/util/testing.py](https://codecov.io/gh/pandas-dev/pandas/pull/18347/diff?src=pr&el=tree#diff-cGFuZGFzL3V0aWwvdGVzdGluZy5weQ==) | `84.9% <0%> (+0.21%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/18347?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/18347?src=pr&el=footer). Last update [cdebcf3...3a5785a](https://codecov.io/gh/pandas-dev/pandas/pull/18347?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

@jorisvandenbossche you had some concerns here?
> @jorisvandenbossche you had some concerns here?

Yes, as I mentioned in the other issue, not sure if we should deprecate this one. Based on stackoverflow questions (and doc page views) this is actually used a bit (in contrast to eg asobject)
> Yes, as I mentioned in the other issue, not sure if we should deprecate this one. Based on stackoverflow questions (and doc page views) this is actually used a bit (in contrast to eg asobject)

where do you see doc page views?
They are used fairly often on stack overflow. I use them. You can see about [20 posts with them this month on stack overflow](https://stackoverflow.com/search?tab=newest&q=%5bpandas%5d%20add_prefix).

That said, personally, I'd like to shrink pandas as much as possible and wouldn't mind seeing them gone.
@jorisvandenbossche: Ok, I can see that pople use them, but they're just so useless. There was at one point a discussion on adding a ``.format`` method to pandas objects, which would solve the same problem as add_prefix/-suffix but be more general. So:

```python
>>> df.columns = df.columns.format("prefix_{}_suffix")
>>> df.columns.format("prefix_{}_suffix", inplace=True)  # equivalent, but inplace
```

This would be much more generally useful than add_prefix/-suffix, and would operate on the relevant object, which IMO is often more readable than methods on the dataframe.

Just an idea, as I could often use such methods and ``add_prefix/-suffix`` are too limiting and ``apply`` feels 'dirty', for a lack of better words.
We actually have this almost already
```
In [13]: i = pd.Index(list('abc'))

In [15]: i.str.cat(['foo'] * 3, sep='_')
Out[15]: Index(['a_foo', 'b_foo', 'c_foo'], dtype='object')
```
wouldn't be a stretch to allow a single element here, e.g.
``i.str.cat('foo')``, or maybe ``i.str.suffix()`` and ``i.str.prefix()``

these are *much* more appealing that a method on Series/DataFrame
> these are much more appealing that a method on Series/DataFrame.

I agree, though two points:
* ``.str`` accessor only works on indices of type ``object`` (prob. specifically ``str``, haven't checked), while ``add_prefix/suffix`` work for all data types. So for e.g. a float Index, you'd still have to do ``i.apply('prefix_{:+.1f}_suffix'.format)``, which would mean different operations for str vs. non-str data types, which isn't good.
* both ``str.cat`` and potential ``str.prefix/suffix`` are/would be quite specific. A ``.format`` method would be more general and would map very well over to people's general Python knowledge, i.e. their knowledge of the Python string formatting language would transfer well over to Pandas, which would be nice.

So, I'd be the most positive to adding a ``str.format`` method, but not ``str.add_prefix/-suffix``. But I'd also like some sanctioned but still flexible way to cast non-string types to string dtype, as ``astype(str)`` is too crude (lacks string formatting options) and using ``.apply`` feels unsanctioned and dirty.
> .str accessor only works on indices of type object (prob. specifically str, haven't checked), while add_prefix/suffix work for all data types. So for e.g. a float Index, you'd still have to do i.apply('prefix_{:+.1f}_suffix'.format), which would mean different operations for str vs. non-str data types, which isn't good.

This is by-definition and on-purpose. string methods should not work on non-strings, full-stop.

We also want composability. So and I think there is an issue about this somewhere, having a generic ``.format()`` method that is basically an ``.astype(str)``, or maybe ``.astype(str, format=....)`` is prob ok (IIRC we had an issue about this somewhere, @jorisvandenbossche ?)
> (IIRC we had an issue about this somewhere, @jorisvandenbossche ?)

I posted an issue #17211 on this.
As I said in the issue some months ago, I am +1 on adding a `.format` method. However, I think that is  more generally usefu and somewhat orthogonal to this one. Eg it does not allow chaining method calls (for that `rename` would still be the alternative)
Hello @topper-123! Thanks for updating the PR.





Cheers ! There are no PEP8 issues in this Pull Request. :beers: 

##### Comment last updated on December 24, 2017 at 10:45 Hours UTC
ok, so I think we need to add a ``.format()`` method before we can deprecate these, @topper-123 interested?
closing, we had a discussion about this, it is a bit premature to do this as these are useful. so alternative is to add a ``suffixes`` arg to ``pd.concat`` (#21791) 
Should this be removed from the checklist in #6581?