DOC:Remove hard-coded examples from _flex_doc_SERIES (#24589)
State: closed

Initial work on #24589 

- Removes hard-coded examples from _flex_doc_SERIES
- Adds separate examples for each op (_*_example_SERIES). At this stage I've just copied the examples which were in the _flex_doc_SERIES template (add, sub, mul, div)
- Modifies _make_flex_doc to format the _flex_doc_SERIES template with an example string from op_desc['series_examples']
- adds references to each of the examples to _op_descriptions['series_examples'], allowing them to be picked up in _make_flex_doc
 
Ops outside not in [add, sub, mul, div] will return their docstring with no examples in this revision.


Labels:
name: Docs

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

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

```diff
@@            Coverage Diff             @@
##           master   #25524      +/-   ##
==========================================
+ Coverage   91.75%   91.76%   +<.01%     
==========================================
  Files         173      173              
  Lines       52960    52964       +4     
==========================================
+ Hits        48595    48600       +5     
+ Misses       4365     4364       -1
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `90.33% <100%> (ø)` | :arrow_up: |
| #single | `41.71% <100%> (-0.01%)` | :arrow_down: |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/25524?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/core/ops.py](https://codecov.io/gh/pandas-dev/pandas/pull/25524/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvb3BzLnB5) | `91.8% <100%> (+0.04%)` | :arrow_up: |
| [pandas/util/testing.py](https://codecov.io/gh/pandas-dev/pandas/pull/25524/diff?src=pr&el=tree#diff-cGFuZGFzL3V0aWwvdGVzdGluZy5weQ==) | `87.66% <0%> (+0.09%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/25524?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/25524?src=pr&el=footer). Last update [42b4c97...2f6173c](https://codecov.io/gh/pandas-dev/pandas/pull/25524?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

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

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

```diff
@@            Coverage Diff            @@
##           master   #25524     +/-   ##
=========================================
- Coverage   91.75%   91.26%   -0.5%     
=========================================
  Files         173      173             
  Lines       52960    52970     +10     
=========================================
- Hits        48595    48341    -254     
- Misses       4365     4629    +264
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `89.83% <100%> (-0.5%)` | :arrow_down: |
| #single | `41.72% <100%> (+0.01%)` | :arrow_up: |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/25524?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/core/ops.py](https://codecov.io/gh/pandas-dev/pandas/pull/25524/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvb3BzLnB5) | `91.74% <100%> (-0.03%)` | :arrow_down: |
| [pandas/core/panel.py](https://codecov.io/gh/pandas-dev/pandas/pull/25524/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvcGFuZWwucHk=) | `38.56% <0%> (-33.19%)` | :arrow_down: |
| [pandas/core/sparse/series.py](https://codecov.io/gh/pandas-dev/pandas/pull/25524/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvc3BhcnNlL3Nlcmllcy5weQ==) | `93.3% <0%> (-2.24%)` | :arrow_down: |
| [pandas/core/indexing.py](https://codecov.io/gh/pandas-dev/pandas/pull/25524/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvaW5kZXhpbmcucHk=) | `90.88% <0%> (-1.41%)` | :arrow_down: |
| [pandas/core/internals/managers.py](https://codecov.io/gh/pandas-dev/pandas/pull/25524/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvaW50ZXJuYWxzL21hbmFnZXJzLnB5) | `93.92% <0%> (-0.92%)` | :arrow_down: |
| [pandas/core/generic.py](https://codecov.io/gh/pandas-dev/pandas/pull/25524/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZ2VuZXJpYy5weQ==) | `93.64% <0%> (-0.53%)` | :arrow_down: |
| [pandas/core/frame.py](https://codecov.io/gh/pandas-dev/pandas/pull/25524/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZnJhbWUucHk=) | `96.79% <0%> (-0.06%)` | :arrow_down: |
| [pandas/core/series.py](https://codecov.io/gh/pandas-dev/pandas/pull/25524/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvc2VyaWVzLnB5) | `93.68% <0%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/25524?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/25524?src=pr&el=footer). Last update [42b4c97...6d7ed91](https://codecov.io/gh/pandas-dev/pandas/pull/25524?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

Thanks for working on this. I think it's OK that other ops like mod (temporarily) lack examples.

Two questions

1. Maybe add a `fill_value` for Series.multiply
2. How to test this? It'd be nice if we could run the doctests, but I don't know how easy that is due to how we define these methods. Do you have any thoughts?
Hello @danielplawrence! Thanks for updating this PR. We checked the lines you've touched for [PEP 8](https://www.python.org/dev/peps/pep-0008) issues, and found:



There are currently no PEP 8 issues detected in this Pull Request. Cheers! :beers: 

##### Comment last updated at 2019-03-05 21:10:44 UTC
Brief update:
- I've gone through and added examples for several other ops. We now have:
    - add
    - sub
    - mul
    - mod
    - pow
    - truediv
    - floordiv
- These are all just examples with the same input arrays of 1s and NaN which were in the previous docstring.
- Added fill_value=0 to each example as suggested above.
- Didn't do divmod as I couldn't get it to work. divmod() works fine, but Series.divmod() fails with a ValueError, see example below.
<details>
<summary>Series.divmod examples</summary>
<pre>
>>> a = pd.Series([1, 1, 1, np.nan], index=['a', 'b', 'c', 'd'])
>>> b = pd.Series([1, np.nan, 1, np.nan], index=['a', 'b', 'd', 'e'])
>>> divmod(a,b)
(a    1.0
b    NaN
c    NaN
d    NaN
e    NaN
dtype: float64, a    0.0
b    NaN
c    NaN
d    NaN
e    NaN
dtype: float64)
>>> a.divmod(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/danlaw/Projects/pandas/pandas/core/ops.py", line 2046, in flex_wrapper
    return self._binop(other, op, level=level, fill_value=fill_value)
  File "/Users/danlaw/Projects/pandas/pandas/core/series.py", line 2522, in _binop
    result = self._constructor(result, index=new_index, name=name)
  File "/Users/danlaw/Projects/pandas/pandas/core/series.py", line 250, in __init__
    .format(val=len(data), ind=len(index)))
ValueError: Length of passed values is 2, index implies 5
</pre>
</details>

- Checked the doctests with validate_docstrings.py, these are validating correctly and fail if I introduce incorrect examples. 
- Doctests raised several errors: (full output attached below)
    - Complained about the spaces introduced by using a format string at the end of the template. I've fixed this so that _make_flex_doc() simply concatenates the base doc with the examples -- this removes trailing whitespace from the cases with no examples.
    - These errors:
          **a)	Parameters {axis} not documented**
*It seems that Series ops can take a parameter which raises 'No axis named a for object type <class 'type'>' for values other than 0. Should this parameter be removed from Series? Doesn't seem to make sense to document it unless I'm missing something. I've added an example at the bottom of this comment.*
	  **b)   Parameter "other" has no description**  
