I had a chance to examine the faithful
data set in R, to score some plausible metrics: a two-sample Kolmogorov-Smirnov test (distance and p-value), Kullback–Leibler divergence, and Mutual Information for a range of different numbers of bins.
An overview of the data:
The duration between Old Faithful geyser eruptions can be plotted as a histogram, but what is the trade off of #bins vs. fidelity to the data set?
A 4 bin histogram shows too much averaging
A 14 bin histograms shows the two main modes
A 34 bin histogram show more minor modes
And a 54 bin histogram shows all of the data at the measurement accuracy:
Results of different metrics:
Using the metrics of information loss we can plot how they change with an increasing number of bins:
Here, the KS metrics remain mostly unchanged above 27 bins, KL Divergence (in my implementation) is a useless metric, but mutual information best captures the increasing accuracy with increasing bin count.
Thoughts?
Teh codez:
library(entropy)
library(ggplot2)
library(dplyr)
library(reshape2)
data("faithful")
duration <- faithful$waiting
xrange <- seq(min(duration),max(duration),by=1)
h.all <- hist(duration,c(xrange[1]-0.5,xrange+0.5))
metrics <- function(nbins){
h <- hist(duration,seq(min(duration)-0.5*(max(duration)-min(duration)+1)/nbins,max(duration)+0.5*(max(duration)-min(duration)+1)/nbins,length.out=1+nbins)) # must force bin breakpoints
x <- unlist(mapply(FUN=function(m,c){rep(m,c)},h$mids,h$counts))
# KS test?
ks <- ks.test(x,duration,alternative='two.sided',exact=FALSE)
# KL divergence?
KLD <- KL.empirical(x,duration)
# Mutual information?
bin.ix.orig <- findInterval(duration,h.all$breaks,rightmost.closed = TRUE)
bin.ix.new <- findInterval(duration,h$breaks,rightmost.closed = TRUE)
y2d <- table(data.frame(bin.ix.new,bin.ix.orig))
MI <- mi.empirical(y2d)
return(data.frame(KS.distance=as.numeric(ks$statistic),
KS.p.value= ks$p.value,
KL.Divergence = KLD,
Mutual.Info = MI))
}
df.sweep <- data.frame(nbins=seq(3,60)) %>% group_by(nbins) %>% do(metrics(.$nbins)) %>% ungroup()
ggplot(melt(df.sweep,id.vars='nbins'),aes(x=nbins,y=value))+geom_point()+facet_wrap(~variable,scales='free_y')