code: def partial_corr(
      data=None,
      x=None,
      y=None,
      covar=None,
      x_covar=None,
      y_covar=None,
      alternative="two-sided",
      method="pearson",
  ):
      """Partial and semi-partial correlation.
  
      Parameters
      ----------
      data : :py:class:`pandas.DataFrame`
          Pandas Dataframe. Note that this function can also directly be used
          as a :py:class:`pandas.DataFrame` method, in which case this argument
          is no longer needed.
      x, y : string
          x and y. Must be names of columns in ``data``.
      covar : string or list
          Covariate(s). Must be a names of columns in ``data``. Use a list if
          there are two or more covariates.
      x_covar : string or list
          Covariate(s) for the ``x`` variable. This is used to compute
          semi-partial correlation (i.e. the effect of ``x_covar`` is removed
          from ``x`` but not from ``y``). Only one of ``covar``,  ``x_covar`` and
          ``y_covar`` can be specified.
      y_covar : string or list
          Covariate(s) for the ``y`` variable. This is used to compute
          semi-partial correlation (i.e. the effect of ``y_covar`` is removed
          from ``y`` but not from ``x``). Only one of ``covar``,  ``x_covar`` and
          ``y_covar`` can be specified.
      alternative : string
          Defines the alternative hypothesis, or tail of the partial correlation. Must be one of
          "two-sided" (default), "greater" or "less". Both "greater" and "less" return a one-sided
          p-value. "greater" tests against the alternative hypothesis that the partial correlation is
          positive (greater than zero), "less" tests against the hypothesis that the partial
          correlation is negative.
      method : string
          Correlation type:
  
          * ``'pearson'``: Pearson :math:`r` product-moment correlation
          * ``'spearman'``: Spearman :math:`\\rho` rank-order correlation
  
      Returns
      -------
      stats : :py:class:`pandas.DataFrame`
  
          * ``'n'``: Sample size (after removal of missing values)
          * ``'r'``: Partial correlation coefficient
          * ``'CI95'``: 95% parametric confidence intervals around :math:`r`
          * ``'p-val'``: p-value
  
      See also
      --------
      corr, pcorr, pairwise_corr, rm_corr
  
      Notes
      -----
      Partial correlation [1]_ measures the degree of association between ``x``
      and ``y``, after removing the effect of one or more controlling variables
      (``covar``, or :math:`Z`). Practically, this is achieved by calculating the
      correlation coefficient between the residuals of two linear regressions:
  
      .. math:: x \\sim Z, y \\sim Z
  
      Like the correlation coefficient, the partial correlation
      coefficient takes on a value in the range from –1 to 1, where 1 indicates a
      perfect positive association.
  
      The semipartial correlation is similar to the partial correlation,
      with the exception that the set of controlling variables is only
      removed for either ``x`` or ``y``, but not both.
  
      Pingouin uses the method described in [2]_ to calculate the (semi)partial
      correlation coefficients and associated p-values. This method is based on
      the inverse covariance matrix and is significantly faster than the
      traditional regression-based method. Results have been tested against the
      `ppcor <https://cran.r-project.org/web/packages/ppcor/index.html>`_
      R package.
  
      .. important:: Rows with missing values are automatically removed from
          data.
  
      References
      ----------
      .. [1] https://en.wikipedia.org/wiki/Partial_correlation
  
      .. [2] https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4681537/
  
      Examples
      --------
      1. Partial correlation with one covariate
  
      >>> import pingouin as pg
      >>> df = pg.read_dataset('partial_corr')
      >>> pg.partial_corr(data=df, x='x', y='y', covar='cv1').round(3)
                n      r         CI95%  p-val
      pearson  30  0.568  [0.25, 0.77]  0.001
  
      2. Spearman partial correlation with several covariates
  
      >>> # Partial correlation of x and y controlling for cv1, cv2 and cv3
      >>> pg.partial_corr(data=df, x='x', y='y', covar=['cv1', 'cv2', 'cv3'],
      ...                 method='spearman').round(3)
                 n      r         CI95%  p-val
      spearman  30  0.521  [0.18, 0.75]  0.005
  
      3. Same but one-sided test
  
      >>> pg.partial_corr(data=df, x='x', y='y', covar=['cv1', 'cv2', 'cv3'],
      ...                 alternative="greater", method='spearman').round(3)
                 n      r        CI95%  p-val
      spearman  30  0.521  [0.24, 1.0]  0.003
  
      >>> pg.partial_corr(data=df, x='x', y='y', covar=['cv1', 'cv2', 'cv3'],
      ...                 alternative="less", method='spearman').round(3)
                 n      r         CI95%  p-val
      spearman  30  0.521  [-1.0, 0.72]  0.997
  
      4. As a pandas method
  
      >>> df.partial_corr(x='x', y='y', covar=['cv1'], method='spearman').round(3)
                 n      r         CI95%  p-val
      spearman  30  0.578  [0.27, 0.78]  0.001
  
      5. Partial correlation matrix (returns only the correlation coefficients)
  
      >>> df.pcorr().round(3)
               x      y    cv1    cv2    cv3
      x    1.000  0.493 -0.095  0.130 -0.385
      y    0.493  1.000 -0.007  0.104 -0.002
      cv1 -0.095 -0.007  1.000 -0.241 -0.470
      cv2  0.130  0.104 -0.241  1.000 -0.118
      cv3 -0.385 -0.002 -0.470 -0.118  1.000
  
      6. Semi-partial correlation on x
  
      >>> pg.partial_corr(data=df, x='x', y='y', x_covar=['cv1', 'cv2', 'cv3']).round(3)
                n      r        CI95%  p-val
      pearson  30  0.463  [0.1, 0.72]  0.015
      """
      from pingouin.utils import _flatten_list
  
      # Safety check
      assert alternative in [
          "two-sided",
          "greater",
          "less",
      ], "Alternative must be one of 'two-sided' (default), 'greater' or 'less'."
      assert method in [
          "pearson",
          "spearman",
      ], 'only "pearson" and "spearman" are supported for partial correlation.'
      assert isinstance(
          data, pd.DataFrame
      ), "data must be a pandas DataFrame."
      assert data.shape[0] > 2, "Data must have at least 3 samples."
      if covar is not None and (
          x_covar is not None or y_covar is not None
      ):
          raise ValueError("Cannot specify both covar and {x,y}_covar.")
      if x_covar is not None and y_covar is not None:
          raise ValueError("Cannot specify both x_covar and y_covar.")
      assert x != y, "x and y must be independent"
      if isinstance(covar, list):
          assert x not in covar, "x and covar must be independent"
          assert y not in covar, "y and covar must be independent"
      else:
          assert x != covar, "x and covar must be independent"
          assert y != covar, "y and covar must be independent"
      # Check that columns exist
      col = _flatten_list([x, y, covar, x_covar, y_covar])
      assert all(
          [c in data for c in col]
      ), "columns are not in dataframe."
      # Check that columns are numeric
      assert all([data[c].dtype.kind in "bfiu" for c in col])
  
      # Drop rows with NaN
      data = data[col].dropna()
      n = data.shape[0]  # Number of samples
      k = data.shape[1] - 2  # Number of covariates
      assert n > 2, "Data must have at least 3 non-NAN samples."
  
      # Calculate the partial corrrelation matrix - similar to pingouin.pcorr()
      if method == "spearman":
          # Convert the data to rank, similar to R cov()
          V = data.rank(na_option="keep").cov(numeric_only=True)
      else:
          V = data.cov(numeric_only=True)
      Vi = np.linalg.pinv(
          V, hermitian=True
      )  # Inverse covariance matrix
      Vi_diag = Vi.diagonal()
      D = np.diag(np.sqrt(1 / Vi_diag))
      pcor = -1 * (D @ Vi @ D)  # Partial correlation matrix
  
      if covar is not None:
          r = pcor[0, 1]
      else:
          # Semi-partial correlation matrix
          with np.errstate(divide="ignore"):
              spcor = (
                  pcor
                  / np.sqrt(np.diag(V))[..., None]
                  / np.sqrt(
                      np.abs(Vi_diag - Vi**2 / Vi_diag[..., None])
                  ).T
              )
          if y_covar is not None:
              r = spcor[0, 1]  # y_covar is removed from y
          else:
              r = spcor[1, 0]  # x_covar is removed from x
  
      if np.isnan(r):
          # Correlation failed. Return NaN. When would this happen?
          return pd.DataFrame(
              {"n": n, "r": np.nan, "CI95%": np.nan, "p-val": np.nan},
              index=[method],
          )
  
      # Compute the two-sided p-value and confidence intervals
      # https://online.stat.psu.edu/stat505/lesson/6/6.3
      pval = _correl_pvalue(r, n, k, alternative)
      ci = compute_esci(
          stat=r,
          nx=(n - k),
          ny=(n - k),
          eftype="r",
          decimals=6,
          alternative=alternative,
      )
  
      # Create dictionnary
      stats = {
          "n": n,
          "r": r,
          "CI95%": [ci],
          "p-val": pval,
      }
  
      # Convert to DataFrame
      stats = pd.DataFrame(stats, index=[method])
  
      # Define order
      col_keep = ["n", "r", "CI95%", "p-val"]
      col_order = [k for k in col_keep if k in stats.keys().tolist()]
      return _postprocess_dataframe(stats)[col_order]
  
imports: import warnings
  import numpy
  import pandas
  import pandas_flavor
  from scipy.spatial.distance import pdist, squareform
  from scipy.stats import pearsonr, spearmanr, kendalltau
  from pingouin.config import options
  from pingouin.power import power_corr
  from pingouin.multicomp import multicomp
  from pingouin.effsize import compute_esci
  from pingouin.utils import remove_na, _perm_pval, _postprocess_dataframe
  from pingouin.bayesian import bayesfactor_pearson
label_desc: Use image node_id 8 for calling a global function with example usage: partial_corr(data, x, y, covar, x_covar, y_covar, alternative, method) and returns: _postprocess_dataframe, pd