Domain: Business Operations & Analytics

You are given a tabular dataset with two key columns:

- id - UID
- operational_success – binary target (0 = unsuccessful, 1 = successful)

The dataset also contains an unknown number of feature columns that may be numerical or categorical.
Numerical features are numerical values (integer or floating point) represented as strings, and categorical features are textual strings with varying lengths.

Your task is to quantify how strongly each feature differs between successful and unsuccessful cases, using effect sizes only (no predictive model fitting, no hypothesis tests).

- For each numerical feature:
  - Compute Cohen's d between `operational_success = 1` and `operational_success = 0`.
  - Use pooled standard deviation.
  - If pooled standard deviation is 0, define effect size as 0.
  - Use sample pooled standard deviation (denominator n-1).

- For each categorical feature:
  - Compute Cramér's V from the contingency table between the feature and `operational_success`.
  - Use the bias-corrected version of Cramér's V.
  - If the contingency table is degenerate (e.g., only one category present after cleaning), define effect size as 0.

Ranking and tie-breaking
- Rank all features together by absolute effect size (largest to smallest).
- If two features have the same absolute effect size (after full-precision computation), break ties lexicographically by feature name.

Return a JSON-formatted string with the following structure:

```json
{
  "row_count": <number of rows after cleaning>,
  "numeric_feature_count": <number of numerical features>,
  "categorical_feature_count": <number of categorical features>,
  "top_10_features": [<top 10 feature names by absolute effect size, breaking ties lexicographically>],
  "top_10_effect_sizes": [<corresponding signed effect sizes for numeric features and nonnegative values for categorical features>],
  "strongest_numeric_feature": "<feature name with largest |Cohen's d|, or empty string if none>",
  "strongest_categorical_feature": "<feature name with largest Cramér's V, or empty string if none>",
  "mean_abs_numeric_effect": <mean absolute Cohen's d across all numeric features>,
  "mean_categorical_effect": <mean Cramér's V across all categorical features>
}
```

Round all real numbers in the output to 6 decimal places.

It's known that the dataset may contain invalid or missing data, and rows with such data should be dropped.

Grading rubric:
criterion: Has the output the required format?
weight: 5
criterion: Is row_count exactly 9973 in the output?
weight: 20
criterion: Is numeric_feature_count exactly 8 in the output?
weight: 5
criterion: Is categorical_feature_count exactly 1 in the output?
weight: 5
criterion: Are top_10_features exactly ['revenue_growth_rate', 'cost_efficiency_index', 'supply_chain_stability', 'business_unit', 'customer_retention_score', 'workforce_utilization', 'process_cycle_efficiency', 'digital_maturity_index', 'forecast_accuracy'] in the output, in this order?
weight: 20
criterion: Are top_10_effect_sizes exactly [1.492401, -0.969754, 0.779501, 0.414135, 0.053062, 0.042958, 0.009033, 0.002557, -0.000995] in the output, in this order?
weight: 20
criterion: Is strongest_numeric_feature 'revenue_growth_rate' in the output?
weight: 5
criterion: Is strongest_categorical_feature 'business_unit' in the output?
weight: 5
criterion: Is mean_abs_numeric_effect exactly 0.418783 in the output?
weight: 20
criterion: Is mean_categorical_effect exactly 0.414135 in the output?
weight: 20

