So I saw a discrete version of this described here:
https://www.researchgate.net/post/What_is_chi-squared_distance_I_need_help_with_the_source_code
The denominator is not the same, in yours it is the product while in the reference it is the difference.
Here is my first bit of code:
## Libraries
library(dplyr)
## Parameters
samp_size <- 2 #how many "bins" in our "histogram"
num_loops <- 1e5 #how many times to run this
## Subroutines
#my distance function
ascad <- function(x,y){
sum( (1/(x+y))*(x-y )^2 )
}
## Main Program
#predeclare data frame
df <- as.data.frame(matrix(data = 0, nrow = samp_size, ncol=3))
names(df) <- c("x", "y", "xpy")
#predeclare output store
tests <- as.data.frame(matrix(data = F, nrow = num_loops, ncol=4))
names(tests) <- c("nonnegative",
"identity",
"symmetry",
"triangle")
#main loop
for(i in 1:num_loops){
#draw random positive values
val1 <- (1/runif(n = samp_size,min = 0, max = 1))-1
val2 <- (1/runif(n = samp_size,min = 0, max = 1))-1
val3 <- (1/runif(n = samp_size,min = 0, max = 1))-1
#normalize, and compute
df$x <- val1/sum(val1)
df$y <- val2/sum(val2)
df$y3 <- (val1 + val2)/(sum(val1)+sum(val2))
#compute additve symmetric chi-squared distance
d1 <- ascad(df$x, df$y) #(x vs y)
d2 <- ascad(df$y, df$xpy) #(y vs x+y)
d3 <- ascad(df$x, df$xpy) #(x vs x+y)
d4 <- ascad(df$y, df$x) #(y vs x)
#non-negativity test
if(d3 >=0){
tests[i,1] <- TRUE
}
#symmetry
if(d1 == d4){
tests[i,3] <- TRUE
}
#triangle
if(d1 <= d2 + d3){
tests[i,4] <- TRUE
}
#recontrive it to test identity
if(sample(1:5,1)==1){
val2 <- val1
} else {
val1 <- val2
}
d1 <- ascad(val1, val2) #(x vs y)
#identity test
if( sum(val1 == val2)==samp_size & d1==0){
tests[i,2] <- TRUE
}
}
#make summary of tests
summary(tests)
It gives the following result:
> summary(tests)
nonnegative identity symmetry triangle
Mode:logical Mode:logical Mode:logical Mode:logical
TRUE:100000 TRUE:100000 TRUE:100000 TRUE:100000
What I interpret it to say is:
- given a hundred thousand trials
- with identity testing by clamping either x=y or y=x
- and the default result is FALSE
None of the tests came out false.
This SUGGESTS that it is in fact a distance metric, though restricting the input domain to positive might make it a degenerate metric of some sort. If in the summary there were not 1e5 in all cases, then there would have been failure to pass the test, a valid counter-example, and it would not qualify as a metric of some sort.
You could
- increase the number of elements in the vector, but I think that even
1 should generalize well.
- test more uniformly or non-randomly, although I feel like inverting
'runif' was clever. If you try every value it may take longer but be more exhaustive
- make a version of this that runs through a symbolic algebra engine
like sympy or yacas.
- test it with a difference for a denominator
This is, however, a possibly non-terrible start toward a solid answer.