10
$\begingroup$

Please allow me to ask a basic question. I understand the mechanics of Naive Bayes for discrete variables, and can redo the calculations "by hand". (code of HouseVotes84 all the way per below).

However - I am struggling to see how the mechanics work for continuous variables (example code per below). How does the package calculate the conditional probabilities [, 1] and [, 2] in the table per below? As any individual X value is unique, does it create a range around each point, and calculate relative frequencies within these ranges (e.g. if the point is +0.311, does it evaluate the incidence of blue and orange spots in e.g. a range of 0.1 and +0.5?) This might be basic question - apologies if so.

Table

A-priori probabilities:
Y
  blue orange 
   0.5    0.5 

Conditional probabilities:
        values
Y              [,1]      [,2]
  blue   0.08703793 0.9238799
  orange 1.33486433 0.9988389

Code

blue=rep("blue",50); orange=rep("orange",50); colour=c(blue,orange); values1=rnorm(50,0,1); values2=rnorm(50,1,1); values=c(values1,values2)
df=data.frame(colour,values)

(model <- naiveBayes(colour ~ ., data = df))
(predict(model, df[1:10,]))
(predict(model, df[1:10,], type = "raw"))
(pred <- predict(model, df))
table(pred, df$colour)

## Categorical data only:
library(e1071)
data(HouseVotes84, package = "mlbench")
HouseVotes84=HouseVotes84[,1:3]
(model <- naiveBayes(Class ~ ., data = HouseVotes84))
(predict(model, HouseVotes84[1:10,]))
(predict(model, HouseVotes84[1:10,], type = "raw"))
(pred <- predict(model, HouseVotes84))
table(pred, HouseVotes84$Class)
$\endgroup$

2 Answers 2

5
$\begingroup$

From the R package (e1071) and the function naiveBayes that you're using:

The standard naive Bayes classifier (at least this implementation) assumes independence of the predictor variables, and Gaussian distribution (given the target class) of metric predictors. For attributes with missing values, the corresponding table entries are omitted for prediction.

It's pretty standard for continuous variables in a naive Bayes that a normal distribution is considered for these variables and a mean and standard deviation can then be calculated and then using some standard z-table calculations probabilities can be estimated for each of your continuous variables to make the naive Bayes classifier. I thought that it was possible to change the distributional assumption in this package, but apparently I'm wrong.

There is another R package (klaR) where you can change the density kernel. (the function is NaiveBayes). From the package:

NaiveBayes(x, grouping, prior, usekernel = FALSE, fL = 0, ...)

usekernel

if TRUE a kernel density estimate (density) is used for denstity estimation. If FALSE a normal density is estimated.

density(x, bw = "nrd0", adjust = 1,
        kernel = c("gaussian", "epanechnikov", "rectangular",
                   "triangular", "biweight",
                   "cosine", "optcosine")
$\endgroup$
0
$\begingroup$

I was working on a project not that long ago for which I needed to compute a naive bayes classifier (from scratch). I started out in R, but once I had the process down, I moved the code to Python. Here's my code that I began with. Don't expect it to be polished. For the most part, I followed Wikipedia's example (https://en.wikipedia.org/wiki/Naive_Bayes_classifier#Examples).

The steps are simple:

  1. calculate the a priori probabilities which are the proportion of classes

  2. For your continuous data, assume a normal distribution and calculate the mean and standard deviation.

  3. To classify observations, take the new input x, calculate dnorm(x, mu, sigma) where mu and sigma come from step 2.

  4. Sum up log(apriori) + log(dnorm(...)).

At this point, log(dnorm(...)) should contain two log-values (in my example). The probability that they belong in class 0 and probability they belong in class 1. This is the point Eric Peterson makes in his second paragraph.

  1. Calculate the posterior probabilities

I also compared my results to R library e1071. My probability results do not line up with theirs for this simple case, though the classification does. In their predict.naiveBayes function, they have something like log(apriori) + apply(log(sapply(...compute dnorm code here...)), 1, sum) which returns log(apriori) + log(1) = log(apriori) which is an error so their classification is solely based on the a priori probabilities (actually, they use counts not the probabilities).

Anyways, I hope this helps you (and anyone else) see what's under the hood as it was not clear to me either.

n=30 ## samples
set.seed(123)
x = c(rnorm(n/2, 10, 2), rnorm(n/2, 0, 2))
y = as.factor(c(rep(0, 20), rep(1, 10)))
y


#library(e1071)
#nb = naiveBayes(x, y, laplace = 0)
#nb

#nb_predictions = predict(nb, x[1], type='raw')
#nb_predictions



library(dplyr)

nbc <- function(x, y){
  df <- as.data.frame(cbind(x,y))
  a_priori <- table(y) #/length(y)

  cond_probs <- df %>% group_by(y) %>% summarise(means = mean(x), var = sd(x))

  print("A Priori Probabilities")
  print(a_priori/sum(a_priori))

  print("conditional probabilities \n")
  print(cond_probs)

  return(list(apriori = a_priori, tables = cond_probs))
}



predict_nbc <- function(model, new_x){
  apriori = as.matrix(model$apriori)
  a = log(apriori/sum(apriori))
  msd = as.matrix(model$tables)[,c(2,3)] ## creates 3 columsn; first is junk
  probs = sapply(new_x, function(v) dnorm(x = v, mean = msd[,1], sd = msd[,2]))
  b = log(probs)
  #L = a + b ## works for 1 new obs
  L = apply(X = b, MARGIN = 2, FUN = function(v) a + v)

  results <- apply(X = L, MARGIN = 2, function(x){
                   sapply(x, function(lp){ 1/sum(exp(x - lp)) }) ## numerically stable
  })
  return(results)
}



fit = nbc(x,y)

fit ## my naive bayes classifier model

myres = predict_nbc(fit, new_x = x[1:4])
myres
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.