Showing posts with label R. Show all posts
Showing posts with label R. Show all posts

Tuesday, 14 January 2020

Loss Function implementation in NN (part 1)

BOMBE - Computer Sci Museum - Bletchley Park
This post is 1 of 2 posts illustrating how a loss function works with a single neuron neural network. Post 2 will add an additional neuron to make things more interesting.

This is a conceptual implementation of neural network, it's a small one that does not use matrices or clever optimisation, but is written to be accessible in terms of programming and mathematics for learning purposes to understand the most atomic part of an NN as well as an atomic part of other loss/error functions. All the R Code is at the end.

Although facets of NN’s are in here, the take away is how a loss function and its partial derivatives works along with impact on the optimisation algorithm. Loss functions are featured in many different machine learning methods and understanding a loss function will help understand a lot of other methods.

Conceptually, think of a neural network as a recursive function – like the Newton Method, but with partial derivatives, all of which must be minimised in terms of the weights with respect to a loss function.  Each pass of the function yields a value by which we increase or decrease weights leading to a smaller loss or cost in terms of the error.

For this demonstration, I will be using a basic NN and loss function to computationally solve an OLS. I won’t make predictions per se - but by solving the weights, we solve the ordinary least square line.

A least squares function is usually found in the following form:
$$Ε· = 𝑏_0 + 𝑏_1π‘₯$$
Analytically, this can be solved easily using the variance and co-variances of x and y. How to do that analytically can be found here

We can also solve this, and more complicated systems, using a single neuron or a sprawling layered network of them.

Components 
A basic neural network, like many optimisation algorithms, is built with the following steps.

1. Initialise 
2. Compute 
3. Measure
4. Adjust
5. Terminate

These steps are general and anecdotal, more advanced algorithms can branch off at any one of the above steps.

Depending on the design, steps 2 to 4 are executed repeatedly until some conclusion is reached.

For our neural network, we are wanting to find b0 and b1 algorithmically for our least square line
$$Ε· = 𝑏_0 + 𝑏_1π‘₯$$

Our starting data set is:
x  y 
1  2
2  1
3  4
4  3

1. Initialize 

We instantiate our weights (W)  - these can be also be considered coefficients, they impact input.
Weight for the x input variable (w1x) = 1
The learning rate (Ξ±) = .01 - this impacts the rate the algorithm tunes the weights.
The bias (b) = 2
The bias weight (w2b) = 1

Because our prediction is a continuous value, we are not making use of a sigmoid function, we want the least square coefficients(b0,b1) which are the equivalent of w1 and w2 in this example. Sigmoid will be posted in part 2.

If we are to visualise the network, it would look something like this:


Except, our activation function - F(X) - is not a logistic function but the product of the data, bias and weights which will become clearer further on. 

2. Compute 

We fetch our first record – (x,y)(1,2) 
x  y 
1  2
2  1
3  4
4  3

We multiply our first x value by the weight w1 to yield w1x: 1x1=1 => w1x=1 
We multiply our bias value b by the bias weight w2 to yield w2b: 1x1=1 => w2b=1 

3. Measure 
We now measure the difference between the actual value of y=2 and the predicted value of this newly instantiated network. 

At present, our network makes predictions using the following formula: 
$$π‘Š_1𝑋 + π‘Š_2𝑏$$  
(πΏπ‘œπ‘œπ‘˜ π‘“π‘Žπ‘šπ‘–π‘™π‘–π‘Žπ‘Ÿ? 𝑏0 + 𝑏1π‘₯) 

If we substitute our data along with the newly instantiated weights into the above formula, our model makes the following prediction: 

$$π‘Š_1𝑋 + π‘Š_2𝑏 => (1)(1) + (1)(2) => 3$$ 

For it to learn, it needs to tune it’s predictions through the weights. This is how we work out by how much: 
$$(\text{𝒓𝒆𝒂𝒍 𝒗𝒂𝒍𝒖𝒆 π‘šπ‘–π‘›π‘’π‘  𝒏𝒏 π’‘π’“π’†π’…π’Šπ’„π’•π’†π’… 𝒗𝒂𝒍𝒖𝒆})^𝟐$$ 
or 
$$(y − Ε·)^𝟐$$
or
$$(𝐲 − (π‘ΎπŸπ‘Ώ + π‘ΎπŸπ’ƒ))^2$$

