Skip to content

Application


๐Ÿงญ Purpose

The app module is the interactive entry point for Sake. It should coordinate the Streamlit interface, data upload flow, workflow selection, statistical analysis controls, feature-engineering options, model-training actions, evaluation output, and visualization rendering.

This page documents the application-facing layer and should remain focused on orchestration rather than duplicating business logic that belongs in data, statistics, features, models, evaluation, or visualization modules.

๐Ÿงฑ Workflow Role

Area Responsibility
Page setup Configure page layout, title, sidebar controls, and user-facing workflow sections.
Upload handling Accept Account Balances, CSV, Excel, or notebook-provided datasets.
Workflow routing Route user choices to statistics, features, models, evaluation, and visualization modules.
State management Preserve uploaded data, selected options, transformed features, trained models, and generated outputs.
Output rendering Present previews, metrics, diagnostic plots, warnings, and model-comparison results.

๐Ÿ”„ Application Flow

User Input -> Streamlit Controls -> Workflow Routing -> Analytical Module Calls -> Rendered Outputs

โœ… Implementation Expectations

  • Keep reusable analytical logic outside the UI layer.
  • Avoid unsafe module-level execution that prevents mkdocstrings from importing app.
  • Initialize session-state keys before they are read.
  • Use stable labels for Account Balances, budget execution, model type, target field, and evaluation output.
  • Keep plotting and metric calculations in dedicated modules when possible.

๐Ÿงช Validation Checklist

Check Purpose
python -m compileall . Confirm the application module and dependencies compile.
python -c "import app" Confirm mkdocstrings can import the module.
streamlit run app.py Confirm the interactive interface launches.
mkdocs build Confirm the API page renders without import or griffe errors.

๐Ÿ“š Source Reference

app

styled_scatter

styled_scatter(
    ax: Axes,
    x: ndarray,
    y: ndarray,
    series_index: int = 0,
    label: Optional[str] = None,
    size: int = 30,
) -> None

Purpose:


Draw a consistently styled scatter plot with clear point boundaries and visually distinct series.

Parameters:


ax : plt.Axes Matplotlib axes to draw on. x : np.ndarray X-coordinates of the points. y : np.ndarray Y-coordinates of the points. series_index : int, optional Index used to pick color and marker from predefined palettes. label : Optional[str], optional Legend label for the series, if any. size : int, optional Marker size for the scatter plot.

Returns:


None This function draws on the provided axes in-place.

Source code in app.py
def styled_scatter( ax: plt.Axes, x: np.ndarray, y: np.ndarray,  series_index: int = 0,
    label: Optional[str] = None,  size: int = 30, ) -> None:
    """

	    Purpose:
	    ________
	    Draw a consistently styled scatter plot with clear point boundaries and
	    visually distinct series.

	    Parameters:
	    ___________
	    ax : plt.Axes
	        Matplotlib axes to draw on.
	    x : np.ndarray
	        X-coordinates of the points.
	    y : np.ndarray
	        Y-coordinates of the points.
	    series_index : int, optional
	        Index used to pick color and marker from predefined palettes.
	    label : Optional[str], optional
	        Legend label for the series, if any.
	    size : int, optional
	        Marker size for the scatter plot.

	    Returns:
	    ________
	    None
	        This function draws on the provided axes in-place.

    """
    color = cfg.PALETTE[series_index % len(cfg.PALETTE)]
    marker = cfg.MARKERS[series_index % len(cfg.MARKERS)]
    ax.scatter( x,  y, s=size, alpha=0.9,  edgecolors="#020617",
        linewidths=0.6,  c=[color],  marker=marker,  label=label,  )
    ax.grid(True, alpha=0.25)

auto_float_format

auto_float_format(
    series: Series, max_decimals: int = 4
) -> str

Purpose:


Infer a reasonable float formatting pattern for a numeric series based on its scale, so large values are readable and decimals are not excessive.

Parameters:


series : pd.Series Series whose numeric magnitude is used to pick the format. max_decimals : int, optional Maximum number of decimal places allowed in the format string.

Returns:


str A Python format string such as '{:,.2f}' appropriate for the series.

