# generate some data
np.random.seed(2)
xdata = np.random.normal(3.0, 1.0, 100)
ydata = np.random.normal(50.0, 30.0, 100) / xdata


plt.plot(xdata, ydata, 'ko',ms=2);

# lets fit to a polynomial function of degree 8
# and plot all together
f = np.poly1d( np.polyfit(xdata, ydata, 8) )
x = np.linspace(np.min(xdata), np.max(xdata), 100)
plt.plot(xdata, ydata, 'ko', ms=2)
plt.plot(x,f(x), 'red');

# compute r**2
from sklearn.metrics import r2_score
r2_score(ydata, f(xdata))

"""
Explanation: <H1>Polynomial fit</H1>
End of explanation
"""


# find the best polynomial
mypoly = dict()
for n in range(1, 10):
    f = np.poly1d( np.polyfit(xdata, ydata, n) )
    mypoly[n] = r2_score(ydata, f(xdata))
    print 'Pol. deg %d -> %f' %(n, mypoly[n])

"""
Explanation: The r2_score is not very good and the large degree of the polynomial suggest an overfitting. The r2_score alone
cannot not say which fitting is the best.
End of explanation
"""


#we'll select 80% of the data to train
xtrain = xdata[:80]
ytrain = ydata[:80]

xtest = xdata[80:]
ytest = ydata[80:]
print(len(xtrain), len(xtest))
plt.plot(xtrain, ytrain, 'ro', ms=2)
plt.xlim(0,7), plt.ylim(0,200)
plt.title('Train');

plt.plot(xtest, ytest, 'bo', ms=2)
plt.xlim(0,7), plt.ylim(0,200)
plt.title('Test');

f = np.poly1d(np.polyfit(xtrain, ytrain, 8))

plt.plot(xtrain, ytrain, 'ko', ms=2)
plt.plot(x, f(x),'r')
plt.title('Train');

plt.plot(xtest, ytest, 'ko', ms=2)
plt.plot(x, f(x),'r')
plt.title('Test');

r2_score(ytrain, f(xtrain)), r2_score(ytest, f(xtest))

"""
Explanation: <H2>Trial and test method</H2>
To avoid overfitting, we'll split the data in two - 80% of it will be used for "training" our model, and the other 20% for testing it.
End of explanation
"""


# let's compute train and test for all polynomial
# find the best polynomial
r2_test, r2_train = list(), list()
polydeg = range(1,15)
for n in polydeg:
    f = np.poly1d( np.polyfit(xtrain, ytrain, n) )
    r2train = r2_score(ytrain, f(xtrain))
    r2_train.append(r2train)
    r2test =  r2_score(ytest, f(xtest))
    r2_test.append(r2test)
    print 'Pol. deg %2d -> r2(train) = %2.4f, r2(test) = %2.4f' %(n,r2train, r2test)
    



"""
Explanation: the r2_score value of the test value is telling us that this fit is not very good
End of explanation
"""


plt.plot(polydeg, r2_train, color='gray')
plt.bar(polydeg, r2_test, color='red', alpha=.4)
plt.xlim(1, 15);

# the best is to fit with a polynomial of degree 6
f = np.poly1d(np.polyfit(xdata,ydata,6))

plt.plot(xdata,ydata, 'ko', ms=2)
plt.plot(x,f(x),'red');

"""
Explanation: Looking at the r2_scores of the test value, we can resolve that a fitting with  a polynomial degree of six is the best
End of explanation
"""


