As an experiment I’d like to start a thread where people can present snippets of Fortran code. I’ll start.
There are continuous symmetric statistical distributions that span (-Inf,Inf), such as the normal distribution, Student t, and Laplace. One may want to simulate data with skew, for example to simulate daily stock market returns, which have some negative skew. In a paper On Bayesian Modeling of Fat Tails and Skewness, Fernandez and Steel suggested scaling positive and negative variates by different amounts to create skew. Here is a function that takes input data with mean and variance assumed to be 0 and 1 and creates skewed data with the same expected mean and variance but with nonzero skew.
module asymm_mod
implicit none
integer, parameter :: dp = kind(1.0d0)
private
public :: asymm
contains
pure function asymm(x,c,abs_x) result(y)
! create asymmetry for symmetric zero-mean, unit-variance data by differentially scaling positive and negative values of x(:)
real(kind=dp), intent(in) :: x(:)
real(kind=dp), intent(in) :: c ! asymmetry parameter: > 1 to create positively skewed data, 0 < c < 1 for negatively skewed data
real(kind=dp), intent(in) :: abs_x ! known absolute mean
real(kind=dp) :: y(size(x))
real(kind=dp) :: ymean,y2,yvar
ymean = (c-(1/c)) * abs_x/2 ! mean of scaled data
y2 = (c**2 + 1/(c**2))/2 ! 2nd moment of scaled data
yvar = y2 - ymean**2 ! variance of scaled data
y = (merge(c,1/c,x>0) * x - ymean)/sqrt(yvar) ! shift and scale y to have zero mean and unit variance
end function asymm
end module asymm_mod
Here is a driver (the modules USEd are big and not provided, but the c
function to evade Fortran’s string limitations is shown here).
program xasymm
! 10/24/2021 08:49 AM driver for asymm, which generates skewed data from symmetric data
use kind_mod , only: dp
use random_normal_mod, only: random_normal
use statistics_mod , only: print_stats
use util_mod , only: c,pi
use asymm_mod , only: asymm
integer , parameter :: n = 10**6, niter = 5
real(kind=dp), allocatable :: x(:,:)
integer :: iter
call random_seed()
allocate (x(n,2))
write (*,*) "#obs =",n
do iter=1,niter
x(:,1) = random_normal(n)
x(:,2) = asymm(x(:,1),3.0_dp,sqrt(2/pi))
call print_stats(c("median","mean","sd","skew","kurt","perc_05","perc_95","min","max"),x)
end do
end program xasymm
Sample output:
#obs = 1000000
var median mean sd skew kurt perc_05 perc_95 min max
original 0.0004 -0.0006 1.0010 -0.0028 -0.0082 -1.6479 1.6433 -5.3463 4.6752
skewed -0.5743 0.0002 1.0000 1.4770 1.8428 -0.8718 2.0894 -1.5381 7.0050
var median mean sd skew kurt perc_05 perc_95 min max
original -0.0001 0.0000 0.9999 0.0007 0.0009 -1.6425 1.6449 -4.5028 4.7293
skewed -0.5750 0.0000 1.0002 1.4812 1.8653 -0.8708 2.0920 -1.3861 7.0928
var median mean sd skew kurt perc_05 perc_95 min max
original 0.0006 0.0007 1.0001 -0.0000 -0.0094 -1.6463 1.6469 -4.5609 4.6480
skewed -0.5740 0.0010 1.0002 1.4763 1.8457 -0.8715 2.0952 -1.3966 6.9610
It can be seen that skewed variates with zero mean and unit variance are generated.