<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Coefficient of Determination (R2)
Step1:  We'll get a perfect R2=1 if actual and predicted values are the same
Step2:  Now if we're a bit off on our predictions R2 will be still pretty high but not a perfect 1.
Step3:  Intuitively it makes sense that we get a worse R2 if the predictions are farther off.
Step4:  Now here's the interesting situation
Step5:  This is because R2 is calculated relative to a hypothetical model that always predicts the mean of the actual values. Obviously we don't have access to the mean of actual values at the time of prediction but we do at evaluation time.
So looking at lines 12-13 in In[2] you can see that
Step6:  Special cases
Step7:  Predicting exactly the same as mean everytime gives us an R2=0
<ASSISTANT_TASK:>
Python Code:
%matplotlib inline
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn
seaborn.set_style('whitegrid')
def r2(actual, predicted):
    if isinstance(actual, list):
        actual = np.array(actual)
    if isinstance(predicted, list):
        predicted = np.array(predicted)
        
    plt.scatter(actual, predicted);
    plt.plot(actual, actual, 'r', alpha=0.5)
    plt.scatter(actual, actual, facecolors='none', edgecolor='r', linestyle='-')
    plt.axhline(y=actual.mean(), ls='dashed')
        
    mean = actual.mean()
    ss_total = ((actual - mean) ** 2).sum()
    ss_residual = ((actual - predicted) ** 2).sum()
    return 1 - ss_residual / ss_total
Explanation: Coefficient of Determination (R2)
End of explanation
actual = list(range(10))
actual
predicted = list(range(10))
r2(actual, predicted)
Explanation: We'll get a perfect R2=1 if actual and predicted values are the same
End of explanation
predicted = list(range(8)) + [9, 10]
predicted
r2(actual, predicted)
Explanation: Now if we're a bit off on our predictions R2 will be still pretty high but not a perfect 1.
End of explanation
predicted = list(range(8)) + [13, 15]
predicted
r2(actual, predicted)
Explanation: Intuitively it makes sense that we get a worse R2 if the predictions are farther off.
End of explanation
predicted = list(range(8)) + [100, 150]
predicted
Explanation: Now here's the interesting situation: R2 can be negative.
End of explanation
r2(actual, predicted)
Explanation: This is because R2 is calculated relative to a hypothetical model that always predicts the mean of the actual values. Obviously we don't have access to the mean of actual values at the time of prediction but we do at evaluation time.
So looking at lines 12-13 in In[2] you can see that:
mean = actual.mean()
ss_total = ((actual - mean) ** 2).sum()
and then R2 is:
ss_residual = ((actual - predicted) ** 2).sum()
R2 = 1 - ss_residual / ss_total
So obviously if we're way off on our predictions we'll be worse than this hypothetical mean model.
End of explanation
actual = [1] * 10
r2(actual, [1] * 10)
r2(actual, [-100] * 10)
actual = list(range(10))
r2(actual, [1] * 10)
Explanation: Special cases
End of explanation
r2(actual, [4.5] * 10)
Explanation: Predicting exactly the same as mean everytime gives us an R2=0
End of explanation
<END_TASK>