*Appears to be a false positive*
	  **c) Missing description for See Also "Series.rpow" reference**
*False positive?*

- I see that Azure piplines threw a linting failure ([link](https://dev.azure.com/pandas-dev/pandas/_build/results?buildId=8995&view=logs)), I'll look into it ASAP

<details>
  <summary>Full output of validate_docstrings.py</summary>
<pre>
(pandas-dev) 8c8590165d94% for op in add sub mul mod pow truediv floordiv
do
python3 scripts/validate_docstrings.py pandas.Series.$op
done

################################################################################
######################## Docstring (pandas.Series.add)  ########################
################################################################################

Return Addition of series and other, element-wise (binary operator `add`).

Equivalent to ``series + other``, but with support to substitute a fill_value for
missing data in one of the inputs.

Parameters
----------
other : Series or scalar value
fill_value : None or float value, default None (NaN)
    Fill existing missing (NaN) values, and any new element needed for
    successful Series alignment, with this value before computation.
    If data in both corresponding Series locations is missing
    the result will be missing.
level : int or name
    Broadcast across a level, matching Index values on the
    passed MultiIndex level.

Returns
-------
Series
    The result of the operation.

See Also
--------
Series.radd

Examples
--------
>>> a = pd.Series([1, 1, 1, np.nan], index=['a', 'b', 'c', 'd'])
>>> a
a    1.0
b    1.0
c    1.0
d    NaN
dtype: float64
>>> b = pd.Series([1, np.nan, 1, np.nan], index=['a', 'b', 'd', 'e'])
>>> b
a    1.0
b    NaN
d    1.0
e    NaN
dtype: float64
>>> a.add(b, fill_value=0)
a    2.0
b    1.0
c    1.0
d    1.0
e    NaN
dtype: float64

################################################################################
################################## Validation ##################################
################################################################################

3 Errors found:
	Parameters {axis} not documented
	Parameter "other" has no description
	Missing description for See Also "Series.radd" reference

################################################################################
######################## Docstring (pandas.Series.sub)  ########################
################################################################################

Return Subtraction of series and other, element-wise (binary operator `sub`).

Equivalent to ``series - other``, but with support to substitute a fill_value for
missing data in one of the inputs.

Parameters
----------
other : Series or scalar value
fill_value : None or float value, default None (NaN)
    Fill existing missing (NaN) values, and any new element needed for
    successful Series alignment, with this value before computation.
    If data in both corresponding Series locations is missing
    the result will be missing.
level : int or name
    Broadcast across a level, matching Index values on the
    passed MultiIndex level.

Returns
-------
Series
    The result of the operation.

See Also
--------
Series.rsub

Examples
--------
>>> a = pd.Series([1, 1, 1, np.nan], index=['a', 'b', 'c', 'd'])
>>> a
a    1.0
b    1.0
c    1.0
d    NaN
dtype: float64
>>> b = pd.Series([1, np.nan, 1, np.nan], index=['a', 'b', 'd', 'e'])
>>> b
a    1.0
b    NaN
d    1.0
e    NaN
dtype: float64
>>> a.subtract(b, fill_value=0)
a    0.0
b    1.0
c    1.0
d   -1.0
e    NaN
dtype: float64

################################################################################
################################## Validation ##################################
################################################################################

3 Errors found:
	Parameters {axis} not documented
	Parameter "other" has no description
	Missing description for See Also "Series.rsub" reference

################################################################################
######################## Docstring (pandas.Series.mul)  ########################
################################################################################

Return Multiplication of series and other, element-wise (binary operator `mul`).

Equivalent to ``series * other``, but with support to substitute a fill_value for
missing data in one of the inputs.

Parameters
----------
other : Series or scalar value
fill_value : None or float value, default None (NaN)
    Fill existing missing (NaN) values, and any new element needed for
    successful Series alignment, with this value before computation.
    If data in both corresponding Series locations is missing
    the result will be missing.
level : int or name
    Broadcast across a level, matching Index values on the
    passed MultiIndex level.

Returns
-------
Series
    The result of the operation.

See Also
--------
Series.rmul

Examples
--------
>>> a = pd.Series([1, 1, 1, np.nan], index=['a', 'b', 'c', 'd'])
>>> a
a    1.0
b    1.0
c    1.0
d    NaN
dtype: float64
>>> b = pd.Series([1, np.nan, 1, np.nan], index=['a', 'b', 'd', 'e'])
>>> b
a    1.0
b    NaN
d    1.0
e    NaN
dtype: float64
>>> a.multiply(b, fill_value=0)
a    1.0
b    0.0
c    0.0
d    0.0
e    NaN
dtype: float64

################################################################################
################################## Validation ##################################
################################################################################

3 Errors found:
	Parameters {axis} not documented
	Parameter "other" has no description
	Missing description for See Also "Series.rmul" reference

################################################################################
######################## Docstring (pandas.Series.mod)  ########################
################################################################################

Return Modulo of series and other, element-wise (binary operator `mod`).

Equivalent to ``series % other``, but with support to substitute a fill_value for
missing data in one of the inputs.

Parameters
----------
other : Series or scalar value
fill_value : None or float value, default None (NaN)
    Fill existing missing (NaN) values, and any new element needed for
    successful Series alignment, with this value before computation.
    If data in both corresponding Series locations is missing
    the result will be missing.
level : int or name
    Broadcast across a level, matching Index values on the
    passed MultiIndex level.

Returns
-------
Series
    The result of the operation.

See Also
--------
Series.rmod

Examples
--------
>>> a = pd.Series([1, 1, 1, np.nan], index=['a', 'b', 'c', 'd'])
>>> a
a    1.0
b    1.0
c    1.0
d    NaN
dtype: float64
>>> b = pd.Series([1, np.nan, 1, np.nan], index=['a', 'b', 'd', 'e'])
>>> b
a    1.0
b    NaN
d    1.0
e    NaN
dtype: float64
>>> a.mod(b, fill_value=0)
a    0.0
b    NaN
c    NaN
d    0.0
e    NaN
dtype: float64

################################################################################
################################## Validation ##################################
################################################################################

3 Errors found:
	Parameters {axis} not documented
	Parameter "other" has no description
	Missing description for See Also "Series.rmod" reference

################################################################################
######################## Docstring (pandas.Series.pow)  ########################
################################################################################

Return Exponential power of series and other, element-wise (binary operator `pow`).

Equivalent to ``series ** other``, but with support to substitute a fill_value for
missing data in one of the inputs.

Parameters
----------
other : Series or scalar value
fill_value : None or float value, default None (NaN)
    Fill existing missing (NaN) values, and any new element needed for
    successful Series alignment, with this value before computation.
    If data in both corresponding Series locations is missing
    the result will be missing.
level : int or name
    Broadcast across a level, matching Index values on the
    passed MultiIndex level.

Returns
-------
Series
    The result of the operation.

See Also
--------
Series.rpow

Examples
--------
>>> a = pd.Series([1, 1, 1, np.nan], index=['a', 'b', 'c', 'd'])
>>> a
a    1.0
b    1.0
c    1.0
d    NaN
dtype: float64
>>> b = pd.Series([1, np.nan, 1, np.nan], index=['a', 'b', 'd', 'e'])
>>> b
a    1.0
b    NaN
d    1.0
e    NaN
dtype: float64
>>> a.pow(b, fill_value=0)
a    1.0
b    1.0
c    1.0
d    0.0
e    NaN
dtype: float64

################################################################################
################################## Validation ##################################
################################################################################

3 Errors found:
	Parameters {axis} not documented
	Parameter "other" has no description
	Missing description for See Also "Series.rpow" reference

################################################################################
###################### Docstring (pandas.Series.truediv)  ######################
################################################################################

Return Floating division of series and other, element-wise (binary operator `truediv`).

Equivalent to ``series / other``, but with support to substitute a fill_value for
missing data in one of the inputs.

Parameters
----------
other : Series or scalar value
fill_value : None or float value, default None (NaN)
    Fill existing missing (NaN) values, and any new element needed for
    successful Series alignment, with this value before computation.
    If data in both corresponding Series locations is missing
    the result will be missing.
level : int or name
    Broadcast across a level, matching Index values on the
    passed MultiIndex level.

Returns
-------
Series
    The result of the operation.

See Also
--------
Series.rtruediv

Examples
--------
>>> a = pd.Series([1, 1, 1, np.nan], index=['a', 'b', 'c', 'd'])
>>> a
a    1.0
b    1.0
c    1.0
d    NaN
dtype: float64
>>> b = pd.Series([1, np.nan, 1, np.nan], index=['a', 'b', 'd', 'e'])
>>> b
a    1.0
b    NaN
d    1.0
e    NaN
dtype: float64
>>> a.divide(b, fill_value=0)
a    1.0
b    inf
c    inf
d    0.0
e    NaN
dtype: float64

################################################################################
################################## Validation ##################################
################################################################################

3 Errors found:
	Parameters {axis} not documented
	Parameter "other" has no description
	Missing description for See Also "Series.rtruediv" reference

################################################################################
###################### Docstring (pandas.Series.floordiv) ######################
################################################################################

Return Integer division of series and other, element-wise (binary operator `floordiv`).

Equivalent to ``series // other``, but with support to substitute a fill_value for
missing data in one of the inputs.

Parameters
----------
other : Series or scalar value
fill_value : None or float value, default None (NaN)
    Fill existing missing (NaN) values, and any new element needed for
    successful Series alignment, with this value before computation.
    If data in both corresponding Series locations is missing
    the result will be missing.
level : int or name
    Broadcast across a level, matching Index values on the
    passed MultiIndex level.

Returns
-------
Series
    The result of the operation.

See Also
--------
Series.rfloordiv

Examples
--------
>>> a = pd.Series([1, 1, 1, np.nan], index=['a', 'b', 'c', 'd'])
>>> a
a    1.0
b    1.0
c    1.0
d    NaN
dtype: float64
>>> b = pd.Series([1, np.nan, 1, np.nan], index=['a', 'b', 'd', 'e'])
>>> b
a    1.0
b    NaN
d    1.0
e    NaN
dtype: float64
>>> a.floordiv(b, fill_value=0)
a    1.0
b    NaN
c    NaN
d    0.0
e    NaN
dtype: float64

################################################################################
################################## Validation ##################################
################################################################################

3 Errors found:
	Parameters {axis} not documented
	Parameter "other" has no description
	Missing description for See Also "Series.rfloordiv" reference
</pre>
</details>
<details><summary>Example ValueError from axis parameter</summary>
<pre>
>>> a = pd.Series([1, 1, 1, np.nan], index=['a', 'b', 'c', 'd'])
>>> b = pd.Series([1, np.nan, 1, np.nan], index=['a', 'b', 'd', 'e'])
>>> a.add(b)
a    2.0
b    NaN
c    NaN
d    NaN
e    NaN
dtype: float64
>>> a.add(b,axis=0)
a    2.0
b    NaN
c    NaN
d    NaN
e    NaN
dtype: float64
>>> a.add(b,axis=1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/danlaw/Projects/pandas/pandas/core/ops.py", line 2044, in flex_wrapper
    self._get_axis_number(axis)
  File "/Users/danlaw/Projects/pandas/pandas/core/generic.py", line 361, in _get_axis_number
    .format(axis, type(cls)))
ValueError: No axis named 1 for object type <class 'type'>
</pre>
</details>
@TomAugspurger 
Thanks @danielplawrence. The divmod failure is being worked on elsewhere, and documenting `axis` is out of scope for this PR.