code: def positive_part_james_stein(
      means: num_mixed, sems: num_mixed
  ) -> Tuple[np.ndarray, np.ndarray]:
      """Estimation method for Positive-part James-Stein estimator.
  
      This method takes a vector of K means (`y_i`) and standard errors
      (`sigma_i`) and calculates the positive-part James Stein estimator.
  
      Resulting estimates are the shrunk means and standard errors. The positive
      part James-Stein estimator shrinks each constituent average to the grand
      average:
  
          y_i - phi_i * y_i + phi_i * ybar
  
      The variable phi_i determines the amount of shrinkage. For `phi_i = 1`,
      `mu_hat` is equal to `ybar` (the mean of all `y_i`), while for `phi_i = 0`,
      `mu_hat` is equal to `y_i`. It can be shown that restricting `phi_i <= 1`
      dominates the unrestricted estimator, so this method restricts `phi_i` in
      this manner. The amount of shrinkage, `phi_i`, is determined by:
  
          (K - 3) * sigma2_i / s2
  
      That is, less shrinkage is applied when individual means are estimated with
      greater precision, and more shrinkage is applied when individual means are
      very tightly clustered together. We also restrict `phi_i` to never be larger
      than 1.
  
      The variance of the mean estimator is:
  
          (1 - phi_i) * sigma2_i
          + phi * sigma2_i / K
          + 2 * phi_i ** 2 * (y_i - ybar)^2 / (K - 3)
  
      The first term is the variance component from `y_i`, the second term is the
      contribution from the mean of all `y_i`, and the third term is the
      contribution from the uncertainty in the sum of squared deviations of `y_i`
      from the mean of all `y_i`.
  
      For more information, see
      https://ax.dev/docs/models.html#empirical-bayes-and-thompson-sampling.
  
      Args:
          means: Means of each arm
          sems: Standard errors of each arm
      Returns:
          mu_hat_i: Empirical Bayes estimate of each arm's mean
          sem_i: Empirical Bayes estimate of each arm's sem
      """
      if np.min(sems) < 0:
          raise ValueError("sems cannot be negative.")
      y_i = np.array(means)
      K = y_i.shape[0]
      if K < 4:
          raise ValueError(
              "Less than 4 measurements passed to positive_part_james_stein. "
              + "Returning raw estimates."
          )
      sigma2_i = np.power(sems, 2)
      ybar = np.mean(y_i)
      s2 = np.var(
          y_i - ybar, ddof=3
      )  # sample variance normalized by K-3
      if s2 == 0:
          phi_i = 1
      else:
          phi_i = np.minimum(1, sigma2_i / s2)
      mu_hat_i = y_i + phi_i * (ybar - y_i)
      sigma_hat_i = np.sqrt(
          (1 - phi_i) * sigma2_i
          + phi_i * sigma2_i / K
          + 2 * phi_i**2 * (y_i - ybar) ** 2 / (K - 3)
      )
      return mu_hat_i, sigma_hat_i
  
file_path: Ax/ax/utils/stats/statstools.py
imports: from logging import Logger
  from typing import List, Tuple, Union
  import numpy
  import pandas
  from ax.core.data import Data
  from ax.utils.common.logger import get_logger
label_desc: Use image node_id 3 for calling a global function with example usage: positive_part_james_stein(means, sems) and returns: mu_hat_i,  sigma_hat_i