s_lin_eof Subroutine

public subroutine s_lin_eof(x, nd, nv, pc, eof, ew, opt, wt, r2, eof_scaled)

Empirical Orthogonal Function (EOF) analysis

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: x(nd,nv)

input data

integer(kind=i4), intent(in) :: nd

number of rows

integer(kind=i4), intent(in) :: nv

number of columns

real(kind=wp), intent(out) :: pc(nd,nv)

principal components

real(kind=wp), intent(out) :: eof(nv,nv)

EOFs/eigenvectors (unweighted)

real(kind=wp), intent(out) :: ew(nv)

eigenvalues

integer(kind=i4), intent(in), optional :: opt

0 = covariance, 1 = correlation

real(kind=wp), intent(in), optional :: wt(nv)

optional weights (default = 1.0/n)

real(kind=wp), intent(out), optional :: r2(nv)

explained variance (fraction)

real(kind=wp), intent(out), optional :: eof_scaled(nv,nv)

EOFs/eigenvectors scaled for plotting


Calls

proc~~s_lin_eof~~CallsGraph proc~s_lin_eof s_lin_eof eigh eigh proc~s_lin_eof->eigh proc~f_sts_mean_core f_sts_mean_core proc~s_lin_eof->proc~f_sts_mean_core proc~f_sts_std_core f_sts_std_core proc~s_lin_eof->proc~f_sts_std_core proc~s_err_print s_err_print proc~s_lin_eof->proc~s_err_print proc~f_sts_var_core f_sts_var_core proc~f_sts_std_core->proc~f_sts_var_core proc~f_utl_r2c f_utl_r2c proc~s_err_print->proc~f_utl_r2c proc~f_sts_var_core->proc~f_sts_mean_core

Called by

proc~~s_lin_eof~~CalledByGraph proc~s_lin_eof s_lin_eof interface~fsml_eof fsml_eof interface~fsml_eof->proc~s_lin_eof proc~s_lin_pca s_lin_pca proc~s_lin_pca->proc~s_lin_eof interface~fsml_pca fsml_pca interface~fsml_pca->proc~s_lin_pca

Source Code

subroutine s_lin_eof(x, nd, nv, pc, eof, ew, opt, wt, r2, eof_scaled)

! ==== Description
!! Empirical Orthogonal Function (EOF) analysis

! ==== Declarations
  integer(i4), intent(in)            :: nd                !! number of rows
  integer(i4), intent(in)            :: nv                !! number of columns
  real(wp)   , intent(in)            :: x(nd,nv)          !! input data
  integer(i4), intent(in) , optional :: opt               !! 0 = covariance, 1 = correlation
  real(wp)   , intent(in) , optional :: wt(nv)            !! optional weights (default = 1.0/n)
  real(wp)   , intent(out)           :: pc(nd,nv)         !! principal components
  real(wp)   , intent(out)           :: eof(nv,nv)        !! EOFs/eigenvectors (unweighted)
  real(wp)   , intent(out)           :: ew(nv)            !! eigenvalues
  real(wp)   , intent(out), optional :: r2(nv)            !! explained variance (fraction)
  real(wp)   , intent(out), optional :: eof_scaled(nv,nv) !! EOFs/eigenvectors scaled for plotting
  real(wp)                           :: eof_tmp(nv,nv)    !! temp storage for EOFs
  real(wp)                           :: x_w(nd,nv)        !! working copy of data
  integer(i4)                        :: opt_w             !! final value for opt
  real(wp)                           :: wt_w(nv)          !! final values for wt
  real(wp)                           :: c(nv,nv)          !! covariance/correlation matrix
  real(wp)                           :: w(nv)             !! matrix for eigenvalues (diagonal)
  integer(i4)                        :: nn                !! number of nonzero eigenvalues
  integer(i4)                        :: i, j              !! loop indices
  real(wp)                           :: tmp               !! temporary real

! ==== Instructions

  ! ---- handle input

  ! check matrix dimensions
  if (nd .lt. 2 .or. nv .lt. 1) then
     ! write error message and stop if invalid
     call s_err_print(fsml_error(3))
     error stop
  endif

  ! weight options; will default to 1/nv if not specified
  tmp = real(nv, kind=wp)
  wt_w = 1.0_wp / tmp
  if (present(wt)) wt_w = wt

  ! matrix options: covariance (0, default) or correlation
  opt_w = 0
  if (present(opt)) opt_w = opt

  ! make working copy of data
  x_w = x

  ! ---- construct covariance or correlation matrix

  ! prepare data
  do j = 1, nv
     ! centre (get anomalies)
     tmp = f_sts_mean_core(x_w(:,j))
     x_w(:,j) = x_w(:,j) - tmp
     ! standardise if specified (if correlation matrix is to be used)
     if (opt_w .eq. 1) then
        tmp = f_sts_std_core(x_w(:,j), 0.0_wp)
        if (tmp .gt. 0.0_wp) then
           x_w(:,j) = x_w(:,j) * (1.0_wp / tmp)
        else
           call s_err_print("[fsml error] Standard deviation&
                           & is zero for a column.")
           error stop
        endif
     endif
     ! apply weights
     x_w(:,j) = x_w(:,j) * sqrt(wt_w(j))
  enddo

  ! construct matrix
  tmp = real(nd - 1, kind=wp)
  c = matmul(transpose(x_w), x_w) / tmp

  ! ---- calculate outputs

  ! eigen-decomposition using stdlib eigh
  call eigh(c, w, vectors=eof_tmp)

  ! extract and reorder eigenvalues/eigenvectors in descending order
  eof = 0.0_wp
  ew  = 0.0_wp
  nn  = 0
  do i = nv, 1, -1
     if (w(i) .gt. 0.0_wp) then
        nn = nn + 1
        ew(nn) = w(i)
        eof(:,nn) = eof_tmp(:,i)
     endif
  enddo

  ! compute principal components
  pc = 0.0_wp
  pc(:,1:nn) = matmul(x_w, eof(:,1:nn))

  ! undo weight scaling for EOFs
  if (nn .gt. 0) then
     do i = 1, nv
        tmp = 1.0_wp / sqrt(wt_w(i))
        eof(i,1:nn) = eof(i,1:nn) * tmp
     enddo
  endif

  ! scale EOFs for plotting
  if (present(eof_scaled)) then
     if (nn .gt. 0) then
        eof_scaled = 0.0_wp
        do j = 1, nn
           tmp = sqrt(ew(j))
           eof_scaled(:,j) = eof(:,j) * tmp
        enddo
     endif
  endif

  ! explained variance (fraction)
  if (present(r2)) then
     r2 = 0.0_wp
     r2(1:nn) = ew(1:nn) / sum(ew(1:nn))
  endif

end subroutine s_lin_eof