import pandas as pd
import numpy as np
import seaborn as sns

sns.set_style('whitegrid')

raw_df = pd.read_excel('https://github.com/chris1610/pbpython/blob/master/data/2018_Sales_Total_v2.xlsx?raw=true')

df = raw_df.groupby(['account number', 'name'])['ext price'].sum().reset_index()

df.head()

"""
Explanation: Binning Data with Pandas cut and qcut
This notebook accompanies the article posted on pbpython.com
End of explanation
"""


df['ext price'].plot(kind='hist')

"""
Explanation: A histogram is an example of binning data and showing the visual representation of the data distribution
Introducing qcut
End of explanation
"""


df['ext price'].describe()

"""
Explanation: Describe shows how data can be cut by percentiles
End of explanation
"""


pd.qcut(df['ext price'], q=4)

"""
Explanation: Here is an example of using qcut
End of explanation
"""


df['quantile_ex_1'] = pd.qcut(df['ext price'], q=4)
df['quantile_ex_2'] = pd.qcut(df['ext price'], q=10, precision=0)

df.head()

"""
Explanation: Assign the results of the values back to the original dataframe
End of explanation
"""


df['quantile_ex_1'].value_counts()

df['quantile_ex_2'].value_counts()

bin_labels_5 = ['Bronze', 'Silver', 'Gold', 'Platinum', 'Diamond']
df['quantile_ex_3'] = pd.qcut(df['ext price'],
                              q=[0, .2, .4, .6, .8, 1],
                              labels=bin_labels_5)
df.head()

df['quantile_ex_3'].value_counts()

results, bin_edges = pd.qcut(df['ext price'],
                             q=[0, .2, .4, .6, .8, 1],
                             labels=bin_labels_5,
                             retbins=True)

results_table = pd.DataFrame(zip(bin_edges, bin_labels_5),
                             columns=['Threshold', 'Tier'])

results_table

df.describe(include='category')

"""
Explanation: Look at the distribution
End of explanation
"""


df.describe(percentiles=[0, 1/3, 2/3, 1])

"""
Explanation: We can pass the percentiles to use to describe
End of explanation
"""


df['quantile_ex_4'] = pd.qcut(df['ext price'],
                            q=[0, .2, .4, .6, .8, 1],
                            labels=False)
df.head()

"""
Explanation: labels=False will return integers for each bin
End of explanation
"""


df = df.drop(columns = ['quantile_ex_1','quantile_ex_2', 'quantile_ex_3', 'quantile_ex_4'])

pd.cut(df['ext price'], bins=4)

pd.cut(df['ext price'], bins=4).value_counts()

cut_labels_4 = ['silver', 'gold', 'platinum', 'diamond']
cut_bins = [0, 70000, 100000, 130000, 200000]
df['cut_ex1'] = pd.cut(df['ext price'], bins=cut_bins, labels=cut_labels_4)

df.head()

"""
Explanation: cut
Remove the added columns to make the examples shorter
End of explanation
"""


np.linspace(0, 200000, 9)

pd.cut(df['ext price'], bins=np.linspace(0, 200000, 9))

"""
Explanation: We can use nump.linspace to define the ranges
End of explanation
"""


np.arange(0, 200000, 10000)

pd.interval_range(start=0, freq=10000, end=200000, closed='left')

interval_range = pd.interval_range(start=0, freq=10000, end=200000)
df['cut_ex2'] = pd.cut(df['ext price'], bins=interval_range, labels=[1,2,3])
df.head()

"""
Explanation: numpy arange is another option
End of explanation
"""


