21  Python: Modular Approach

21.1 Function: Mean (AM, GM, HM)

  • Calculate AM, GM and HM of a numeric vector using the following formula.

Arithmetic Mean (AM)

\[ \large AM = (x_1 + x_2 + ... + x_n)/n = \frac{1}{n}\sum\limits_{i=1}^{n} x_{i}\]

Geometric Mean (GM)

\[ \large GM = \sqrt[n]{(x_1 x_2 ... x_n)} = \left( \prod \limits_{i=1}^{n} x_{i} \right) ^{\frac{1}{n}} \]

Harmonic Mean (HM)

\[ \large HM = \frac{n}{(\frac{1}{x_1} + \frac{1}{x_2} + ... + \frac{1}{x_n})} = \frac{1}{\frac{1}{n}\sum\limits_{i=1}^{n} \frac{1}{x_i}}\]

21.2 Functions: Modular approach

Note: The following script is for demonstration only to explain the structure of a function and modular approach to function call.

We are expanding all base functions without any obvious advantages here. A possible optimal version is shown in the next section.

Code
import numpy as np

def fn_n(x, **kwargs):
    na_rm = kwargs.get('na_rm', None)
    if na_rm:
          n = len(x[~np.isnan(x)])
    else:
          n = len(x)
    return(n)

def fn_sum(x, **kwargs):
    na_rm = kwargs.get('na_rm', None)
    if na_rm:
          out = np.nansum(x)
    else:
          out = np.sum(x)
    return(out)

def fn_prod(x, **kwargs):
    na_rm = kwargs.get('na_rm', None)
    if na_rm:
          out = np.nanprod(x)
    else:
          out = np.prod(x)
    return(out)

def fn_suminv(x, **kwargs):
    na_rm = kwargs.get('na_rm', None)
    if na_rm:
          out = np.nansum(1/x)
    else:
          out = np.sum(1/x)
    return(out)

def fn_AM(x, **kwargs):
  out = fn_sum(x, **kwargs)/fn_n(x, **kwargs)
  return(out)

def fn_GM(x, **kwargs):
  out = fn_prod(x, **kwargs)**(1/fn_n(x, **kwargs))
  return(out)

def fn_HM(x, **kwargs):
  out = fn_n(x, **kwargs)/fn_suminv(x, **kwargs)
  return(out)

# fn_nMean
def fn_nMean(x, **kwargs):
  n = fn_n(x, **kwargs)
  AM = fn_AM(x, **kwargs)
  GM = fn_GM(x, **kwargs)
  HM = fn_HM(x, **kwargs)
  Mean = {'n': n, 'AM': AM, 'GM': GM, 'HM': HM}
  return(Mean)

21.3 Data

Code
A = np.array([11, 12, np.nan, 14, 15])

21.4 Function call 1

Code
fn_Mean(A)

21.5 Function call 2

Code
fn_Mean(A, na.rm = TRUE)

21.6 Optimal version

We have all base functions available; a possible optimal version of the above function can be written as shown below.

Code
import numpy as np

def fn_Mean(x, na_rm=False):
    
    def fn_n(x, na_rm=False):
        if na_rm:
            x = x[~np.isnan(x)]
        return len(x)
    
    n = fn_n(x, na_rm=na_rm)
    AM = np.nanmean(x)
    GM = np.exp(np.nanmean(np.log(x)))
    HM = 1 / np.nanmean(1 / x)
    Mean = {'n': n, 'AM': AM, 'GM': GM, 'HM': HM}
    return Mean