So if we substitute for our first value (x=1) and our actual value (y=2) into the above, our error is:
$$(y − π‘Š1𝑋 − π‘Š2𝑏)^𝟐 => ((2) − 3)^𝟐 => (−1)^𝟐 => 1$$

Our error is 1 (2-3=-1)

So the question is, by how much must we change W1 and W2 to reduce our error of 1 to something smaller? 

4. Adjust 

This is where we need to formulate our partial derivative of our error function in terms of each of the weights. 

A derivative is the rate of change of a function. Think f(x)=2x. If x is 1, f(1) will be 2. If x is 2, f(2) will be 4, f(3)=6 and so on. Our rate of change, or the derivative of f(x)=2x is 2. 

A partial derivative is similar to the above, but the derivative is now subject to 2 or more variables. We get the derivative of one parameter (say x) while fixing the remaining parameters (say y). We say we get the derivative of function f(x,y) in terms of x – because we’re interested in the rate of change of x alone while y is treated as constant. See the graph below to intuit how the rate of change for x varies at each value of y (hence the fixing)


If we want to find the rate of change for x, we have to hold y in place, because the rate of change for x is potentially different for all values of y.  

Because our error function has other values in it apart from our value w1 (the weight we want to adjust to improve the error rate) we need to get the partial derivative to compute the adjustment. We will do this next.
$$ 𝑬𝒓𝒓𝒐𝒓 = (Ε· − π‘Š_1𝑋 − π‘Š_2𝑏)^𝟐 $$

$${{πœ•πΈπ‘Ÿπ‘Ÿπ‘œπ‘Ÿ}\over{πœ•π‘Š1}} = 2(−𝑿)(Ε· − π‘Š_1𝑋 − π‘Š_2𝑏) $$
$$= −2𝑿(Ε· − π‘Š_1𝑋 − π‘Š_2𝑏)$$

This is our partial derivative and we will use it again to find the weight for the bias.

$${{πœ•πΈπ‘Ÿπ‘Ÿπ‘œπ‘Ÿ}\over{πœ•π‘Š2}} = 2(−b)(Ε· − π‘Š_1𝑋 − π‘Š_2𝑏) $$
$$= −2b(Ε· − π‘Š_1𝑋 − π‘Š_2𝑏)$$

Now we can make our first weight adjustments

$$∇π‘Š1: − 2𝑋(Ε· − π‘Š_1𝑋 − π‘Š_2𝑏) => −2(1)((2) − (1)(1) − (1)(2)) => −2(−1) => 𝟐$$
$$∇π‘Š2: − 2𝑏(Ε· − π‘Š_1𝑋 − π‘Š_2𝑏) => −2(2)((2) − (1)(1) − (1)(2)) => −4(−1) => πŸ’$$

The method above falls under stochastic gradient descent – when you hear gradient descent, you can think partial derivatives 

Now that we have our change, we can adjust W1 and W2  using our adjustments and our learning rate  Ξ± = .01
So our new weights are:

$$W_1 = W_1 - Ξ±∇W_1 => (1) – (.01)(2) = 1-(0.02)=>.98$$
$$W_2 = W_2 - Ξ±∇W_2 => (1) – (.01)(4) = 1-(0.04)=>.96$$

5. Terminate
A neural network will pass in the next set of record values (x,y) and carry out step 2, 3, and 4 with the new weights from above (.98 & .96). Once all the data is used, the process will continue from the first record again until all the epochs are completed. An epoch is a single pass of the entire dataset through step 2 to 4.

Because we’re running 100 epochs, we’re not done and so we go back to step 2 using weights W1 = .98 and W2 = .96

Continuation 

I will do one more record and then provide the read out from an R script: (x,y)=(2,1)

