I am attempting to take into account the interaction among 5 different parameters in a Latin hypercube design in Python 3.8. However, I can't understand how, probably also because - not being my field - I never used these methods. I initially posted this on Stack Overflow, but it seems more relevant here.
I have an initial ensemble of 500 simulations, where I perturb 5 parameters. The 500 members are initially created by generating a Latin hypercube with 5 dimensions, sampled 500 times from uniform distributions.
The parameters are input model parameters, and I have limited knowledge of them. The outputs are forecasts of a plume. I have observations available for comparing the forecasts to the real event, satellite data that detected the plume. However, the observations do not provide any information about these parameters.
Among the 5 parameters, I am expecting correlation especially among X, Y and Z as all the three parameters are used to calculate the source strength in the model.
The comparison between forecasts and real observations (satellite data), however, will allow me to see which forecast is closer to the observations. Therefore the ensemble members closer to the observations will help in constraining my input model parameters ranges.
I use the parameters ranges from these retained simulations to fit new distributions and resample each parameter in a Latin hypercube for a new ensemble. The data are skewed and Gamma is a good fit. So, for now without considering interaction, I am operating like this:
```python
# Fit the parameters with a gamma distribution:
a_X, loc_X, scale_X = stats.gamma.fit(X)
a_Y, loc_Y, scale_Y = stats.gamma.fit(Y)
a_Z, loc_Z, scale_Z = stats.gamma.fit(Z)
a_R, loc_R, scale_R = stats.gamma.fit(R)
a_T, loc_T, scale_T = stats.gamma.fit(T)
# Create my new LHS design using the LHS function from pyDOE:
parameter_sets = lhs(5, samples=500)
# Transform it to be gamma distributed:
parameter_sets[:, 0] = gamma(a=a_X, loc=loc_X, scale=scale_X).ppf(parameter_sets[:, 0])
parameter_sets[:, 1] = gamma(a=a_Y, loc=loc_Y, scale=scale_Y).ppf(parameter_sets[:, 1])
parameter_sets[:, 2] = gamma(a=a_Z, loc=loc_Z, scale=scale_Z).ppf(parameter_sets[:, 2])
parameter_sets[:, 3] = gamma(a=a_R, loc=loc_R, scale=scale_R).ppf(parameter_sets[:, 3])
parameter_sets[:, 4] = gamma(a=a_T, loc=loc_T, scale=scale_T).ppf(parameter_sets[:, 4])
If I run these new 500 simulations with this new parameter set, and compare them with the observations, the number of simulations closer to the observations increase considerably. However, I now want to improve the method, and take into account interaction among the parameters. I created a correlation matrix, and it seems that the major interactions are among X, Y and Z (I was expecting it). The correlation matrix I get, is something like this
X Y Z R T
X 1.000000 -0.477159 -0.135367 0.074064 -0.045986
Y -0.477159 1.000000 -0.501240 -0.127851 -0.139549
Z -0.135367 -0.501240 1.000000 -0.078035 -0.026585
R 0.074064 -0.127851 -0.078035 1.000000 -0.004523
T -0.045986 -0.139549 -0.026585 -0.004523 1.000000
However, I can’t figure out how to integrate the correlation matrix in the LHS.. By calculating the means and the covariance matrix I could use a multivariate normal, as I often read in these cases. But it assumes that all inputs are normally distributed that is not my case. And also in this case, I can't figure out then how to sample the multivariate from the LHS. Any help is greatly appreciated!
EDIT:
after looking into using multivariate copulas (still need to understand the concept properly, but I wanted to give it a go already), I modified the method like this (using this package specifically: Copulas):
from copulas.multivariate import GaussianMultivariate
from copulas.univariate import GammaUnivariate
# Here I specify which distribution to use, gamma in my case
dist = GaussianMultivariate(distribution={
"X": GammaUnivariate,
"Y": GammaUnivariate,
"Z": GammaUnivariate,
"R": GammaUnivariate,
"T": GammaUnivariate})
copula = GaussianMultivariate()
copula.fit(data)
# Create a new LH
test_lhs = lhs(5, samples=500)
# Transform test_lhs based on the copula
test_lhs[:, 0] = copula.univariates[0].ppf(test_lhs[:, 0])
test_lhs[:, 1] = copula.univariates[1].ppf(test_lhs[:, 1])
test_lhs[:, 2] = copula.univariates[2].ppf(test_lhs[:, 2])
test_lhs[:, 3] = copula.univariates[3].ppf(test_lhs[:, 3])
test_lhs[:, 4] = copula.univariates[4].ppf(test_lhs[:, 4])
I'll keep looking into this, but I wonder if I am on the right direction or not?