Source code in app.py
def auto_float_format(series: pd.Series, max_decimals: int = 4) -> str:
    """

	    Purpose:
	    ________
	    Infer a reasonable float formatting pattern for a numeric series based on
	    its scale, so large values are readable and decimals are not excessive.

	    Parameters:
	    ___________
	    series : pd.Series
	        Series whose numeric magnitude is used to pick the format.
	    max_decimals : int, optional
	        Maximum number of decimal places allowed in the format string.

	    Returns:
	    ________
	    str
	        A Python format string such as '{:,.2f}' appropriate for the series.

    """
    s = pd.to_numeric(series, errors="coerce")
    s = s.replace([np.inf, -np.inf], np.nan).dropna()
    if s.empty:
        return "{:,.2f}"

    mag = float(np.nanpercentile(np.abs(s.values), 95))

    if mag >= 1e9:
        decimals = 0
    elif mag >= 1e6:
        decimals = 1
    elif mag >= 1e3:
        decimals = 2
    elif mag >= 1:
        decimals = 3
    else:
        decimals = 4

    decimals = min(decimals, max_decimals)
    return f"{{:,.{decimals}f}}"

render_table

render_table(
    df: DataFrame,
    title: Optional[str] = None,
    caption: Optional[str] = None,
    precision: int = 4,
    dark_mode: bool = True,
    max_rows: int = 500,
) -> None

Purpose:


Render a pandas DataFrame as a styled HTML table suitable for Streamlit, avoiding raw JSON output.

Parameters:


df : pd.DataFrame DataFrame to render. title : Optional[str], optional Optional section title to display above the table. caption : Optional[str], optional Descriptive caption explaining the table content. precision : int, optional Maximum decimal precision for numeric columns. dark_mode : bool, optional If True, use dark-themed table colors; otherwise light theme. max_rows : int, optional Maximum number of rows to display; additional rows are truncated.

Returns:


None The function writes directly to the active Streamlit app.

Source code in app.py
def render_table( df: pd.DataFrame,  title: Optional[str] = None,  caption: Optional[str] = None,
    precision: int = 4, dark_mode: bool = True, max_rows: int = 500, ) -> None:
    """

	    Purpose:
	    ________
	    Render a pandas DataFrame as a styled HTML table suitable for Streamlit,
	    avoiding raw JSON output.

	    Parameters:
	    ___________
	    df : pd.DataFrame
	        DataFrame to render.
	    title : Optional[str], optional
	        Optional section title to display above the table.
	    caption : Optional[str], optional
	        Descriptive caption explaining the table content.
	    precision : int, optional
	        Maximum decimal precision for numeric columns.
	    dark_mode : bool, optional
	        If True, use dark-themed table colors; otherwise light theme.
	    max_rows : int, optional
	        Maximum number of rows to display; additional rows are truncated.

	    Returns:
	    ________
	    None
	        The function writes directly to the active Streamlit app.

    """
    if title:
        st.markdown(f'#### {title}')

    if df is None or df.empty:
        st.info("No data to display.")
        return

    df_show = df.copy()
    if len(df_show) > max_rows:
        df_show = df_show.head(max_rows)

    num_cols = df_show.select_dtypes(include=[np.number]).columns.tolist()
    fmt = {c: auto_float_format(df_show[c], precision) for c in num_cols}

    if dark_mode:
        text = '#F9FAFB'
        header_bg = '#1F2937'
        row_even = '#020617'
        row_odd = '#030712'
        border = '#374151'
    else:
        text = '#111827'
        header_bg = '#E5E7EB'
        row_even = '#FFFFFF'
        row_odd = '#F9FAFB'
        border = '#D1D5DB'

    styler = (
        df_show.style
        .format(fmt)
        .set_table_styles(
            [
                {
                    'selector': 'table',
                    'props': [
                        ('border-collapse', 'collapse'),
                        ('width', '100%'),
                        ('font-size', '0.85rem'),
                    ],
                },
                {
                    'selector': 'th',
                    'props': [
                        ('background-color', header_bg),
                        ('color', text),
                        ('border', f'1px solid {border}'),
                        ('padding', '6px 8px'),
                        ('font-weight', '600'),
                        ('text-align', 'left'),
                        ('white-space', 'nowrap'),
                    ],
                },
                {
                    'selector': 'td',
                    'props': [
                        ('color', text),
                        ('border', f'1px solid {border}'),
                        ('padding', '4px 8px'),
                        ('white-space', 'nowrap'),
                    ],
                },
                {
                    'selector': 'tr:nth-child(even) td',
                    'props': [('background-color', row_even)],
                },
                {
                    'selector': 'tr:nth-child(odd) td',
                    'props': [('background-color', row_odd)],
                },
            ]
        )
    )
    st.markdown(styler.to_html(), unsafe_allow_html=True)

    if caption:
        st.caption(caption)

safe_numeric_series

safe_numeric_series(df: DataFrame, col: str) -> np.ndarray