$$∇π‘Š_1: − 2𝑋(Ε· − π‘Š_1𝑋 − π‘Š_2𝑏) => −2(2)((1) − (.98)(2) − (.96)(2)) => −4(−2.880) = 11.52$$
$$∇π‘Š_2: − 2𝑏(Ε· − π‘Š_1𝑋 − π‘Š_2𝑏) => −2(2)((1) − (.98)(2) − (.96)(2)) => −4(−2.880) = 11.52$$

And our new weights are:

$$W_1 = W_1 - Ξ±∇W_1 => (.98) – (.01)(11.52) = .98-(0.115) =>.865$$
$$W_2 = W_2 - Ξ±∇W_2 => (.96) – (.01)(11.52) = .96-(0.115) =>.845$$

Readout from 1-100 epochs 
x: 1 y: 2 epoch: 1   w1 0.98 : w2 0.96 
x: 2 y: 1 epoch: 1   w1 0.86 : w2 0.84 
x: 3 y: 4 epoch: 1   w1 0.85 : w2 0.83 
x: 4 y: 3 epoch: 1   w1 0.68 : w2 0.75 
x: 1 y: 2 epoch: 2   w1 0.68 : w2 0.74
x: 2 y: 1 epoch: 2   w1 0.61 : w2 0.67 
x: 3 y: 4 epoch: 2   w1 0.66 : w2 0.7 
x: 4 y: 3 epoch: 2   w1 0.57 : w2 0.66 
x: 1 y: 2 epoch: 3   w1 0.58 : w2 0.67 
x: 2 y: 1 epoch: 3   w1 0.52 : w2 0.61
x: 3 y: 4 epoch: 3   w1 0.59 : w2 0.66 
x: 4 y: 3 epoch: 3   w1 0.54 : w2 0.63 
x: 1 y: 2 epoch: 4   w1 0.54 : w2 0.64 
x: 2 y: 1 epoch: 4   w1 0.49 : w2 0.58 
x: 3 y: 4 epoch: 4   w1 0.57 : w2 0.64 
x: 4 y: 3 epoch: 4   w1 0.52 : w2 0.62 
..... 
x: 1 y: 2 epoch: 99  w1 0.57 : w2 0.56 
x: 2 y: 1 epoch: 99  w1 0.52 : w2 0.51 
x: 3 y: 4 epoch: 99  w1 0.6  : w2 0.57 
x: 4 y: 3 epoch: 99  w1 0.56 : w2 0.55 

x: 1 y: 2 epoch: 100 w1 0.57 : w2 0.56
x: 2 y: 1 epoch: 100 w1 0.52 : w2 0.51
x: 3 y: 4 epoch: 100 w1 0.6  : w2 0.57 
x: 4 y: 3 epoch: 100 w1 0.56 : w2 0.54 

Our final weights W1 and W2  are .56 and .54 

If I pass the same data independently through a generic least squares model in R to find the gradient (done using R’s lm() for shortcut) I find the following:
Coefficients: 
(Intercept)            x   
        1.0          0.6   
W2 corresponds to our intercept of 1.0 (b0*w2=2*.54~1)
And our W1 (.56) corresponds to our x of .6

Our algorithm increments are visually represented below, and we can see the progression of accuracy per epoch:

If we carry out the same process using 100 data points with 3000 epochs, and a learning rate of .00001 with w1 and w2 initialised to -10 and -20, we will see the following: 

Our starting point is a line with a steep gradient and an intercept around 1 
Incrementally, the intercept shifts and the gradient changes 




By the end of the last epoch, the line is suitably fitted to the data where the data was generated with an intercept of 50 and a gradient of 1.5 – which is basically the same as below. 

> lm(dfXY, formula = y~.) 
Call: lm(formula = y ~ ., data = dfXY) 
Coefficients: 
(Intercept)            x        
49.658        1.566   

A note on learning rate:  Learning rate will allow for the error function to fall into a minima and reach the smallest allowable global error. If the learning rate is too high, the weights will cycle back and forth, continually overshooting the minima and fail to allow an adequate window for the algorithm to converge on the minima. The flip flopping will usually cause weights to spiral out of control and ‘blow up’ – weight or weights hit INF or NANs 

