Module stikpetP.tests.test_ham_owa

Expand source code
import pandas as pd
from scipy.stats import f

def ts_ham_owa(nomField, scaleField, categories=None):
    '''
    Hartung-Agac-Makabi One-Way ANOVA
    ------------------------------
    Tests if the means (averages) of each category could be the same in the population.
        
    If the p-value is below a pre-defined threshold (usually 0.05), the null hypothesis is rejected, and there are then at least two categories who will have a different mean on the scaleField score in the population.
    
    This test is a modification of the Welch one-way ANOVA.
    
    There are quite some alternatives for this, the stikpet library has Fisher, Welch, James, Box, Scott-Smith, Brown-Forsythe, Alexander-Govern, Mehrotra modified Brown-Forsythe, Hartung-Agac-Makabi, Özdemir-Kurt and Wilcox as options. See the notes from ts_fisher_owa() for some discussion on the differences.
    
    Parameters
    ----------
    nomField : pandas series
        data with categories
    scaleField : pandas series
        data with the scores
    categories : list or dictionary, optional
        the categories to use from catField
    
    Returns
    -------
    Dataframe with:
    
    * *n*, the sample size
    * *k*, the number of categories
    * *statistic*, the test statistic (F value)
    * *df1*, degrees of freedom 1
    * *df2*, degrees of freedom 2
    * *p-value*, the p-value (significance)
    
    Notes
    -----
    The formula used (Hartung et al., 2002, pp. 206-207):
    $$ F_{HAM} = \\frac{\\frac{1}{k-1}\\times\\sum_{j=1}^k w_j^*\\times\\left(\\bar{x}_j - \\bar{y}_w^*\\right)^2}{1 + \\frac{2\\times\\left(k-2\\right)}{k^2-1}\\times \\lambda^*}$$
    $$ df_1 = k - 1$$
    $$ df_2 = \\frac{k^2-1}{3\\times\\lambda^*}$$
    $$ F_{HAM}\\sim F\\left(df_1, df_2\\right)$$
    
    With:
    $$ \\bar{y}_w^* = \\sum_{j=1}^k h_j^*\\times \\bar{x}_j$$
    $$ h_j^* = \\frac{w_j^*}{w^*}$$
    $$ w_j^* = \\frac{n_j}{s_j^2}\\times\\frac{1}{\\varphi_j}$$
    $$ w^* = \\sum_{j=1}^k w_j^*$$
    $$ \\varphi_j = \\frac{n_j + 2}{n_j + 1}$$
    $$ \\bar{x}_j = \\frac{\\sum_{j=1}^{n_j} x_{i,j}}{n_j}$$
    $$ s_j^2 = \\frac{\\sum_{i=1}^{n_j} \\left(x_{i,j} - \\bar{x}_j\\right)^2}{n_j - 1}$$
    $$ \\lambda^* = \\sum_{j=1}^k \\frac{\\left(1 - h_j^*\\right)^2}{n_j - 1}$$
    
    *Symbols used:* 
    
    * \\(k\\), for the number of categories
    * \\(x_{i,j}\\), for the i-th score in category j
    * \\(n_j\\), the sample size of category j
    * \\(\\bar{x}_j\\), the sample mean of category j
    * \\(s_j^2\\), the sample variance of the scores in category j
    * \\(w_j^*\\), the modified weight for category j
    * \\(h_j^*\\), the adjusted modified weight for category j
    * \\(df_i\\), i-th degrees of freedom
    * \\(\\varphi_j\\), the modification factor.
    
    Note that the R-library 'doex' uses \\( \\varphi_j = \\frac{n_j - 1}{n_j - 3}\\). The original article though states that these are unbalanced weights of the Welch test and in their experience, using these makes the test too conservative. In the original article they find from their simulation experience that using \\( \\varphi_j = \\frac{n_j + 2}{n_j + 1}\\) gives reliable results for small sample sizes, and a large number of populations.
    
    References
    ----------
    Hartung, J., Argaç, D., & Makambi, K. H. (2002). Small sample properties of tests on homogeneity in one-way anova and meta-analysis. *Statistical Papers, 43*(2), 197–235. doi:10.1007/s00362-002-0097-8
    
    Author
    ------
    Made by P. Stikker
    
    Companion website: https://PeterStatistics.com  
    YouTube channel: https://www.youtube.com/stikpet  
    Donations: https://www.patreon.com/bePatron?u=19398076
    
    '''
    if type(nomField) == list:
        nomField = pd.Series(nomField)
        
    if type(scaleField) == list:
        scaleField = pd.Series(scaleField)
        
    data = pd.concat([nomField, scaleField], axis=1)
    data.columns = ["category", "score"]
    
    #remove unused categories
    if categories is not None:
        data = data[data.category.isin(categories)]
    
    #Remove rows with missing values and reset index
    data = data.dropna()    
    data.reset_index()
    
    #overall n, mean and ss
    n = len(data["category"])
    m = data.score.mean()
    sst = data.score.var()*(n-1)
    
    #sample sizes, variances and means per category
    nj = data.groupby('category').count()
    sj2 = data.groupby('category').var()
    mj = data.groupby('category').mean()
    
    #number of categories
    k = len(mj)
    
    pj = (nj + 2)/(nj + 1)
    wj = nj/(pj*sj2)
    w = float(wj.sum())
    hj = wj/w
    ym = float((hj*mj).sum())
    lm = float(((1 - hj)**2/(nj - 1)).sum())
    
    fVal = float((wj*(mj - ym)**2).sum()/((k - 1) + 2*(k - 2)*1/(k+1)*lm))
    df1 = k - 1
    df2 = float((k**2 - 1)/(3*lm))
    
    pVal = f.sf(fVal, df1, df2)
    
    #results
    res = pd.DataFrame([[n, k, fVal, df1, df2, pVal]])
    res.columns = ["n", "k", "statistic", "df1", "df2", "p-value"]
    
    return res

