code: def granger_causality(
      data: pd.DataFrame,
      test: str = "ssr_chi2test",
      addconst: bool = True,
      maxlag: int = 5,
      max_diff: int = 3,
      adf_confidence: float = 0.1,
  ) -> Tuple[pd.DataFrame, pd.DataFrame, int]:
      """Attempts to make the data stationary by applying differencing if needed (up to ``max_diff``)
      -- determined based on ADFuller test on ``adf_confidence`` with autolag calculation based on
      AIC; for each series pair, runs granger causality test and gets the smallest p-value and
      corresponding lag; builds a matrix with the result.
  
      Args:
          data (pd.DataFrame): Timeseries dataframe.
          test (str): Test name to use with Granger Causality.
          addconst (bool): Include a constant in the model.
          maxlag (int): Compute Granger Causality up til maxlag.
          max_diff (int): Diff at most max_diff times to attain stationarity.
          adf_confidence (float): Confidence level for the ADFuller test on stationarity.
  
      Returns:
          pd.DataFrame, pd.DataFrame, int: p-values matrix, lags matrix, number of differencing
  
      See Also:
          https://www.statsmodels.org/stable/generated/statsmodels.tsa.stattools.grangercausalitytests.html
      """
      # only stationary
      df_ = data.fillna(method="bfill").fillna(method="ffill")
      stationary = []
      for d in range(max_diff + 1):
          stationary = [
              sms.adfuller(df_[c], autolag="AIC")[1] < adf_confidence
              for c in df_.columns
          ]
          if all(stationary):
              break
          df_ = df_.diff().dropna()
      df_ = df_.iloc[:, [i for i, st in enumerate(stationary) if st]]
  
      dfp = pd.DataFrame(
          np.zeros((df_.shape[1], df_.shape[1])),
          columns=df_.columns,
          index=df_.columns,
      )
      dfl = pd.DataFrame(
          np.zeros((df_.shape[1], df_.shape[1])),
          columns=df_.columns,
          index=df_.columns,
      )
      for c in dfp.columns:
          for r in dfp.index:
              if c == r:
                  min_p_value = np.nan
                  min_lag = -1
              else:
                  test_result = sms.grangercausalitytests(
                      df_[[r, c]],
                      addconst=addconst,
                      maxlag=maxlag,
                      verbose=False,
                  )
                  p_values = sorted(
                      [
                          test_result[lag][0][test]
                          for lag in range(1, maxlag + 1)
                      ],
                      key=lambda v: float(v[1]),
                  )
                  _, min_p_value, min_lag = p_values[0]
              dfp.loc[r, c] = min_p_value
              dfl.loc[r, c] = min_lag
      dfp.columns = [f"{c}[x]" for c in dfp.columns]
      dfp.index = [f"{c}[y]" for c in dfp.index]
      dfl.columns = [f"{c}[x]" for c in dfl.columns]
      dfl.index = [f"{c}[y]" for c in dfl.index]
      dfl = dfl.astype(int)
      return dfp, dfl, d
  
file_path: tslumen/tslumen/profile/correlation.py
imports: from typing import Optional, Tuple
  import numpy
  import pandas
  from statsmodels.tsa import stattools
  from tslumen.profile.base import ProfilingFunction
label_desc: Use image node_id 14 for calling a global function with example usage: granger_causality(data, test, addconst, maxlag, max_diff, adf_confidence) and returns: dfp,  dfl,  d