Consider some data (distr.dat). It is a tabulated PDF: x, y, z, PDF[x,y,z]
, where x
is some parameter (and the integral of PDF[x,y,z]
over y,z for the fixed x should give 1). I would like to sample points {y,z} according to the distribution for the given value of x
(any value within the minimal and maximal value of x
from data
). This is how I do it: first, interpolate data logarithmically (just to accurately follow the shapes when it is changed exponentially fast), with the function named distr
, then uniformly sample y,z in the domain of definition of the PDF, then calculate the weights, and, finally, produce the "true" y,z using RandomChoice
:
(*Interpolating the distribution*)
data = Import[FileNameJoin[{NotebookDirectory[], "distr.dat"}],
"Table"];
distr[x_, y_, z_] =
10^Interpolation[
Log10[{#[[1]], #[[2]], #[[3]], #[[4]] + 10^-90} & /@ data],
InterpolationOrder -> 1][Log10[x], Log10[y], Log10[z]]
(*Generating the random y,z points - unweighted*)
{xminmax, yminmax, zminmax} = MinMax[data[[All, #]]] & /@ {1, 2, 3};
NevtsUnw = 10^6;
yzrandgrid =
Join[RandomReal[yminmax, {NevtsUnw, 1}],
RandomReal[zminmax, {NevtsUnw, 1}], 2];
(*Calculating weights*)
xval = RandomReal[xminmax];
distrtemp[y_, z_] = distr[xval, y, z];
weights = distrtemp @@@ yzrandgrid; // AbsoluteTiming
(*Final "true" points*)
NevFinal = 5*10^5;
truepoints =
RandomChoice[weights -> yzrandgrid, NevFinal]; // AbsoluteTiming
The resulting points follow the distribution, although not accurate enough. This is because the distribution is highly non-uniform (for instance, it is peaked at small y), while the unweighted data is distributed uniformly. Let me illustrate this by comparing the marginal distribution in y with the corresponding histogram obtained using the generated sample:
pdf1[y_] =
10^Interpolation[
ParallelTable[{y,
Log10[Quiet[
NIntegrate[
distrtemp[10^y, Exp[z]] Exp[z], {z, Log[zminmax[[1]]],
Log[zminmax[[2]]]},
Method -> "InterpolationPointsSubdivision"]]]}, {y,
Log10[yminmax[[1]]],
Log10[yminmax[[2]]], (Log10[yminmax[[2]]] -
Log10[yminmax[[1]]])/50}], InterpolationOrder -> 1][Log10[y]];
Show[Plot[pdf1[y], {y, yminmax[[1]], yminmax[[2]]}, PlotRange -> All],
Histogram[truepoints[[All, 1]], 500, "ProbabilityDensity"]]
The slowest part is weights = distrtemp @@@ yzrandgrid;
. As for distr
, I use a Mathematica built-in interpolation, which slows things down significantly, taking 8-9 seconds on my laptop. There are much faster ways to interpolate. For instance, these answers allow one to map the interpolated data into a structured grid much-much faster than the ordinary interpolation does, but it is not obvious whether it is possible to adapt them to the unstructured grid as in the question.
Is there any way to speed it up significantly, or is there another method allowing to sample the points quickly and accurately?
Edit
The interpolation may be sped up using the combination of the method from this question (which fastly maps the grid x,y,z,PDF[x,y,z]
onto y,z,PDF[X,y,z]
, where X
is any desirable value within the grid for x
) and this question (100X faster interpolation than the built-in one).
x
? I ask because if a smooth kernel estimate of the bivariate density was available, then random samples from the bivariate distribution would be extremely straightforward. $\endgroup${y,z,f[x,y,z]}
for any given value ofx
? $\endgroup${x,y,z,pdf[x,y,z]}
. I may, however, quickly convert it to{y,z,pdf[x,y,z]}
for any value ofx
using the method from this question: mathematica.stackexchange.com/questions/282637/… . $\endgroup$NIntegrate[distr[0.05, y, z], {y, yminmax[[1]], yminmax[[2]]}, {z, zminmax[[1]], zminmax[[2]]}]
, I get 0.969948. Or am I doing it wrong? $\endgroup$