Functions

def ts_ham_owa(nomField, scaleField, categories=None)

Hartung-Agac-Makabi One-Way ANOVA

Tests if the means (averages) of each category could be the same in the population.

If the p-value is below a pre-defined threshold (usually 0.05), the null hypothesis is rejected, and there are then at least two categories who will have a different mean on the scaleField score in the population.

This test is a modification of the Welch one-way ANOVA.

There are quite some alternatives for this, the stikpet library has Fisher, Welch, James, Box, Scott-Smith, Brown-Forsythe, Alexander-Govern, Mehrotra modified Brown-Forsythe, Hartung-Agac-Makabi, Özdemir-Kurt and Wilcox as options. See the notes from ts_fisher_owa() for some discussion on the differences.

Parameters

nomField : pandas series
data with categories
scaleField : pandas series
data with the scores
categories : list or dictionary, optional
the categories to use from catField

Returns

Dataframe with:
 
  • n, the sample size
  • k, the number of categories
  • statistic, the test statistic (F value)
  • df1, degrees of freedom 1
  • df2, degrees of freedom 2
  • p-value, the p-value (significance)

Notes

The formula used (Hartung et al., 2002, pp. 206-207): F_{HAM} = \frac{\frac{1}{k-1}\times\sum_{j=1}^k w_j^*\times\left(\bar{x}_j - \bar{y}_w^*\right)^2}{1 + \frac{2\times\left(k-2\right)}{k^2-1}\times \lambda^*} df_1 = k - 1 df_2 = \frac{k^2-1}{3\times\lambda^*} F_{HAM}\sim F\left(df_1, df_2\right)

With: \bar{y}_w^* = \sum_{j=1}^k h_j^*\times \bar{x}_j h_j^* = \frac{w_j^*}{w^*} w_j^* = \frac{n_j}{s_j^2}\times\frac{1}{\varphi_j} w^* = \sum_{j=1}^k w_j^* \varphi_j = \frac{n_j + 2}{n_j + 1} \bar{x}_j = \frac{\sum_{j=1}^{n_j} x_{i,j}}{n_j} s_j^2 = \frac{\sum_{i=1}^{n_j} \left(x_{i,j} - \bar{x}_j\right)^2}{n_j - 1} \lambda^* = \sum_{j=1}^k \frac{\left(1 - h_j^*\right)^2}{n_j - 1}

Symbols used:

  • k, for the number of categories
  • x_{i,j}, for the i-th score in category j
  • n_j, the sample size of category j
  • \bar{x}_j, the sample mean of category j
  • s_j^2, the sample variance of the scores in category j
  • w_j^*, the modified weight for category j
  • h_j^*, the adjusted modified weight for category j
  • df_i, i-th degrees of freedom
  • \varphi_j, the modification factor.

Note that the R-library 'doex' uses \varphi_j = \frac{n_j - 1}{n_j - 3}. The original article though states that these are unbalanced weights of the Welch test and in their experience, using these makes the test too conservative. In the original article they find from their simulation experience that using \varphi_j = \frac{n_j + 2}{n_j + 1} gives reliable results for small sample sizes, and a large number of populations.

References

Hartung, J., Argaç, D., & Makambi, K. H. (2002). Small sample properties of tests on homogeneity in one-way anova and meta-analysis. Statistical Papers, 43(2), 197–235. doi:10.1007/s00362-002-0097-8

Author

Made by P. Stikker

Companion website: https://PeterStatistics.com
YouTube channel: https://www.youtube.com/stikpet
Donations: https://www.patreon.com/bePatron?u=19398076

