code: def _linear_regression_inner(
      genotype_pdf: pd.DataFrame,
      Y_state: YState,
      Y_mask: NDArray[(Any, Any), Float],
      Y_scale: NDArray[(Any,), Float],
      Q: NDArray[(Any, Any), Float],
      dof: int,
      phenotype_names: pd.Series,
      Y_for_verbose_output: Optional[NDArray[(Any, Any), Float]],
      verbose_output: Optional[bool],
      gt_indices_to_drop: Optional[NDArray[(Any,), Int32]],
  ) -> pd.DataFrame:
      """
      Applies a linear regression model to a block of genotypes. We first project the covariates out of the
      genotype block and then perform single variate linear regression for each site.
  
      To account for samples with missing traits, we additionally accept a mask indicating which samples are missing
      for each phenotype. This mask is used to ensure that missing samples are not included when summing across individuals.
  
      Rather than use traditional matrix indices in the einsum expressions, we use semantic indices.
      s: sample
      g: genotype
      p: phenotype
  
      So, if a matrix's indices are `sg` (like the X matrix), it has one row per sample and one column per genotype.
      """
  
      genotype_values = genotype_pdf[_VALUES_COLUMN_NAME].array
      X = np.column_stack(genotype_values)
      if gt_indices_to_drop is not None and gt_indices_to_drop.size:
          X = np.delete(X, gt_indices_to_drop, axis=0)
  
      genotype_pdf.drop(_VALUES_COLUMN_NAME, axis=1, inplace=True)
      num_genotypes = genotype_pdf.shape[0]
      out_df = pd.concat([genotype_pdf] * Y_state.Y.shape[1])
      if verbose_output:
          out_df["n"] = list(np.ravel(Y_mask.T @ np.ones(X.shape)))
          out_df["sum_x"] = list(np.ravel(Y_mask.T @ X))
          out_df["y_transpose_x"] = list(
              np.ravel(Y_for_verbose_output.T @ X)
          )
      X = gwas_fx._residualize_in_place(X, Q)
  
      XdotY = Y_state.Y.T @ X
      XdotX_reciprocal = 1 / gwas_fx._einsum(
          "sp,sg,sg->pg", Y_mask, X, X
      )
      betas = XdotY * XdotX_reciprocal
      standard_error = np.sqrt(
          (Y_state.YdotY[:, None] * XdotX_reciprocal - betas * betas)
          / dof
      )
      T = betas / standard_error
      pvalues = 2 * stats.distributions.t.sf(np.abs(T), dof)
  
      Y_scale_mat = Y_scale[:, None]
      out_df["effect"] = list(np.ravel(betas * Y_scale_mat))
      out_df["stderror"] = list(np.ravel(standard_error * Y_scale_mat))
      out_df["tvalue"] = list(np.ravel(T))
      out_df["pvalue"] = list(np.ravel(pvalues))
      out_df["phenotype"] = phenotype_names.repeat(
          num_genotypes
      ).tolist()
  
      return out_df
  
file_path: glow/python/glow/gwas/lin_reg.py
imports: import pandas
  import numpy
  from nptyping import Float, NDArray, Int32
  from dataclasses import dataclass
  from typing import Any, Dict, List, Optional, Union
  from pyspark.sql import Column, DataFrame
  from pyspark.sql.types import StringType, StructField, IntegerType
  from scipy import stats
  from typeguard import typechecked
  from .None import functions
  from .functions import _VALUES_COLUMN_NAME, _get_indices_to_drop
  from ..wgr.wgr_functions import _get_contigs_from_loco_df
label_desc: Use image node_id 4 for calling a global function with example usage: _linear_regression_inner(genotype_pdf, Y_state, Y_mask, Y_scale, Q, dof, phenotype_names, Y_for_verbose_output, verbose_output, gt_indices_to_drop) and returns: out_df