If the coefficients are converged and it can be visually seen that the line settles in a position that is clearly not efficient, it means the algorithm has found a local minima, and not the global minima. This can be corrected using advanced analytic and programming methods 

A note on weights: In the above notes, weights are declared as separate variables and all the moving parts reside in a variable. This is done so that it is easy to follow. However, in real implementations of NNs, the weights per neuron and the input data are stored in matrices and their dot product are used to create the prediction. This is programmatically neater and generally computationally efficient. 

RScript
library(ggplot2)  
setwd("C:\\MyImageFolder")  
#Noise generator 
generatePairs <- function(x,b0,b1,corre) 
{   
  err <- sample(rnorm(100,0,corre),1)   
  y <- (  b0 + b1*x + err  )   
  return(y) 
} 
 
#Holding frame 
dfXY <- data.frame(x=1:100, y=1:100) 
 
#Create a noisy scatter plot with an intercept and a gradient 
for (x in 1:100) 
{   
  dfXY[x,]$y <- generatePairs(x,50,1.5,20) 
} 
 
#Set variables 
learningRate <- .00001 
epoch <- 3000 
b0 <- 2 
w1 <- -10 
w2 <- -20 
 
#Loop through epochs 
for (z in 1:epoch) 
{   
  error <- 0 
 
  #loop through data points   
  for (loopCount in 1:nrow(dfXY)) 
  {     
    #assign variables     
    w1x <- w1*dfXY[loopCount,]$x     
    w2b <- w2*b0 
 
    #partial derivative value     
    pdw1x <- -2*dfXY[loopCount,]$x*(dfXY[loopCount,]$y-w1x-w2b)     
    pdw2b <- -2*b0*(dfXY[loopCount,]$y-w1x-w2b) 
 
    #error     
    error <- error +(dfXY[loopCount,]$y-w1x-w2b)^2 
 
    #Reassign weights     
    w1 <- w1-(learningRate*pdw1x)     
    w2 <- w2-(learningRate*pdw2b)   
  } 
 
  #Create plots at x epochs   
  if (z %% 200 == 0 || z == 1 || z == 2 || z == 3 || z == 4 || z == 5 || z == 10 || z == 15 || z == 20 || z == 30 || z == 50 || z == 100) 
  { 
    print(paste("x:",dfXY[loopCount,]$x,"y:",y,"epoch:",z,"w1x",w1,": w2b",w2,"error:",error)) 
 
    ggplot(data=dfXY,aes(x=dfXY$x,y=dfXY$y))+       
    geom_point()+       
    geom_line(aes(x=1:nrow(dfXY),w2*b0+w1*(1:nrow(dfXY))))+       
    ggtitle(paste("OLS b0 & b1 search - Ξ£Error:",round(error,2),"epoch:",z,"b0:",round(w2*b0,2)," b1:",round(w1,2)))+       
    xlim(c(0,220))+       
    ylim(c(0,220))+       
    ggsave(paste0(z,".jpg"), plot = last_plot(), width = 180, height = 120,limitsize = TRUE, units="mm") 
 
    #For monitoring progression     
    file.copy(paste0(z,".jpg"), "current.jpg", overwrite = TRUE )   
  } 
} 

Monday, 13 January 2020

Newton Method

The Newton method (or Newton-Raphson Method)is an algorithm that allows one to find the roots of an equation when solving the equation (f(x)=0) is not an option.