Expand source code
def ts_ham_owa(nomField, scaleField, categories=None):
    '''
    Hartung-Agac-Makabi One-Way ANOVA
    ------------------------------
    Tests if the means (averages) of each category could be the same in the population.
        
    If the p-value is below a pre-defined threshold (usually 0.05), the null hypothesis is rejected, and there are then at least two categories who will have a different mean on the scaleField score in the population.
    
    This test is a modification of the Welch one-way ANOVA.
    
    There are quite some alternatives for this, the stikpet library has Fisher, Welch, James, Box, Scott-Smith, Brown-Forsythe, Alexander-Govern, Mehrotra modified Brown-Forsythe, Hartung-Agac-Makabi, Özdemir-Kurt and Wilcox as options. See the notes from ts_fisher_owa() for some discussion on the differences.
    
    Parameters
    ----------
    nomField : pandas series
        data with categories
    scaleField : pandas series
        data with the scores
    categories : list or dictionary, optional
        the categories to use from catField
    
    Returns
    -------
    Dataframe with:
    
    * *n*, the sample size
    * *k*, the number of categories
    * *statistic*, the test statistic (F value)
    * *df1*, degrees of freedom 1
    * *df2*, degrees of freedom 2
    * *p-value*, the p-value (significance)
    
    Notes
    -----
    The formula used (Hartung et al., 2002, pp. 206-207):
    $$ F_{HAM} = \\frac{\\frac{1}{k-1}\\times\\sum_{j=1}^k w_j^*\\times\\left(\\bar{x}_j - \\bar{y}_w^*\\right)^2}{1 + \\frac{2\\times\\left(k-2\\right)}{k^2-1}\\times \\lambda^*}$$
    $$ df_1 = k - 1$$
    $$ df_2 = \\frac{k^2-1}{3\\times\\lambda^*}$$
    $$ F_{HAM}\\sim F\\left(df_1, df_2\\right)$$
    
    With:
    $$ \\bar{y}_w^* = \\sum_{j=1}^k h_j^*\\times \\bar{x}_j$$
    $$ h_j^* = \\frac{w_j^*}{w^*}$$
    $$ w_j^* = \\frac{n_j}{s_j^2}\\times\\frac{1}{\\varphi_j}$$
    $$ w^* = \\sum_{j=1}^k w_j^*$$
    $$ \\varphi_j = \\frac{n_j + 2}{n_j + 1}$$
    $$ \\bar{x}_j = \\frac{\\sum_{j=1}^{n_j} x_{i,j}}{n_j}$$
    $$ s_j^2 = \\frac{\\sum_{i=1}^{n_j} \\left(x_{i,j} - \\bar{x}_j\\right)^2}{n_j - 1}$$
    $$ \\lambda^* = \\sum_{j=1}^k \\frac{\\left(1 - h_j^*\\right)^2}{n_j - 1}$$
    
    *Symbols used:* 
    
    * \\(k\\), for the number of categories
    * \\(x_{i,j}\\), for the i-th score in category j
    * \\(n_j\\), the sample size of category j
    * \\(\\bar{x}_j\\), the sample mean of category j
    * \\(s_j^2\\), the sample variance of the scores in category j
    * \\(w_j^*\\), the modified weight for category j
    * \\(h_j^*\\), the adjusted modified weight for category j
    * \\(df_i\\), i-th degrees of freedom
    * \\(\\varphi_j\\), the modification factor.
    
    Note that the R-library 'doex' uses \\( \\varphi_j = \\frac{n_j - 1}{n_j - 3}\\). The original article though states that these are unbalanced weights of the Welch test and in their experience, using these makes the test too conservative. In the original article they find from their simulation experience that using \\( \\varphi_j = \\frac{n_j + 2}{n_j + 1}\\) gives reliable results for small sample sizes, and a large number of populations.
    
    References
    ----------
    Hartung, J., Argaç, D., & Makambi, K. H. (2002). Small sample properties of tests on homogeneity in one-way anova and meta-analysis. *Statistical Papers, 43*(2), 197–235. doi:10.1007/s00362-002-0097-8
    
    Author
    ------
    Made by P. Stikker
    
    Companion website: https://PeterStatistics.com  
    YouTube channel: https://www.youtube.com/stikpet  
    Donations: https://www.patreon.com/bePatron?u=19398076
    
    '''
    if type(nomField) == list:
        nomField = pd.Series(nomField)
        
    if type(scaleField) == list:
        scaleField = pd.Series(scaleField)
        
    data = pd.concat([nomField, scaleField], axis=1)
    data.columns = ["category", "score"]
    
    #remove unused categories
    if categories is not None:
        data = data[data.category.isin(categories)]
    
    #Remove rows with missing values and reset index
    data = data.dropna()    
    data.reset_index()
    
    #overall n, mean and ss
    n = len(data["category"])
    m = data.score.mean()
    sst = data.score.var()*(n-1)
    
    #sample sizes, variances and means per category
    nj = data.groupby('category').count()
    sj2 = data.groupby('category').var()
    mj = data.groupby('category').mean()
    
    #number of categories
    k = len(mj)
    
    pj = (nj + 2)/(nj + 1)
    wj = nj/(pj*sj2)
    w = float(wj.sum())
    hj = wj/w
    ym = float((hj*mj).sum())
    lm = float(((1 - hj)**2/(nj - 1)).sum())
    
    fVal = float((wj*(mj - ym)**2).sum()/((k - 1) + 2*(k - 2)*1/(k+1)*lm))
    df1 = k - 1
    df2 = float((k**2 - 1)/(3*lm))
    
    pVal = f.sf(fVal, df1, df2)
    
    #results
    res = pd.DataFrame([[n, k, fVal, df1, df2, pVal]])
    res.columns = ["n", "k", "statistic", "df1", "df2", "p-value"]
    
    return res