PERF: Improved performance for StringMethods.encode/decode
State: closed

Reusing encoding/decoding function for a specified encoding doubles speed.

```
import pandas as pd
s1 = pd.Series(["Dzień добрый"]*100000)
s2 = s1.str.encode('cp1251', 'ignore')
%timeit s1.str.encode('cp1251', 'ignore')
%timeit s2.str.decode('cp1251')

```

10 loops, best of 3: 157 ms per loop --> 80.9 ms per loop
10 loops, best of 3: 179 ms per loop --> 84.1 ms per loop


Labels:
name: Performance
description: Memory or execution speed performance
name: Strings
description: String extension data type and string data

Comments:
## [Current coverage](https://codecov.io/gh/pydata/pandas/pull/12989?src=pr) is **83.84%**

> Merging [#12989](https://codecov.io/gh/pydata/pandas/pull/12989?src=pr) into [master](https://codecov.io/gh/pydata/pandas/branch/master?src=pr) will increase coverage by **+<.01%**

``` diff
@@             master     #12989   diff @@
==========================================
  Files           136        136          
  Lines         49711      49714     +3   
  Methods           0          0          
  Messages          0          0          
  Branches          0          0          
==========================================
+ Hits          41679      41682     +3   
  Misses         8032       8032          
  Partials          0          0          
```

[![Sunburst](https://codecov.io/gh/pydata/pandas/pull/12989/graphs/sunburst.svg?src=pr&size=660)](https://codecov.io/gh/pydata/pandas/pull/12989?src=pr)

> Powered by [Codecov](https://codecov.io?src=pr). Last updated by e9a9c14

pls add an asv benchmark for this

so even better is to do this:

```
In [11]: %timeit s1.astype('category').str.decode('utf-8')
100 loops, best of 3: 4.39 ms per loop
```

This is of course is dependent on the number of uniques, so there is some overhead here if k (nuniques) is about n (len). 

Tested on 3 different encodings with python3.4, this solution is better only in certain cases. I think that's because of some optimizations in CPython:

[7.2.3. Standard Encodings](https://docs.python.org/3/library/codecs.html#standard-encodings)

> CPython implementation detail: Some common encodings can bypass the codecs lookup machinery to improve performance. These optimization opportunities are only recognized by CPython for a limited set of aliases: utf-8, utf8, latin-1, latin1, iso-8859-1, mbcs (Windows only), ascii, utf-16, and utf-32.

Despite of this _encoding_ of `utf-16/32` is faster with `codecs.getencoder`.

```
import pandas as pd
s1 = pd.Series(["йцукенгшщз", "фывапролдж", "ячсмитьбю.", "ъхзщшгнеку", "эждлорпавы",
              "окнсдветур", "ЙЦУКЕнгшщз", "ЭЖДЛОрпавы", "ЯЧСМИтьбю.", "ХЖЩЛГреауы"])
s1 = pd.concat([s1]*100000)
enc = 'utf-16'
s2 = s1.str.encode(enc)
%timeit s1.str.encode(enc)
%timeit s2.str.decode(enc)
enc = 'utf-8'
s2 = s1.str.encode(enc)
%timeit s1.str.encode(enc)
%timeit s2.str.decode(enc)
enc = 'cp1251'
s2 = s1.str.encode(enc)
%timeit s1.str.encode(enc)
%timeit s2.str.decode(enc)

1 loop, best of 3: 1.05 s per loop -> 437 ms (++) utf-16
1 loop, best of 3: 531 ms per loop -> 804 ms (--)
1 loop, best of 3: 454 ms per loop -> 483 ms (-) utf-8
1 loop, best of 3: 565 ms per loop -> 869 ms (--)
1 loop, best of 3: 1.51 s per loop -> 766 ms (++) cp1251
1 loop, best of 3: 1.76 s per loop -> 827 ms (++)
```