Purpose:


Convert a DataFrame column to a clean numeric NumPy array, dropping any non-numeric or missing values.

Parameters:


df : pd.DataFrame Source DataFrame containing the column. col : str Name of the column to convert.

Returns:


np.ndarray One-dimensional array of float values with NaNs removed.

Source code in app.py
def safe_numeric_series(df: pd.DataFrame, col: str) -> np.ndarray:
    """

	    Purpose:
	    ________
	    Convert a DataFrame column to a clean numeric NumPy array, dropping any
	    non-numeric or missing values.

	    Parameters:
	    ___________
	    df : pd.DataFrame
	        Source DataFrame containing the column.
	    col : str
	        Name of the column to convert.

	    Returns:
	    ________
	    np.ndarray
	        One-dimensional array of float values with NaNs removed.

    """
    v = pd.to_numeric(df[col], errors="coerce").dropna().values.astype(float)
    return v

descriptive_profile

descriptive_profile(
    df: DataFrame, cols: List[str]
) -> pd.DataFrame

Purpose:


Compute an extended descriptive statistics profile for a set of numeric columns, including tails, dispersion measures, and simple outlier rates.

Parameters:


df : pd.DataFrame DataFrame containing the numeric columns. cols : List[str] List of column names to profile.

Returns:


pd.DataFrame DataFrame with one row per feature and many descriptive statistics columns (mean, std, quantiles, skew, kurtosis, outlier rates, etc.).

Source code in app.py
def descriptive_profile(df: pd.DataFrame, cols: List[str]) -> pd.DataFrame:
    """

	    Purpose:
	    ________
	    Compute an extended descriptive statistics profile for a set of numeric
	    columns, including tails, dispersion measures, and simple outlier rates.

	    Parameters:
	    ___________
	    df : pd.DataFrame
	        DataFrame containing the numeric columns.
	    cols : List[str]
	        List of column names to profile.

	    Returns:
	    ________
	    pd.DataFrame
	        DataFrame with one row per feature and many descriptive statistics
	        columns (mean, std, quantiles, skew, kurtosis, outlier rates, etc.).

    """
    rows: List[Dict[str, Any]] = []
    n = df.shape[0]
    percentiles = [0, 1, 5, 10, 25, 50, 75, 90, 95, 99, 100]

    for c in cols:
        v = safe_numeric_series(df, c)
        non_missing = int(np.isfinite(v).sum())
        missing = int(n - non_missing)
        if non_missing == 0:
            continue

        q_vals = np.nanpercentile(v, percentiles)
        q = dict(zip(percentiles, q_vals))

        mean = float(np.nanmean(v))
        std = float(np.nanstd(v, ddof=0))
        var = float(np.nanvar(v, ddof=0))
        med = float(np.nanmedian(v))
        mad = float(np.nanmedian(np.abs(v - med)))
        iqr = float(q[75] - q[25])
        rng = float(q[100] - q[0])

        skew = float(stats.skew(v)) if v.size >= 3 else 0.0
        kurt = float(stats.kurtosis(v)) if v.size >= 4 else 0.0
        zero_pct = float((v == 0).mean() * 100.0)

        lo = q[25] - 1.5 * iqr
        hi = q[75] + 1.5 * iqr
        out_iqr = float(((v < lo) | (v > hi)).mean() * 100.0)

        z = (v - mean) / (std + 1e-12)
        out_z3 = float((np.abs(z) > 3.0).mean() * 100.0)

        normal_p: Optional[float] = None
        try:
            if 8 <= v.size <= 5000:
                _, p = stats.shapiro(v)
                normal_p = float(p)
            elif v.size > 5000:
                _, p = stats.normaltest(v[:5000])
                normal_p = float(p)
            elif v.size >= 8:
                _, p = stats.normaltest(v)
                normal_p = float(p)
        except Exception:
            normal_p = None

        rows.append(
            {
                "feature": c,
                "count": int(v.size),
                "missing_pct": float((missing / n) * 100.0) if n else 0.0,
                "mean": mean,
                "std": std,
                "var": var,
                "min": float(q[0]),
                "p01": float(q[1]),
                "p05": float(q[5]),
                "p10": float(q[10]),
                "q1": float(q[25]),
                "median": float(q[50]),
                "q3": float(q[75]),
                "p90": float(q[90]),
                "p95": float(q[95]),
                "p99": float(q[99]),
                "max": float(q[100]),
                "iqr": iqr,
                "range": rng,
                "mad": mad,
                "skew": skew,
                "kurtosis": kurt,
                "zero_pct": zero_pct,
                "outlier_iqr_pct": out_iqr,
                "outlier_z3_pct": out_z3,
                "normality_p": normal_p,
            }
        )

    out = pd.DataFrame(rows)
    if out.empty:
        return out
    return out.sort_values(
        ["missing_pct", "outlier_iqr_pct"], ascending=[True, False]
    )

