code: def anovan(
      data=None, dv=None, between=None, ss_type=2, effsize="np2"
  ):
      """N-way ANOVA using statsmodels.
  
      This is an internal function. The main call to this function should be done
      by the :py:func:`pingouin.anova` function.
      """
      # Check that stasmodels is installed
      from pingouin.utils import _is_statsmodels_installed
  
      _is_statsmodels_installed(raise_error=True)
      from statsmodels.api import stats
      from statsmodels.formula.api import ols
  
      # Validate the dataframe
      data = _check_dataframe(
          dv=dv, between=between, data=data, effects="between"
      )
      all_cols = _flatten_list([dv, between])
      bad_chars = [",", "(", ")", ":"]
      if not all([c not in v for c in bad_chars for v in all_cols]):
          err_msg = "comma, bracket, and colon are not allowed in column names."
          raise ValueError(err_msg)
  
      # Drop missing values
      data = data[all_cols].dropna()
      assert (
          data.shape[0] >= 5
      ), "Data must have at least 5 non-missing values."
  
      # Reset index (avoid duplicate axis error)
      data = data.reset_index(drop=True)
  
      # Create R-like formula
      # https://patsy.readthedocs.io/en/latest/builtins-reference.html
      # C marks the data as categorical
      # Q allows to quote variable that do not meet Python variable name rule
      # e.g. if variable is "weight.in.kg" or "2A"
      assert dv not in ["C", "Q"], "`dv` must not be 'C' or 'Q'."
      assert all(
          fac not in ["C", "Q"] for fac in between
      ), "`between` must not contain 'C' or 'Q'."
      formula = "Q('%s') ~ " % dv
      for fac in between:
          formula += "C(Q('%s'), Sum) * " % fac
      formula = formula[:-3]  # Remove last * and space
  
      # Fit using statsmodels
      lm = ols(formula, data=data).fit()
      aov = stats.anova_lm(lm, typ=ss_type)
  
      # Convert to Pingouin-like dataframe
      if ss_type == 1:
          # statsmodels output is not exactly the same when ss_type = 1
          aov = aov[["sum_sq", "df", "F", "PR(>F)"]]
      if ss_type == 3:
          # Remove intercept row
          aov = aov.iloc[1:, :]
  
      aov = aov.reset_index()
      aov = aov.rename(
          columns={
              "index": "Source",
              "sum_sq": "SS",
              "df": "DF",
              "PR(>F)": "p-unc",
          }
      )
      aov["MS"] = aov["SS"] / aov["DF"]
  
      # Effect size
      if effsize == "n2":
          # Get standard eta-square for all effects except residuals (last)
          all_n2 = (aov["SS"] / aov["SS"].sum()).to_numpy()
          all_n2[-1] = np.nan
          aov["n2"] = all_n2
      else:
          aov["np2"] = (aov["F"] * aov["DF"]) / (
              aov["F"] * aov["DF"] + aov.iloc[-1, 2]
          )
  
      def format_source(x):
          for fac in between:
              x = x.replace("C(Q('%s'), Sum)" % fac, fac)
          return x.replace(":", " * ")
  
      aov["Source"] = aov["Source"].apply(format_source)
  
      # Re-index and round
      col_order = ["Source", "SS", "DF", "MS", "F", "p-unc", effsize]
      aov = aov.reindex(columns=col_order)
      aov.dropna(how="all", axis=1, inplace=True)
  
      # Add formula to dataframe
      aov = _postprocess_dataframe(aov)
      aov.formula_ = formula
      return aov
  
file_path: pingouin/pingouin/parametric.py
imports: import warnings
  from collections.abc import Iterable
  import numpy
  import pandas
  from scipy.stats import f
  import pandas_flavor
  from pingouin import _check_dataframe, remove_na, _flatten_list, bayesfactor_ttest, epsilon, sphericity, _postprocess_dataframe
label_desc: Use image node_id 6 for calling a global function with example usage: anovan(data, dv, between, ss_type, effsize) and returns: aov, x