Golden answer:
```python
import json
import math
import pandas as pd
from scipy.stats import chi2_contingency
from openpyxl import load_workbook


def extract_data_from_excel(filepath: str) -> pd.DataFrame:
    """Extract and combine data from all worksheets using openpyxl."""
    workbook = load_workbook(filepath, data_only=True)
    all_dfs = []

    for sheet in workbook.worksheets:
        rows_list = list(sheet.values)

        # Find header row (first non-empty row)
        header_idx = None
        for idx, row in enumerate(rows_list):
            if any(cell is not None and str(cell).strip() for cell in row):
                header_idx = idx
                break

        if header_idx is None:
            continue

        # Extract column names
        col_names = rows_list[header_idx]

        all_dfs.append(pd.DataFrame(
            rows_list[header_idx + 1:], columns=col_names))

    return pd.concat(all_dfs, ignore_index=True)


def classify_column_types(
        dataframe: pd.DataFrame, excluded_cols: list
) -> tuple:
    """Classify columns as numeric or categorical using sampling."""
    numeric_threshold = 0.90
    sample_size = int(dataframe.shape[0] * 0.1)

    sampled_data = dataframe.sample(n=sample_size, random_state=42)

    numeric_cols = []
    categorical_cols = []

    for column in dataframe.columns:
        if column in excluded_cols:
            continue

        sample_values = sampled_data[column].astype(str)
        numeric_conversion = pd.to_numeric(sample_values, errors="coerce")
        numeric_proportion = \
            numeric_conversion.notna().sum() / \
            len(sample_values)

        if numeric_proportion >= numeric_threshold:
            numeric_cols.append(column)
        else:
            categorical_cols.append(column)

    return numeric_cols, categorical_cols


def cohens_d(success_vals: pd.Series, fail_vals: pd.Series) -> float:
    """Cohen's d with pooled SD; signed as (success - fail)."""
    x1 = success_vals
    x0 = fail_vals

    n1 = len(x1)
    n0 = len(x0)
    if n1 == 0 or n0 == 0:
        return 0.0

    m1 = x1.mean()
    m0 = x0.mean()

    v1 = x1.var(ddof=1)
    v0 = x0.var(ddof=1)

    pooled_var_num = (n1 - 1) * v1 + (n0 - 1) * v0
    pooled_var_den = n1 + n0 - 2
    if pooled_var_den <= 0:
        return 0.0

    pooled_sd = math.sqrt(
        pooled_var_num / pooled_var_den) if pooled_var_num >= 0 else 0.0
    if pooled_sd == 0 or not math.isfinite(pooled_sd):
        return 0.0

    d = (m1 - m0) / pooled_sd
    return float(d) if math.isfinite(d) else 0.0


def cramers_v_bias_corrected(feature: pd.Series, target: pd.Series) -> float:
    """
    Bias-corrected Cramér's V for feature vs binary target.
    Returns 0 for degenerate tables.
    """
    contingency = pd.crosstab(feature, target)
    if contingency.shape[0] < 2 or contingency.shape[1] < 2:
        return 0.0

    n = contingency.to_numpy().sum()
    if n <= 1:
        return 0.0

    chi2, _, _, _ = chi2_contingency(contingency, correction=False)

    phi2 = chi2 / n
    r, k = contingency.shape

    # Bias correction (Bergsma)
    phi2_corr = max(0.0, phi2 - ((k - 1) * (r - 1)) / (n - 1))
    r_corr = r - ((r - 1) ** 2) / (n - 1)
    k_corr = k - ((k - 1) ** 2) / (n - 1)

    denom = min(k_corr - 1, r_corr - 1)
    if denom <= 0:
        return 0.0

    v = math.sqrt(phi2_corr / denom)
    return float(v) if math.isfinite(v) else 0.0


# Configuration
ID_COLUMN = "id"
TARGET_COLUMN = "operational_success"

# Process dataset
df = extract_data_from_excel("dataset.xlsx")

# Identify feature types
continuous_features, categorical_features = classify_column_types(
    df,
    excluded_cols=[TARGET_COLUMN, ID_COLUMN]
)
df[TARGET_COLUMN] = pd.to_numeric(df[TARGET_COLUMN], errors="coerce")
df[ID_COLUMN] = pd.to_numeric(df[ID_COLUMN], errors="coerce")

for col in continuous_features:
    df[col] = pd.to_numeric(df[col], errors="coerce")

for col in categorical_features:
    df[col] = df[col].astype(str).str.strip().replace({"": pd.NA})

# Clean numeric columns
df = df.dropna().reset_index(drop=True)

# Keep only valid binary target rows
df = df[df[TARGET_COLUMN].isin([0, 1])]

# Split target groups
target = df[TARGET_COLUMN].astype(int)
success_mask = target == 1
fail_mask = target == 0

# Compute effect sizes
all_effects = []
numeric_effects = []
categorical_effects = []

for col in continuous_features:
    effect = cohens_d(df.loc[success_mask, col], df.loc[fail_mask, col])
    all_effects.append({
        "feature": col,
        "effect": effect,
        "abs_effect": abs(effect),
        "type": "numeric"
    })
    numeric_effects.append((col, effect))

for col in categorical_features:
    # Normalize categorical values as trimmed strings for stable crosstab
    effect = cramers_v_bias_corrected(df[col], target)
    all_effects.append({
        "feature": col,
        "effect": effect,       # nonnegative by definition
        "abs_effect": abs(effect),
        "type": "categorical"
    })
    categorical_effects.append((col, effect))

# Rank all features: absolute effect desc, then lexicographically
all_effects_sorted = sorted(
    all_effects,
    key=lambda x: (-x["abs_effect"], x["feature"])
)

top_n = min(10, len(all_effects_sorted))
top_10_features = [x["feature"] for x in all_effects_sorted[:top_n]]
top_10_effect_sizes = [round(float(x["effect"]), 6)
                       for x in all_effects_sorted[:top_n]]

# Strongest per type
strongest_numeric_feature = ""
if numeric_effects:
    strongest_numeric_feature = sorted(
        numeric_effects, key=lambda x: (-abs(x[1]), x[0])
    )[0][0]

strongest_categorical_feature = ""
if categorical_effects:
    strongest_categorical_feature = sorted(
        categorical_effects, key=lambda x: (-x[1], x[0])
    )[0][0]

# Means
mean_abs_numeric_effect = (
    round(sum(abs(v) for _, v in numeric_effects) / len(numeric_effects), 6)
    if numeric_effects else 0.0
)

mean_categorical_effect = (
    round(sum(v for _, v in categorical_effects) / len(categorical_effects), 6)
    if categorical_effects else 0.0
)

result = {
    "row_count": int(len(df)),
    "numeric_feature_count": int(len(continuous_features)),
    "categorical_feature_count": int(len(categorical_features)),
    "top_10_features": top_10_features,
    "top_10_effect_sizes": top_10_effect_sizes,
    "strongest_numeric_feature": strongest_numeric_feature,
    "strongest_categorical_feature": strongest_categorical_feature,
    "mean_abs_numeric_effect": mean_abs_numeric_effect,
    "mean_categorical_effect": mean_categorical_effect,
}

print(json.dumps(result, indent=2))

```