feature_quality

feature_quality(df: DataFrame) -> pd.DataFrame

Purpose:


Summarize data quality signals for each column, such as completeness, uniqueness, and simple variance/entropy indicators.

Parameters:


df : pd.DataFrame DataFrame whose columns are to be evaluated.

Returns:


pd.DataFrame DataFrame with one row per column, including completeness percentage, unique counts, cardinality ratio, and variance/entropy metrics.

Source code in app.py
def feature_quality(df: pd.DataFrame) -> pd.DataFrame:
    """

	    Purpose:
	    ________
	    Summarize data quality signals for each column, such as completeness,
	    uniqueness, and simple variance/entropy indicators.

	    Parameters:
	    ___________
	    df : pd.DataFrame
	        DataFrame whose columns are to be evaluated.

	    Returns:
	    ________
	    pd.DataFrame
	        DataFrame with one row per column, including completeness percentage,
	        unique counts, cardinality ratio, and variance/entropy metrics.

    """
    rows: List[Dict[str, Any]] = []
    n = df.shape[0]
    for c in df.columns:
        s = df[c]
        non_missing = int(s.notna().sum())
        completeness = float((non_missing / n) * 100.0) if n else 0.0
        uniq = int(s.nunique(dropna=True))
        card_ratio = float(uniq / non_missing) if non_missing else 0.0

        variance = np.nan
        entropy = np.nan

        if pd.api.types.is_numeric_dtype(s):
            v = pd.to_numeric(s, errors="coerce").dropna().values.astype(float)
            variance = float(np.var(v)) if v.size else np.nan
        else:
            vc = s.dropna().astype(str).value_counts(normalize=True)
            entropy = float(stats.entropy(vc.values)) if vc.size > 1 else 0.0

        rows.append(
            {
                "feature": c,
                "dtype": str(s.dtype),
                "completeness_pct": completeness,
                "unique_values": uniq,
                "cardinality_ratio": card_ratio,
                "variance": variance,
                "entropy": entropy,
            }
        )

    out = pd.DataFrame(rows)
    return out.sort_values(
        ["completeness_pct", "cardinality_ratio"], ascending=[False, False]
    )

corr_with_pvalues

corr_with_pvalues(
    df: DataFrame, cols: List[str], method: str
) -> Tuple[pd.DataFrame, pd.DataFrame]

Purpose:


Compute a correlation matrix and the corresponding matrix of p-values for a set of numeric columns using Pearson or Spearman correlation.

Parameters:


df : pd.DataFrame DataFrame containing the columns of interest. cols : List[str] List of column names to include in the correlation computation. method : str Correlation type; either 'pearson' or 'spearman'.

Returns:


Tuple[pd.DataFrame, pd.DataFrame] Two DataFrames with identical indices/columns: - correlation coefficients - p-values for the corresponding tests.

Source code in app.py
def corr_with_pvalues( df: pd.DataFrame, cols: List[str], 
		method: str ) -> Tuple[pd.DataFrame, pd.DataFrame]:
    """

	    Purpose:
	    ________
	    Compute a correlation matrix and the corresponding matrix of p-values for
	    a set of numeric columns using Pearson or Spearman correlation.

	    Parameters:
	    ___________
	    df : pd.DataFrame
	        DataFrame containing the columns of interest.
	    cols : List[str]
	        List of column names to include in the correlation computation.
	    method : str
	        Correlation type; either 'pearson' or 'spearman'.

	    Returns:
	    ________
	    Tuple[pd.DataFrame, pd.DataFrame]
	        Two DataFrames with identical indices/columns:
	        - correlation coefficients
	        - p-values for the corresponding tests.

    """
    corr = pd.DataFrame(index=cols, columns=cols, dtype=float)
    pval = pd.DataFrame(index=cols, columns=cols, dtype=float)

    for i, a in enumerate(cols):
        for j, b in enumerate(cols):
            if j < i:
                corr.loc[a, b] = corr.loc[b, a]
                pval.loc[a, b] = pval.loc[b, a]
                continue

            x = pd.to_numeric(df[a], errors="coerce")
            y = pd.to_numeric(df[b], errors="coerce")
            m = x.notna() & y.notna()

            if int(m.sum()) < 3:
                r, p = np.nan, np.nan
            else:
                if method == "pearson":
                    r, p = stats.pearsonr(x[m].values, y[m].values)
                else:
                    r, p = stats.spearmanr(x[m].values, y[m].values)

            corr.loc[a, b] = float(r)
            pval.loc[a, b] = float(p)

    return corr, pval

