ENH: Allow numerical values in groupby to be within a certain tolerance
#### Is your feature request related to a problem?
It is rather simple to group exact values together:
```python
# Example without noise:
import numpy as np
import pandas as pd

# Create input data without noise:
y = np.array([100, 100, 100, 100, 300, 300, 300, 300, 500, 500, 500, 500])
z = np.array([1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2])
time = np.arange(*y.shape)*0.1
df = pd.DataFrame(data=dict(y=y, z=z), index=time)

# Plot the groups:
df.y.groupby(by=df.y).plot(style="o")

```
yields
![image](https://user-images.githubusercontent.com/14371165/101986960-7ec9a200-3c91-11eb-900f-f201defda526.png)

But once the values are a little noisy the default groupby function does not work:

```python
# Example with noise where groupby is successful:
import numpy as np
import pandas as pd

# Create input data with noise:
y = np.array([100, 100, 100, 100, 300, 300, 300, 300, 500, 500, 500, 500])
z = np.array([1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2])
noise = 1 + 0.1*(np.random.rand(*y.shape) - 0.5)
y = y * noise
z = z * noise
time = np.arange(*y.shape)*0.1
df = pd.DataFrame(data=dict(y=y, z=z), index=time)

# Plot the groups:
df.y.groupby(by=df.y).plot(style="o")

```
yields
![image](https://user-images.githubusercontent.com/14371165/101987249-2eebda80-3c93-11eb-9ed1-dcefc5c3f570.png)


#### Describe the solution you'd like
I want to add the optional arguments `atol=0 `and `rtol=0` (similar to `np.isclose`) to groupby. 
Then `df.y.groupby(by=df.y, rtol=0.1).plot(style="o")` would yield the same groups as without any noise.


#### Describe alternatives you've considered
At the moment I have a function that does this. But it is not an elegant 1-liner. And at least I didn't think it was super obvious how to group like this either.

```python
# Example with noise with a workaround:
import numpy as np
import pandas as pd

# Create input data with noise:
y = np.array([100, 100, 100, 100, 300, 300, 300, 300, 500, 500, 500, 500])
z = np.array([1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2])
noise = 1 + 0.1*(np.random.rand(*y.shape) - 0.5)
y = y * noise
z = z * noise
time = np.arange(*y.shape)*0.1
df = pd.DataFrame(data=dict(y=y, z=z), index=time)


def groupby_isclose(series, atol=0, rtol=0):
    # Sort values to make sure values are monotonically increasing:
    s = series.sort_values()

    # Calculate tolerance value:
    tolerance = atol + rtol * s

    # Calculate a monotonically increasing index that increase when the
    # differnce between current and previous value changes:
    by = s.diff().fillna(0).gt(tolerance).cumsum()
    # s_old = s.shift().fillna(s)
    # by = ((s - s_old).abs() > tolerance).cumsum().sort_index()

    return by


# groupby values that are close:
by = groupby_isclose(df.y, rtol=0.1)

# Plot the groups:
df.y.groupby(by=by).plot(style="o")
```
yields
![image](https://user-images.githubusercontent.com/14371165/101987624-94d96180-3c95-11eb-9f30-88d33baaa06e.png)


#### API breaking implications
`groupby_isclose` sorts the values to determine when values changes significantly. Unless there's another way of calculating this it might make the `sort` argument unusable.