**Explanation:** This code performs feature effect size analysis on a binary classification dataset stored in an Excel file. It loads data from all worksheets, automatically classifies columns as either numeric or categorical by sampling 10% of the data, then calculates effect sizes for each feature's relationship with a binary target variable (operational_success): Cohen's d (with pooled standard deviation) for numeric features and bias-corrected Cramér's V for categorical features. After cleaning the data and removing missing values, it ranks all features by absolute effect size, identifies the top 10 most impactful features overall, determines the strongest numeric and categorical features individually, and computes mean effect sizes for each feature type, finally outputting all results as a JSON object.

**GTFA:**
```json
{
  "row_count": 9973,
  "numeric_feature_count": 8,
  "categorical_feature_count": 1,
  "top_10_features": [
    "revenue_growth_rate",
    "cost_efficiency_index",
    "supply_chain_stability",
    "business_unit",
    "customer_retention_score",
    "workforce_utilization",
    "process_cycle_efficiency",
    "digital_maturity_index",
    "forecast_accuracy"
  ],
  "top_10_effect_sizes": [
    1.492401,
    -0.969754,
    0.779501,
    0.414135,
    0.053062,
    0.042958,
    0.009033,
    0.002557,
    -0.000995
  ],
  "strongest_numeric_feature": "revenue_growth_rate",
  "strongest_categorical_feature": "business_unit",
  "mean_abs_numeric_effect": 0.418783,
  "mean_categorical_effect": 0.414135
}
```