numeric_default_candidates

numeric_default_candidates(
    df: DataFrame, numeric_cols: List[str]
) -> List[str]

Purpose:


Identify numeric columns that are likely to be measures (not IDs or codes) and should be pre-selected in numeric analyses.

Parameters:


df : pd.DataFrame Source DataFrame containing the numeric columns. numeric_cols : List[str] List of all numeric column names inferred from dtypes.

Returns:


List[str] Subset of numeric column names to use as sensible defaults.

Source code in app.py
def numeric_default_candidates( df: pd.DataFrame, numeric_cols: List[str] ) -> List[str]:
    """

	    Purpose:
	    ________
	    Identify numeric columns that are likely to be measures (not IDs or codes)
	    and should be pre-selected in numeric analyses.

	    Parameters:
	    ___________
	    df : pd.DataFrame
	        Source DataFrame containing the numeric columns.
	    numeric_cols : List[str]
	        List of all numeric column names inferred from dtypes.

	    Returns:
	    ________
	    List[str]
	        Subset of numeric column names to use as sensible defaults.

    """
    defaults: List[str] = []
    for col in numeric_cols:
        name = col.lower()

        s_num = pd.to_numeric(df[col], errors="coerce")
        v = s_num.dropna().values
        if v.size == 0:
            continue

        # Exclude pure integer-valued columns from defaults.
        if np.all(np.isfinite(v)) and np.all(np.floor(v) == v):
            continue

        # Skip obvious IDs/codes based on name.
        if name.endswith("id") or name.endswith("_id") or "code" in name:
            continue

        # High-cardinality near-unique numeric looks like row IDs.
        non_missing = int(np.isfinite(v).sum())
        if non_missing == 0:
            continue
        uniq = len(np.unique(v))
        ratio = uniq / max(non_missing, 1)

        if non_missing >= 50 and ratio > 0.98:
            continue

        defaults.append(col)

    if not defaults:
        defaults = numeric_cols[:]
    return defaults

categorical_default_candidates

categorical_default_candidates(
    df: DataFrame, cat_cols: List[str]
) -> List[str]

Purpose:


Identify categorical columns that behave like true categories (not near-unique IDs) and are good candidates for pre-selection.

Parameters:


df : pd.DataFrame Source DataFrame containing the categorical columns. cat_cols : List[str] List of column names treated as categorical.

Returns:


List[str] Subset of categorical column names that have reasonable cardinality and should be pre-selected by default.

Source code in app.py
def categorical_default_candidates( df: pd.DataFrame, cat_cols: List[str] ) -> List[str]:
    """

	    Purpose:
	    ________
	    Identify categorical columns that behave like true categories (not
	    near-unique IDs) and are good candidates for pre-selection.

	    Parameters:
	    ___________
	    df : pd.DataFrame
	        Source DataFrame containing the categorical columns.
	    cat_cols : List[str]
	        List of column names treated as categorical.

	    Returns:
	    ________
	    List[str]
	        Subset of categorical column names that have reasonable cardinality
	        and should be pre-selected by default.

    """
    defaults: List[str] = []
    for col in cat_cols:
        s = df[col].astype(str)
        non_missing = int(s.notna().sum())
        if non_missing == 0:
            continue
        uniq = int(s.nunique(dropna=True))
        ratio = uniq / max(non_missing, 1)

        if ratio <= 0.5:
            defaults.append(col)

    if not defaults:
        defaults = cat_cols[:]
    return defaults

style_subheaders

style_subheaders() -> None

Purpose:


Sets the style of subheaders in the main UI

Source code in app.py
def style_subheaders( ) -> None:
	"""

		Purpose:
		_________
		Sets the style of subheaders in the main UI

	"""
	st.markdown(
		"""
		<style>
		div[data-testid="stMarkdownContainer"] h2,
		div[data-testid="stMarkdownContainer"] h3,
		div[data-testid="stChatMessage"] div[data-testid="stMarkdownContainer"] h2,
		div[data-testid="stChatMessage"] div[data-testid="stMarkdownContainer"] h3 {
			color: rgb(0, 120, 252) !important;
		}
		</style>
		""",
		unsafe_allow_html=True,
	)