The general form of the algorithm is here:
$${b_{n} = b_{n-1}- {{f(x)}\over{f'(x)}}}$$

$$end:f(x) < err$$

It is simply an arbitrary starting value less the function of the value over the derivative of the value.The idea is that the result of the first iteration feeds into the subsequent iteration, and the process terminates when f(x) < err threshold.
We start with parameter b0 and an error threshold which remains constant.

We want to find the x intercept of the following function
$$f(x)=-x^3+2x^2-e^x$$



We will need its derivative
$${{df(x)\over{dx}} =-3x^2+4x-e^x}$$

The form of the algorithm will look as follows:
$${b_{1} = b_{0}- {{-x^3+2x^2-e^x}\over{-3x^2+4x-e^x}}}$$

We arbitrarily set b0 as 10 and the error threshold to .001

I will print the values out instead of computing the algorithm with MathJax.

b0: -6.47058884010279  fx: 354.649469421832  fxdx: -151.489463487155
b0: -4.12950543233229  fx: 104.509232053146  fxdx: -67.6925579111767
b0: -2.58562475655285  fx: 30.5816407170309  fxdx: -30.474214161426
b0: -1.58209959409794  fx: 8.76059234819741  fxdx: -14.0430588411127
b0: -0.95826169330173  fx: 2.33291052253774  fxdx: -6.97140224182995
b0: -0.623621624975194 fx: 0.484337035554275 fxdx: -4.19719802152472
b0: -0.508226298600212 fx: 0.046298105996643 fxdx: -3.4093487299717
b0: -0.494646547843232 fx: 0.000591761687825487 fxdx: -3.32239821278166
-0.4946465

The value of x where f(x) = 0 is ~ -.495. This can be seen in the above graph where the blue line intercepts the x axis.

R Script
library(ggplot2)

#Function
f1_x <- function(x) {
  return (-x^3+2*(x^2)-exp(x))
}

#Derivative
f1_dxdy <- function(x) {
  return (-3*(x^2)+(4*x)-exp(x))
}

#Load data
dfFX <- data.frame(x=seq(-2.5,2.5,.01),f1_x=f1_x(seq(-2.5,2.5,.01)),
                         f1_dxdy=f1_dxdy(seq(-2.5,2.5,.01)))

#plot the data
ggplot(data=dfFX) + 
  geom_line(aes(x=x, y=f1_x),size=1,color="blue")  + 
  ylim(c(-2.5,2.5)) +
  xlim(c(-2.5,2.5)) +
  geom_vline(xintercept=0) +
  geom_hline(yintercept=0) 

#The Newton Method - caps N
NewtonM <- function(b0,err,fx=f1_x,fxdx=f1_dxdy) {
  b0 <- b0-(fx(b0)/fxdx(b0))

  print(paste("b0:",b0,"fx:",fx(b0),"fxdx:",fxdx(b0)))
  
  if (abs(f1_x(b0)) <= err) {
    return(b0)
  }
  if (abs(f1_x(b0)) >= err) {
    return(newtonM(b0,err))
  }
}

Geometric Distribution

I've discovered the geometric distribution, it looks very handy.

$$E(X) = {1\over p}$$
$$V(X) = {1-p\over p^2}$$
$$P(Y=y) = {(1-p)}^{y-1}p$$

If I have a cheap laptop with an in built failure rate of 1% percent per month (per factory design),  I can find the probability of it failing in exactly 2 years time. Here, p=.01, and y=24

Therefore,
$$P(Y=24) = {(1-(.01))}^{(24)-1}(.01) = 0.008 $$

If I want to know the chances of it failing before and up until that point, then I need the cumulative sum of P(Y=y) = 1,2,3,4,..,24
A shortcut to get this value is

$$P(Y<=y) = 1-(1-p)^y$$
$$P(Y<=24) = 1-(1-.01)^{24}=0.214$$

In R, I can plot this as follows

library(dplyr)
library(ggplot2)

dfGeom <- data.frame(x = 1:24, y = pgeom(1:24-1,.01))

ggplot(data=dfGeom, aes(x = x, y = y)) + 
  geom_bar(stat = "identity", position = "dodge") +
  geom_text(aes(y = y+.02, label = round(y,2), 
                x = as.numeric(x))) 
 

Meaning, chances of failure approaching year 2 and beyond is ~.2


Saturday, 11 January 2020

Birthday Problem and other R snippets

Birthday problem solver code for R. I need a place to store this for future. I am working through second year statistics and will post other interesting things in this page as time goes on.
Here is a link to the full explanation.
In summary
You need 23 people to have at least a 50/50 chance of having a clashing birthday. See the graph below to see how that dynamic works as n increases.
#[1]: 10 people - number of possible birthday combinations
365^10

#Chances of a single combination of interest occurring
1/365^10

#number of combinations within sample space [1] having a unique birthday
(365)*(364)*(363)*(362)*(361)*(360)*(359)*(358)*(357)*(356)

#probability of no one in the room having the same birthday
distinctTuples <- function(elements,sampleSize) 
{
  n = sampleSize
  n_a <- elements
  
  for (x in 1:(n-1)) {
    n_a <- n_a*(elements-x)
  }
  
  return(n_a)
}

distinctTuples(365,10)

#Wrap the distinct tuples up and subtract from one to figure out the birthday problem
birthdayProblem <- function(numberOfPeople) {
  return (1-(distinctTuples(365,numberOfPeople)/365^numberOfPeople))
}

#Print the results
birthdayResults <- lapply(2:50,birthdayProblem)
plot(x=2:50,y=birthdayResults, type="l",main="Birthday Problem", 
     xlab="Number of people", ylab="Chance")

#Binomial distribution function where n=sample size, p=success chance, y=number of 
#successfull outcomes
calc_binomial <- function(p=1,n=1,y=1) {
  q <- 1-p
  s <- factorial(n)/(factorial(y)*factorial(n-y))
  p <- s * (p^y) * (q^(n-y))
  return (p)
}


plot(x=1:20,y=calc_binomial(.5,20,y=1:20), type="l",
     main="20 Coin flips and count of heads per trial", xlab="Number of flips", 
     ylab="Chance")

#quick factorial
easyFact <- function(n,y) {return (factorial(n)/(factorial(y)*factorial(n-y))) }

Output


Sunday, 26 August 2018

Least Squares Line in R (or on pen and paper)


I like to know how things work. I am not 100% comfortable in reusing plugins, packs or technologies without at least drilling to some depth into the underlying supporting knowledge. I do this so I can maybe understand it's parameters and how the component behaves. Ever wondered how a simple linear regression line in Excel or R works? This post will cover a simpler version of a linear regression model.

Least Squares Method

The Least Squares approach is a basic linear regression method for 2 variables that visually represents their relationship. This model will tell us how strongly 2 variables are coupled and approximately by how much one variable (y) changes in reaction to the amount of change in the other variable (x). Usually, in a variable pair, one variable is considered a dependent (y) while the other is considered independent (x). The method below can be done on paper for small data sets, but usually processing will be done in R or Excel for larger sets.

To build a least square line, I need to have some data. To keep in line with an earlier post, I'll stick to SA crime and economic data. I will try to see if there is a relationship between common burglary and the country’s gross domestic product (GDP). These 2 variables are complex outputs of 2 very broad processes - so don't look too deeply into the findings.

I found this data on tradingeconomics.com and the SAPS statistics resources page and manually plugged it together as shown below.

Year,      x  ,y
2005-01-01,247,25351
2006-01-01,261,25148
2007-01-01,287,22456
2008-01-01,297,20410
2009-01-01,375,19842
2010-01-01,416,18007
2011-01-01,396,15826
2012-01-01,366,15404
2013-01-01,350,15579
2014-01-01,317,17379
2015-01-01,295,18051
2016-01-01,349,17367

x is GDP in billions of dollars. y is the number of burglaries recorded during the financial year.
The data on a scatter plot is below:




R Script
#Load data into string
strCrimeGDP <- "
Year,      x  ,y
2005-01-01,247,25351
2006-01-01,261,25148
2007-01-01,287,22456
2008-01-01,297,20410
2009-01-01,375,19842
2010-01-01,416,18007
2011-01-01,396,15826
2012-01-01,366,15404
2013-01-01,350,15579
2014-01-01,317,17379
2015-01-01,295,18051
2016-01-01,349,17367"  

#Create a dataframe
dfGDPC <- read.delim(textConnection(strCrimeGDP),header=TRUE,sep=",",strip.white=TRUE)
dfGDPC$label <- paste0("(",dfGDPC$x,",",dfGDPC$y,")")

ggplot(data=dfGDPC, aes(x=x, y=y)) +
    geom_point(color='darkblue') +
    geom_text(aes(label=label),hjust=0, vjust=-1) +
    ggtitle(paste0("SA Common Burglary and GDP")) +
    ylab("Common Burglary (y)") +
    xlab("GDP (x)")

Now, I need the mean, variance and standard deviations of both X and Y followed by their covariance.

Mean x
`\bar{x} = \frac{ sum_(i=1)^n x }{n} = \frac{ 3956 }{12} = 329.66`


Mean y
`\bar{y} = \frac{ sum_(i=1)^n y }{n} = \frac{ 230820 }{12} = 19235`


x Variance
`Sx^2 = 1/(n-1)(sum_(i=1)^n x^2 - (sum_(i=1)^n x)^2/n)=1/(12-1)(1335976 - (3956)^2/12) = 2892.24`


x Standard deviation
`Sx = \sqrt{Sx^2} = \sqrt{2892.24} = 53.78`


y Variance
`Sy^2 = 1/(n-1)(sum_(i=1)^n y^2 - (sum_(i=1)^n y)^2/n)=1/(12-1)(4573823818 - (230820)^2/12) = 12181920`


y Standard deviation
`Sy = \sqrt{Sy^2} = \sqrt{ 12181920 } = 3490.261`


Covariance
`Sxy = 1/(n-1)(sum_(i=1)^n xy - ((sum_(i=1)^n x)(sum_(i=1)^n y))/n)=1/(12-1)( 74516510 - ((3956)( 230820))/12) = -143377.3`

With covariance being negative, this tells us that when x increases (GDP), y decreases (Burglaries)
With the values in hand, I substitute these values into the least squares formula:

`\hat{y} = b_0 + b_1x`

where 
`b_1 = \frac{Sxy}{Sx2}`

`b_0 = \bary - b_1\barx`

`x = {sum_(i=1)^n x}/n`


Substitute
`b_1 = frac{Sxy}{Sx^2} = frac{-143377.3}{2892.24} = -49.57`
and
`b_0 = \bary-b_1\barx = 19235 - -49.57(329.66) = 35576.25`

The line is now
`\hat{y} = 35576.25 -49.57\bar{x}`

We can now plug values in for X to find Y and draw a line. I’ve picked `x_1` = 247 and `x_2` = 416. Substituting these into the formula, I get `y_1` = 23332.46 and `y_2` = 14955.13

With the line, the scatter plot now looks like this


R Script

dfPoints <- data.frame(x=c(247,416),y=c(23332.46,14955.13),label=c("(247,23332)","(416,14955)"))
ggplot(data=dfGDPC, aes(x=x, y=y)) + 
    geom_point(color='darkblue') + 
    geom_text(aes(label=label),hjust=0, vjust=-1) +
    geom_point(data=dfPoints, aes(x=x, y=y),color='red',size=2) + 
    geom_text(data=dfPoints, aes(label=label),hjust=0, vjust=-1) +
    geom_line(data=dfPoints,aes(x=x,y=y))
    ggtitle(paste0("SA Common Burglary ~ GDP")) +
    ylab("Common Burglary (y)") +
    xlab("GDP (x)")

Now to tie the whole thing up from beginning to end.

R Script

#Load data into string
strCrimeGDP <- "
Year,      x  ,y
2005-01-01,247,25351
2006-01-01,261,25148
2007-01-01,287,22456
2008-01-01,297,20410
2009-01-01,375,19842
2010-01-01,416,18007
2011-01-01,396,15826
2012-01-01,366,15404
2013-01-01,350,15579
2014-01-01,317,17379
2015-01-01,295,18051
2016-01-01,349,17367"   

#Create a dataframe
dfGDPC <- read.delim(textConnection(strCrimeGDP),header=TRUE,sep=",",strip.white=TRUE)
dfGDPC$label <- paste0("(",dfGDPC$x,",",dfGDPC$y,")")

#Create a dataframe
dfGDPC <- read.delim(textConnection(strCrimeGDP),header=TRUE,sep=",",strip.white=TRUE)
dfGDPC$label <- paste0("(",dfGDPC$x,",",dfGDPC$y,")")

#Compute the mean and standard deviation for X and Y
MeanX <- mean(dfGDPC$x)
MeanY <- mean(dfGDPC$y)
StdX <- sd(dfGDPC$x)
StdY <- sd(dfGDPC$y)

#Compute the variance of X
varX <- sum((dfGDPC$x-MeanX)^2)/(nrow(dfGDPC)-1)

#Compute the covariance of X and Y
covXY <- (1/(nrow(dfGDPC)-1))*(sum(dfGDPC$x*dfGDPC$y) - ( (sum(dfGDPC$x)*sum(dfGDPC$y))/(nrow(dfGDPC))   ))

#Compute the slope of the regression line or m of y = mx + c
slope <- covXY/varX

#Compute the intercept of y = mx + c
intercept <- MeanY-(slope*MeanX)

#Compute the coefficient of correlation between X and Y (r)
CoefCor <- covXY/(StdX*StdY)

#Compute the coefficient of determination between X and Y (r^2)
CoefDet <- CoefCor^2

#Load the graphic
library(ggplot2)

dfPoints <- data.frame(x=c(247,416),y=c(23332.46,14955.13),label=c("(247,23332)","(416,14955)"))

plot <- ggplot(data=dfGDPC, aes(x=x, y=y)) + 
        geom_point(color='darkblue') + 
 geom_text(aes(label=label),hjust=0, vjust=-1) +
 geom_point(data=dfPoints, aes(x=x, y=y),color='red',size=2) + 
 geom_text(data=dfPoints, aes(label=label),hjust=0, vjust=-1) +
 geom_abline(intercept = intercept, slope = slope, color="red", linetype="dashed", size=0.5) + 
 ggtitle(paste0("SA Common Burglary ~ GDP : \u0176 = b0 + b1x\u0304 / Coef Correlation : ", round(CoefCor,2)," / Coeff Determination:",round(CoefDet,2))) +
 ylab("Common Burglary (y)") +
 xlab("GDP (x)")
  
plot



Now, we don’t really have to do so many calculations from the ground up. It can all be done automatically for you in GGPLOT using an inbuilt linear regression model - lm through geom_smooth. Thats right, there is no need to calculate covariance or the slope and intercept. All one has to do is plug the data into GGPLOT and invoke geom_smooth with method=lm

R Script
ggplot(dfGDPC,aes(x,y))+
       geom_point(color='darkblue') +
       geom_text(aes(label=label),hjust=0, vjust=-1) +
       geom_point(data=dfPoints, aes(x=x, y=y),color='red',size=2) +
       geom_text(data=dfPoints, aes(label=label),hjust=0, vjust=-1) +
       geom_smooth(method='lm',formula=y~x) +
       ggtitle(paste0("SA Common Burglary ~ GDP")) +
       ylab("Common Burglary (y)") +
       xlab("GDP (x)")




The linear regression line cuts through the red points that were calculated manually, meaning the calculations are accurate.

So does GDP impact the amount of common burglaries? Possibly, to some degree. Again, these 2 measurements are extracted from 2 very general and very complicated processes. SA is effectively 2 countries in 1 - a large formal and informal sector. Being so, the the relationship could be incidental.

To play devils advocate, the measurement for common burglaries may be subject to a number of issues - it is widely understood that many people don't bother reporting common burglaries so the number of reported incidents may be dwindling in relation to an increasing GDP reinforcing the inverse relationship. In reality, the true number of burglaries could be increasing along with GDP but we won't see it owing to under reporting. As for GDP, these increases might be increasing owing to an increase in government spending - not necessarily job creation, one of the antidotes of unemployment and crime. Government spending makes up ~50% of SA GDP and has been steadily increasing for the last decade using borrowed money. This crowds out the private sector, the sector which creates more sustainable employment opportunities than the public sector. With rampant corruption, these borrowed amounts (pushing up our GDP) will not be efficiently discharged into the economy and will not have the swaying impact one would imagine on unemployment.