code: def pairwise_corr(
      data,
      columns=None,
      covar=None,
      alternative="two-sided",
      method="pearson",
      padjust="none",
      nan_policy="pairwise",
  ):
      """Pairwise (partial) correlations between columns of a pandas dataframe.
  
      Parameters
      ----------
      data : :py:class:`pandas.DataFrame`
          DataFrame. Note that this function can also directly be used as a
          Pandas method, in which case this argument is no longer needed.
      columns : list or str
          Column names in data:
  
          * ``["a", "b", "c"]``: combination between columns a, b, and c.
          * ``["a"]``: product between a and all the other numeric columns.
          * ``[["a"], ["b", "c"]]``: product between ["a"] and ["b", "c"].
          * ``[["a", "d"], ["b", "c"]]``: product between ["a", "d"] and
            ["b", "c"].
          * ``[["a", "d"], None]``: product between ["a", "d"] and all other
            numeric columns in dataframe.
  
          If column is None, the function will return the pairwise correlation
          between the combination of all the numeric columns in data.
          See the examples section for more details on this.
      covar : None, string or list
          Covariate(s) for partial correlation. Must be one or more columns
          in data. Use a list if there are more than one covariate. If
          ``covar`` is not None, a partial correlation will be computed using
          :py:func:`pingouin.partial_corr` function.
  
          .. important:: Only ``method='pearson'`` and ``method='spearman'``
              are currently supported in partial correlation.
      alternative : string
          Defines the alternative hypothesis, or tail of the 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 correlation is
          positive (greater than zero), "less" tests against the hypothesis that the correlation is
          negative.
      method : string
          Correlation type:
  
          * ``'pearson'``: Pearson :math:`r` product-moment correlation
          * ``'spearman'``: Spearman :math:`\\rho` rank-order correlation
          * ``'kendall'``: Kendall's :math:`\\tau_B` correlation
            (for ordinal data)
          * ``'bicor'``: Biweight midcorrelation (robust)
          * ``'percbend'``: Percentage bend correlation (robust)
          * ``'shepherd'``: Shepherd's pi correlation (robust)
          * ``'skipped'``: Skipped correlation (robust)
      padjust : string
          Method used for testing and adjustment of pvalues.
  
          * ``'none'``: no correction
          * ``'bonf'``: one-step Bonferroni correction
          * ``'sidak'``: one-step Sidak correction
          * ``'holm'``: step-down method using Bonferroni adjustments
          * ``'fdr_bh'``: Benjamini/Hochberg FDR correction
          * ``'fdr_by'``: Benjamini/Yekutieli FDR correction
      nan_policy : string
          Can be ``'listwise'`` for listwise deletion of missing values
          (= complete-case analysis) or ``'pairwise'`` (default) for the more
          liberal pairwise deletion (= available-case analysis).
  
          .. versionadded:: 0.2.9
  
      Returns
      -------
      stats : :py:class:`pandas.DataFrame`
  
          * ``'X'``: Name(s) of first columns.
          * ``'Y'``: Name(s) of second columns.
          * ``'method'``: Correlation type.
          * ``'covar'``: List of specified covariate(s), only when covariates are passed.
          * ``'alternative'``: Tail of the test.
          * ``'n'``: Sample size (after removal of missing values).
          * ``'r'``: Correlation coefficients.
          * ``'CI95'``: 95% parametric confidence intervals.
          * ``'p-unc'``: Uncorrected p-values.
          * ``'p-corr'``: Corrected p-values.
          * ``'p-adjust'``: P-values correction method.
          * ``'BF10'``: Bayes Factor of the alternative hypothesis (only for Pearson correlation)
          * ``'power'``: achieved power of the test (= 1 - type II error).
  
      Notes
      -----
      Please refer to the :py:func:`pingouin.corr()` function for a description
      of the different methods. Missing values are automatically removed from the
      data using a pairwise deletion.
  
      This function is more flexible and gives a much more detailed
      output than the :py:func:`pandas.DataFrame.corr()` method (i.e. p-values,
      confidence interval, Bayes Factor...). This comes however at
      an increased computational cost. While this should not be discernible for
      a dataframe with less than 10,000 rows and/or less than 20 columns, this
      function can be slow for very large datasets.
  
      A faster alternative to get the r-values and p-values in a matrix format is
      to use the :py:func:`pingouin.rcorr` function, which works directly as a
      :py:class:`pandas.DataFrame` method (see example below).
  
      This function also works with two-dimensional multi-index columns. In this
      case, columns must be list(s) of tuple(s). Please refer to this `example
      Jupyter notebook
      <https://github.com/raphaelvallat/pingouin/blob/master/notebooks/04_Correlations.ipynb>`_
      for more details.
  
      If and only if ``covar`` is specified, this function will compute the
      pairwise partial correlation between the variables. If you are only
      interested in computing the partial correlation matrix (i.e. the raw
      pairwise partial correlation coefficient matrix, without the p-values,
      sample sizes, etc), a better alternative is to use the
      :py:func:`pingouin.pcorr` function (see example 7).
  
      Examples
      --------
      1. One-sided spearman correlation corrected for multiple comparisons
  
      >>> import pandas as pd
      >>> import pingouin as pg
      >>> pd.set_option('display.expand_frame_repr', False)
      >>> pd.set_option('display.max_columns', 20)
      >>> data = pg.read_dataset('pairwise_corr').iloc[:, 1:]
      >>> pg.pairwise_corr(data, method='spearman', alternative='greater', padjust='bonf').round(3)
                     X                  Y    method alternative    n      r         CI95%  p-unc  p-corr p-adjust  power
      0    Neuroticism       Extraversion  spearman     greater  500 -0.325  [-0.39, 1.0]  1.000   1.000     bonf  0.000
      1    Neuroticism           Openness  spearman     greater  500 -0.028   [-0.1, 1.0]  0.735   1.000     bonf  0.012
      2    Neuroticism      Agreeableness  spearman     greater  500 -0.151  [-0.22, 1.0]  1.000   1.000     bonf  0.000
      3    Neuroticism  Conscientiousness  spearman     greater  500 -0.356  [-0.42, 1.0]  1.000   1.000     bonf  0.000
      4   Extraversion           Openness  spearman     greater  500  0.243   [0.17, 1.0]  0.000   0.000     bonf  1.000
      5   Extraversion      Agreeableness  spearman     greater  500  0.062  [-0.01, 1.0]  0.083   0.832     bonf  0.398
      6   Extraversion  Conscientiousness  spearman     greater  500  0.056  [-0.02, 1.0]  0.106   1.000     bonf  0.345
      7       Openness      Agreeableness  spearman     greater  500  0.170    [0.1, 1.0]  0.000   0.001     bonf  0.985
      8       Openness  Conscientiousness  spearman     greater  500 -0.007  [-0.08, 1.0]  0.560   1.000     bonf  0.036
      9  Agreeableness  Conscientiousness  spearman     greater  500  0.161   [0.09, 1.0]  0.000   0.002     bonf  0.976
  
      2. Robust two-sided biweight midcorrelation with uncorrected p-values
  
      >>> pcor = pg.pairwise_corr(data, columns=['Openness', 'Extraversion',
      ...                                        'Neuroticism'], method='bicor')
      >>> pcor.round(3)
                    X             Y method alternative    n      r           CI95%  p-unc  power
      0      Openness  Extraversion  bicor   two-sided  500  0.247    [0.16, 0.33]  0.000  1.000
      1      Openness   Neuroticism  bicor   two-sided  500 -0.028   [-0.12, 0.06]  0.535  0.095
      2  Extraversion   Neuroticism  bicor   two-sided  500 -0.343  [-0.42, -0.26]  0.000  1.000
  
      3. One-versus-all pairwise correlations
  
      >>> pg.pairwise_corr(data, columns=['Neuroticism']).round(3)
                   X                  Y   method alternative    n      r           CI95%  p-unc       BF10  power
      0  Neuroticism       Extraversion  pearson   two-sided  500 -0.350  [-0.42, -0.27]  0.000  6.765e+12  1.000
      1  Neuroticism           Openness  pearson   two-sided  500 -0.010    [-0.1, 0.08]  0.817      0.058  0.056
      2  Neuroticism      Agreeableness  pearson   two-sided  500 -0.134  [-0.22, -0.05]  0.003      5.122  0.854
      3  Neuroticism  Conscientiousness  pearson   two-sided  500 -0.368  [-0.44, -0.29]  0.000  2.644e+14  1.000
  
      4. Pairwise correlations between two lists of columns (cartesian product)
  
      >>> columns = [['Neuroticism', 'Extraversion'], ['Openness']]
      >>> pg.pairwise_corr(data, columns).round(3)
                    X         Y   method alternative    n      r         CI95%  p-unc       BF10  power
      0   Neuroticism  Openness  pearson   two-sided  500 -0.010  [-0.1, 0.08]  0.817      0.058  0.056
      1  Extraversion  Openness  pearson   two-sided  500  0.267  [0.18, 0.35]  0.000  5.277e+06  1.000
  
      5. As a Pandas method
  
      >>> pcor = data.pairwise_corr(covar='Neuroticism', method='spearman')
  
      6. Pairwise partial correlation
  
      >>> pg.pairwise_corr(data, covar=['Neuroticism', 'Openness'])
                     X                  Y   method                        covar alternative    n         r          CI95%     p-unc
      0   Extraversion      Agreeableness  pearson  ['Neuroticism', 'Openness']   two-sided  500 -0.038737  [-0.13, 0.05]  0.388361
      1   Extraversion  Conscientiousness  pearson  ['Neuroticism', 'Openness']   two-sided  500 -0.071427  [-0.16, 0.02]  0.111389
      2  Agreeableness  Conscientiousness  pearson  ['Neuroticism', 'Openness']   two-sided  500  0.123108   [0.04, 0.21]  0.005944
  
      7. Pairwise partial correlation matrix using :py:func:`pingouin.pcorr`
  
      >>> data[['Neuroticism', 'Openness', 'Extraversion']].pcorr().round(3)
                    Neuroticism  Openness  Extraversion
      Neuroticism         1.000     0.092        -0.360
      Openness            0.092     1.000         0.281
      Extraversion       -0.360     0.281         1.000
  
      8. Correlation matrix with p-values using :py:func:`pingouin.rcorr`
  
      >>> data[['Neuroticism', 'Openness', 'Extraversion']].rcorr()
                   Neuroticism Openness Extraversion
      Neuroticism            -                   ***
      Openness           -0.01        -          ***
      Extraversion       -0.35    0.267            -
      """
      from pingouin.correlation import corr, partial_corr
  
      # Check arguments
      assert alternative in [
          "two-sided",
          "greater",
          "less",
      ], "Alternative must be one of 'two-sided' (default), 'greater' or 'less'."
      assert nan_policy in ["listwise", "pairwise"]
  
      # Keep only numeric columns
      data = data._get_numeric_data()
      # Remove columns with constant value and/or NaN
      data = data.loc[:, data.nunique(dropna=True) >= 2]
      # Extract columns names
      keys = data.columns.tolist()
  
      # First ensure that columns is a list
      if isinstance(columns, (str, tuple)):
          columns = [columns]
  
      def traverse(o, tree_types=(list, tuple)):
          """Helper function to flatten nested lists.
          From https://stackoverflow.com/a/6340578
          """
          if isinstance(o, tree_types):
              for value in o:
                  yield from traverse(value, tree_types)
          else:
              yield o
  
      # Check if columns index has multiple levels
      if isinstance(data.columns, pd.MultiIndex):
          multi_index = True
          if columns is not None:
              # Simple List with one element: [('L0', 'L1')]
              # Simple list with >= 2 elements: [('L0', 'L1'), ('L0', 'L2')]
              # Nested lists: [[('L0', 'L1')], ...] or [..., [('L0', 'L1')]]
              col_flatten = list(traverse(columns, tree_types=list))
              assert all(
                  isinstance(c, (tuple, type(None)))
                  for c in col_flatten
              )
      else:
          multi_index = False
  
      # Then define combinations / products between columns
      if columns is None:
          # Case A: column is not defined --> corr between all numeric columns
          combs = list(combinations(keys, 2))
      else:
          # Case B: column is specified
          if isinstance(columns[0], (list, np.ndarray)):
              group1 = [e for e in columns[0] if e in keys]
              # Assert that column is two-dimensional
              if len(columns) == 1:
                  columns.append(None)
              if isinstance(columns[1], (list, np.ndarray)) and len(
                  columns[1]
              ):
                  # B1: [['a', 'b'], ['c', 'd']]
                  group2 = [e for e in columns[1] if e in keys]
              else:
                  # B2: [['a', 'b']], [['a', 'b'], None] or [['a', 'b'], 'all']
                  group2 = [e for e in keys if e not in group1]
              combs = list(product(group1, group2))
          else:
              # Column is a simple list
              if len(columns) == 1:
                  # Case B3: one-versus-all, e.g. ['a'] or 'a'
                  # Check that this column exist
                  if columns[0] not in keys:
                      msg = (
                          '"%s" is not in data or is not numeric.'
                          % columns[0]
                      )
                      raise ValueError(msg)
                  others = [e for e in keys if e != columns[0]]
                  combs = list(product(columns, others))
              else:
                  # Combinations between all specified columns ['a', 'b', 'c']
                  # Make sure that we keep numeric columns
                  columns = [c for c in columns if c in keys]
                  if len(columns) == 1:
                      # If only one-column is left, equivalent to ['a']
                      others = [e for e in keys if e != columns[0]]
                      combs = list(product(columns, others))
                  else:
                      # combinations between ['a', 'b', 'c']
                      combs = list(combinations(columns, 2))
  
      combs = np.array(combs)
      if len(combs) == 0:
          raise ValueError(
              "No column combination found. Please make sure that "
              "the specified columns exist in the dataframe, are "
              "numeric, and contains at least two unique values."
          )
  
      # Initialize empty dataframe
      if multi_index:
          X = list(zip(combs[:, 0, 0], combs[:, 0, 1]))
          Y = list(zip(combs[:, 1, 0], combs[:, 1, 1]))
      else:
          X = combs[:, 0]
          Y = combs[:, 1]
      stats = pd.DataFrame(
          {
              "X": X,
              "Y": Y,
              "method": method,
              "alternative": alternative,
          },
          index=range(len(combs)),
          columns=[
              "X",
              "Y",
              "method",
              "alternative",
              "n",
              "outliers",
              "r",
              "CI95%",
              "p-val",
              "BF10",
              "power",
          ],
      )
  
      # Now we check if covariates are present
      if covar is not None:
          assert isinstance(
              covar, (str, list, pd.Index)
          ), "covar must be list or string."
          if isinstance(covar, str):
              covar = [covar]
          elif isinstance(covar, pd.Index):
              covar = covar.tolist()
          # Check that columns exist and are numeric
          assert all(
              [c in keys for c in covar]
          ), "Covariate(s) are either not in data or not numeric."
          # And we make sure that X or Y does not contain covar
          stats = stats[~stats[["X", "Y"]].isin(covar).any(axis=1)]
          stats = stats.reset_index(drop=True)
          if stats.shape[0] == 0:
              raise ValueError(
                  "No column combination found. Please make sure "
                  "that the specified columns and covar exist in "
                  "the dataframe, are numeric, and contains at "
                  "least two unique values."
              )
  
      # Listwise deletion of missing values
      if nan_policy == "listwise":
          all_cols = np.unique(stats[["X", "Y"]].to_numpy()).tolist()
          if covar is not None:
              all_cols.extend(covar)
          data = data[all_cols].dropna()
  
      # For max precision, make sure rounding is disabled
      old_options = options.copy()
      options["round"] = None
  
      # Compute pairwise correlations and fill dataframe
      for i in range(stats.shape[0]):
          col1, col2 = stats.at[i, "X"], stats.at[i, "Y"]
          if covar is None:
              cor_st = corr(
                  data[col1].to_numpy(),
                  data[col2].to_numpy(),
                  alternative=alternative,
                  method=method,
              )
          else:
              cor_st = partial_corr(
                  data=data,
                  x=col1,
                  y=col2,
                  covar=covar,
                  alternative=alternative,
                  method=method,
              )
          cor_st_keys = cor_st.columns.tolist()
  
          for c in cor_st_keys:
              stats.at[i, c] = cor_st.at[method, c]
  
      options.update(old_options)  # restore options
  
      # Force conversion to numeric
      stats = stats.astype(
          {
              "r": float,
              "n": int,
              "p-val": float,
              "outliers": float,
              "power": float,
          }
      )
  
      # Multiple comparisons
      stats = stats.rename(columns={"p-val": "p-unc"})
      padjust = None if stats["p-unc"].size <= 1 else padjust
      if padjust is not None:
          if padjust.lower() != "none":
              reject, stats["p-corr"] = multicomp(
                  stats["p-unc"].to_numpy(), method=padjust
              )
              stats["p-adjust"] = padjust
      else:
          stats["p-corr"] = None
          stats["p-adjust"] = None
  
      # Standardize correlation coefficients (Fisher z-transformation)
      # stats['z'] = np.arctanh(stats['r'].to_numpy())
  
      col_order = [
          "X",
          "Y",
          "method",
          "alternative",
          "n",
          "outliers",
          "r",
          "CI95%",
          "p-unc",
          "p-corr",
          "p-adjust",
          "BF10",
          "power",
      ]
  
      # Reorder columns and remove empty ones
      stats = stats.reindex(columns=col_order).dropna(how="all", axis=1)
  
      # Add covariates names if present
      if covar is not None:
          stats.insert(loc=3, column="covar", value=str(covar))
  
      return _postprocess_dataframe(stats)
  
file_path: pingouin/pingouin/pairwise.py
imports: import numpy
  import pandas
  import pandas_flavor
  from itertools import combinations, product
  from pingouin.config import options
  from pingouin.parametric import anova
  from pingouin.multicomp import multicomp
  from pingouin.effsize import compute_effsize
  from pingouin.utils import _check_dataframe, _flatten_list, _postprocess_dataframe
  from scipy.stats import studentized_range
  import warnings
label_desc: Use image node_id 6 for calling a global function with example usage: pairwise_corr(data, columns, covar, alternative, method, padjust, nan_policy) and returns: _postprocess_dataframe