Bug: heatmaps and contour plots fail for reasonable function
bienvenu opened this issue · comments
Hi, I think I have found a bug.
Let dp denote the Lp distance, that is,
dp(x, y) = (|x1 - y1|p + |x2 - y2|p + |x3 - y3|p)1/p
and, for p = ∞, d∞(x, y) = max(|x1 - y1|, |x2 - y2|, |x3 - y3|)
I want to make a contour plot showing the corresponding balls centered on c = (1/3, 1/3, 1/3), as a function of p. So, first, I define a function giving the distance to c:
dc <- function (p) {
function (a, b, c) {
(abs(a - 1/3)^p + abs(b - 1/3)^p + abs(c - 1/3)^p)^(1/p)
}
}
Then, I use the code from the doc:
p = 1
TernaryPlot()
values <- TernaryPointValues(dc(p), resolution = 36L)
ColourTernary(values)
TernaryContour(dc(p), resolution = 36L)
For p = 1, 2, ..., the plots look about right:
and, for p = 100, we get something that looks exactly like what we are supposed to see for p = ∞:
However, for p = ∞ this is not working. Let us start by defining the relevant distance function:
dcmax <- function (a, b, c) {
max(abs(a - 1/3), abs(b - 1/3), abs(c - 1/3))
}
The function seems to work fine. In particular, it is not constant:
> dcmax(1/3, 1/3, 1/3)
[1] 0
> dcmax(1/2, 1/4, 1/4)
[1] 0.1666667
> dcmax(0.6, 0.1, 0.3)
[1] 0.2666667
However, trying to plot the contours fails and raises an error:
> TernaryPlot()
> values <- TernaryPointValues(dcmax, resolution = 36L)
> ColourTernary(values)
> TernaryContour(dcmax, resolution = 36L)
Message d'avis :
Dans contour.default(x, y, z, add = TRUE, ...) :
toutes les valeurs en z sont égales
The last message (in French) says "All z values are equal". And, indeed,
values
...
[,1278] [,1279] [,1280] [,1281] [,1282] [,1283]
x 0.02777778 0.04166667 0.05555556 -0.04166667 -0.02777778 -0.01388889
y 0.75376285 0.76178161 0.75376285 0.77781911 0.78583787 0.77781911
z 0.64814815 0.64814815 0.64814815 0.64814815 0.64814815 0.64814815
down 0.00000000 1.00000000 0.00000000 0.00000000 1.00000000 0.00000000
[,1284] [,1285] [,1286] [,1287] [,1288] [,1289]
x 0.0000000 0.01388889 0.02777778 0.04166667 -0.02777778 -0.01388889
y 0.7858379 0.77781911 0.78583787 0.77781911 0.80187537 0.80989413
z 0.6481481 0.64814815 0.64814815 0.64814815 0.64814815 0.64814815
down 1.0000000 0.00000000 1.00000000 0.00000000 0.00000000 1.00000000
[,1290] [,1291] [,1292] [,1293] [,1294] [,1295] [,1296]
x 0.0000000 0.01388889 0.02777778 -0.01388889 0.0000000 0.01388889 0.0000000
y 0.8018754 0.80989413 0.80187537 0.82593164 0.8339504 0.82593164 0.8499879
z 0.6481481 0.64814815 0.64814815 0.64814815 0.6481481 0.64814815 0.6481481
down 0.0000000 1.00000000 0.00000000 0.00000000 1.0000000 0.00000000 0.0000000
dcmax
is a continuous, non-constant function so there is no reason why we should not be able to get a contour plot.
In case that can be useful:
> version
_
platform x86_64-pc-linux-gnu
arch x86_64
os linux-gnu
system x86_64, linux-gnu
status
major 4
minor 1.1
year 2021
month 08
day 10
svn rev 80725
language R
version.string R version 4.1.1 (2021-08-10)
nickname Kick Things
> sessionInfo()
R version 4.1.1 (2021-08-10)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Debian GNU/Linux 10 (buster)
Matrix products: default
BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.8.0
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.8.0
locale:
[1] LC_CTYPE=fr_FR.UTF-8 LC_NUMERIC=C
[3] LC_TIME=fr_FR.UTF-8 LC_COLLATE=fr_FR.UTF-8
[5] LC_MONETARY=fr_FR.UTF-8 LC_MESSAGES=fr_FR.UTF-8
[7] LC_PAPER=fr_FR.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=fr_FR.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] Ternary_1.2.3
loaded via a namespace (and not attached):
[1] Rcpp_1.0.7 viridisLite_0.4.0 digest_0.6.28 later_1.3.0
[5] mime_0.12 R6_2.5.1 lifecycle_1.0.1 xtable_1.8-4
[9] magrittr_2.0.1 rlang_0.4.12 promises_1.2.0.1 ellipsis_0.3.2
[13] tools_4.1.1 shiny_1.7.1 httpuv_1.6.3 fastmap_1.1.0
[17] compiler_4.1.1 htmltools_0.5.2
Hi, thanks for the detailed bug report.
In your function dcmax
, try replacing max()
with pmax()
.
So this happens because the functions TernaryPointValues
and TernaryContour
only work with vectorized functions. This might be worth adding to the doc: I do not think this is stated anywhere. For instance, in the documentation of these functions, the argument is described as
Func: Function taking the parameters ‘a’, ‘b’ and ‘c’, which
evaluates to a numeric whose value should be depicted.
Moreover, there is never a type error that could give the user a clue as to what is going on.
That's a good suggestion, thanks. Could you suggest a suitable wording that I could use in the docs?
I'm not sure how I'd set up a type error – could you talk me through how you'd imagine the error detection process working?
Thanks!
I do not think that that my description of the problem being that "the functions TernaryPointValues and TernaryContour only work with vectorized functions" was accurate. I think that in R the term "vectorized" is used to refer to two different things:
- In the loose sense, functions f such that for a vector x = (xi), f(x) return y = (f(xi))
- In the strict sense, functions f that are weakly vectorized and where the iteration on the entries of the (atomized) input vector is done outside of R.
Here, I think that you only need the argument to be vectorized in the loose sense (although I haven't tried to confirm it).
(also, I think that R's function max
is vectorized in the strict sense, but not in the loose one)
Could you suggest a suitable wording that I could use in the docs?
I'm not sure, I'm not a R user so I do not know what the standard practices are. I thought that saying
Func: Function taking the parameters ‘a’, ‘b’ and ‘c’, which
evaluates to a numeric whose value should be depicted.
Must be a vectorized function.
would be concise and clear enough, but based on what I said above I'm afraid that is not a very precise wording. And I don't know what the R jargon is to refer to "vectorized functions" in the loose sense.
I'm not sure how I'd set up a type error – could you talk me through how you'd imagine the error detection process working?
Unfortunately, I have no idea how to do that. Based on this, It seems that there is no way to check if a function is "vectorized". Depending on how your functions are implemented, simply checking that f(a, b, c) is a vector and not a scalar. For instance,
> dcmax(c(1/2, 1/3), c(1/4, 1/3), c(1/4, 1/3))
[1] 0.1666667
whereas I'm assuming that your code is expecting this to be a vector (whether the entry-wise operation are performed in R or vectorized in the strict sense being irrelevant). In that case, you might just check this explicitly and raise an error with a detailed error message if this is not the case.
Hope that helps. I'm sorry but, not being a R user, I'm not really going to be able to be any useful on this.
Thanks for these pointers – I've made appropriate improvements on this basis. Hopefully this will make life easier for future users!
Hope you can now get the package to do everything you need. Do let me know if you run into any other snags!
Now incorporated in v1.2.4 on CRAN.