Full Pipeline Outputs

Legacy low-level tutorial: this page documents the pre-clusterer intermediate pipeline. For the current public contract use class-guided regions, regions_, region_metrics_, labels_, and assign_regions().

This tutorial documents the intermediate artifacts worth saving when you run InsideForest with get_detail=True. Each stage records dimensions, key columns, and up to 10 sample rows so the analysis can be audited later.

Optional dependencies

pip install InsideForest==0.3.9
pip install MetaCraft==2025.10.6
import pandas as pd

pd.set_option("display.max_columns", None)

def audit_df(name, df, n=10):
    print(f"{name}: shape={df.shape}")
    print("columns:", df.columns.tolist())
    return df.head(n) if len(df) >= n else df

Output Map

Stage Object What it confirms Recommended sample
Prepared datadf, X, yFeature names, target column, and preprocessing.df.head(10)
Trainingin_f.labels_, pred_labelsLearned and predicted cluster labels.Label counts and 10 rows aligned to the target.
Regionsin_f.df_reres_Prioritized ranges from the forest.First DataFrame in the list; show all rows if fewer than 10.
Descriptionin_f.df_clusters_description_Readable rules, weight, effectiveness, and cluster size.head(10)
Relative expansiondf_datos_explain or in_f.df_datos_explain_Rules expressed as variable percentiles.head(10)
Frontiersfrontiers or in_f.frontiers_Comparable cluster pairs, similarity, delta, and score.head(10) sorted by score.
Textdescrip_generales, descrip_gptOriginal conditions and generated narratives.First 10 descriptions.
Metadatametadata.df, df_meta_subVariable catalog and the rule-linked metadata subset.head(10) with identity, type, domain, and actionability columns.
Experimentsmis_df2s, top_experiments, df_experPrioritized comparisons between clusters.head(10), or all rows for two-cluster experiments.
Hypothesisgenerate_hypothesis(...)Actionable narrative using rules, metrics, and metadata.Local text plus optional GPT output.

Iris: fit, labels, and regions

from InsideForest import InsideForest
from sklearn.datasets import load_iris

iris = load_iris()
df = pd.DataFrame(iris.data, columns=["petal_length", "petal_width", "sepal_length", "sepal_width"])
df["species"] = iris.target.astype(float)

in_f = InsideForest(
    rf_params={"random_state": 15},
    tree_params={"lang": "py", "n_sample_multiplier": 0.05, "ef_sample_multiplier": 10},
    leaf_percentile=95,
    low_leaf_fraction=0.25,
    get_detail=True,
    var_obj="species",
)
in_f.fit(df)

pred_labels = in_f.predict(df.drop(columns=["species"]))
training_labels = in_f.labels_

Prepared data output: shape=(150, 5). Keep at least 10 rows to review units and target encoding.

rowpetal_lengthpetal_widthsepal_lengthsepal_widthspecies
05.13.51.40.20.0
14.93.01.40.20.0
24.73.21.30.20.0
34.63.11.50.20.0
45.03.61.40.20.0
55.43.91.70.40.0
64.63.41.40.30.0
75.03.41.50.20.0
84.42.91.40.20.0
94.93.11.50.10.0

Labels and regions

df_plot = pd.DataFrame({"species": iris.target, "training_labels": training_labels})
audit_df("df_plot", df_plot)
audit_df("in_f.df_reres_[0]", in_f.df_reres_[0])

The label table stays aligned to the original target. Records that do not match any rule can receive -1.

rowspeciestraining_labels
0011
1011
2011
3011
4011
5012
6011
7011
8011
9011

df_reres_ is a list of DataFrames. Each table combines lower bounds, upper bounds, and region metrics. If the first table has fewer than 10 rows, show it entirely.

rowlinf.petal_widthlinf.sepal_lengthlinf.sepal_widthlsup.petal_widthlsup.sepal_lengthlsup.sepal_widthmetrics.ef_samplemetrics.n_samplemetrics.ponderadormetrics.count
02.451.000.104.801.900.600.00500.918
12.454.451.207.005.101.801.00480.867
22.454.801.707.906.902.502.00460.795
32.203.001.006.405.001.701.00310.633
42.504.901.807.906.902.502.00290.582

Descriptions, explanation, and frontiers

from InsideForest.descrip import get_frontiers, generate_descriptions

df_datos_explain, frontiers = get_frontiers(in_f.df_clusters_description_, df, divide=5)

audit_df("in_f.df_clusters_description_", in_f.df_clusters_description_)
audit_df("df_datos_explain", df_datos_explain)
audit_df("frontiers", frontiers.sort_values("score", ascending=False))

df_clusters_description_ contains one row per candidate cluster, with the original readable rule and its metrics.

clustercluster_descriptioncluster_weightcluster_ef_samplecluster_n_samplecluster_count
01.00 <= sepal_length <= 1.90 AND 0.10 <= sepal_width <= 0.600.910.00508
14.45 <= sepal_length <= 5.10 AND 1.20 <= sepal_width <= 1.800.861.00487
24.80 <= sepal_length <= 6.90 AND 1.70 <= sepal_width <= 2.500.792.00465
33.00 <= sepal_length <= 5.00 AND 1.00 <= sepal_width <= 1.700.631.00313
44.90 <= sepal_length <= 6.90 AND 1.80 <= sepal_width <= 2.500.582.00292
52.30 <= petal_width <= 3.20 AND 1.00 <= sepal_length <= 1.700.470.00252
62.70 <= petal_width <= 3.40 AND 4.00 <= sepal_length <= 5.000.441.00242
72.90 <= petal_width <= 3.80 AND 5.10 <= sepal_length <= 6.700.402.00212
84.60 <= petal_length <= 6.30 AND 1.50 <= sepal_width <= 2.400.362.00191
93.30 <= petal_length <= 4.90 AND 1.00 <= sepal_width <= 1.600.321.00181

df_datos_explain adds relative percentile columns for each variable used in the rules.

clustercluster_ef_samplecluster_n_samplecluster_desc_relativepetal_widthsepal_lengthsepal_widthpetal_length
00.0050sepal_length = Percentile 20, sepal_width = Percentile 20PERCENTILE 20PERCENTILE 20
11.0048sepal_length = Percentile 60, sepal_width = Percentile 60PERCENTILE 60PERCENTILE 60
22.0046sepal_length = Percentile 90, sepal_width = Percentile 90PERCENTILE 90PERCENTILE 90
31.0031sepal_length = Percentile 50, sepal_width = Percentile 50PERCENTILE 50PERCENTILE 50
42.0029sepal_length = Percentile 90, sepal_width = Percentile 90PERCENTILE 90PERCENTILE 90
50.0025petal_width = Percentile 40, sepal_length = Percentile 20PERCENTILE 40PERCENTILE 20
61.0024petal_width = Percentile 60, sepal_length = Percentile 60PERCENTILE 60PERCENTILE 60
72.0021petal_width = Percentile 80, sepal_length = Percentile 90PERCENTILE 80PERCENTILE 90
82.0019petal_length = Percentile 80, sepal_width = Percentile 80PERCENTILE 80PERCENTILE 80
91.0018petal_length = Percentile 60, sepal_width = Percentile 60PERCENTILE 60PERCENTILE 60

frontiers ranks cluster pairs. The score favors similar pairs with larger metric deltas.

cluster_1cluster_2similaritydelta_cluster_ef_samplescore
120.821.000.74
340.761.000.68
670.711.000.61
010.651.000.57
560.631.000.55
890.601.000.52
020.542.000.51
570.482.000.46
140.441.000.39
680.421.000.37

Generated descriptions

descrip_generales = [
    x for x in in_f.df_clusters_description_["cluster_description"].unique().tolist()
    if isinstance(x, str)
]

# Optional when an API key is available.
descrip_gpt = generate_descriptions(descrip_generales[:10], "english", api_k)
audit_df("descriptions", pd.DataFrame({"cluster_description": descrip_generales[:10]}))

Titanic: metadata and experiments

import seaborn as sns
from metacraft import Metadata
from InsideForest.metadata import MetaExtractor, run_experiments

df = sns.load_dataset("titanic")
var_obj = "survived"

metadata = Metadata()
titanic_meta = "https://raw.githubusercontent.com/jcval94/Metadata_files/refs/heads/main/titanic.yaml"
metadata.update(df, titanic_meta, inplace=False, output="titanic.yaml")
metadata.validate(df)

mx = MetaExtractor(metadata.df, var_obj)
audit_df("metadata.df", metadata.df)

metadata.df.head(10): this is the minimum 10-row metadata review. It includes identity, logical type, domain, and actionability.

metadata_rowidentity.label_i18n.enidentity.description_i18n.entype.logical_typedomain.categorical.codesincrease_difficultydecrease_difficultyside_effects
survivedSurvivedPassenger survival indicator.categorical0=no; 1=yes1010Target variable; do not intervene directly.
pclassTicket classSocioeconomic ticket class.ordinal1=first; 2=second; 3=third88Socioeconomic proxy; possible bias.
sexSexRegistered passenger sex.categoricalmale; female1010Sensitive attribute; not actionable.
ageAgeRecorded or estimated age.numeric1010Demographic attribute; not actionable.
sibspSiblings/spousesNumber of siblings or spouses aboard.numeric77May represent family structure.
parchParents/childrenNumber of parents or children aboard.numeric77May represent dependents.
fareFareTicket fare paid.numeric66Economic access proxy.
embarkedEmbarkation portPort where the passenger boarded.categoricalC=Cherbourg; Q=Queenstown; S=Southampton99May introduce geographic bias.
classClass labelPassenger class as text.categoricalFirst; Second; Third88Redundant with pclass.
whoPerson groupDerived group: man, woman, or child.categoricalman; woman; child1010Derived from age and sex; review bias.

Experiment construction

percentile_75 = frontiers["score"].quantile(0.75)
frontiers_flt = frontiers[frontiers["score"] > percentile_75]

mis_df2s = {}
for vals in range(len(frontiers_flt) - 1):
    quick_wins = frontiers_flt[["cluster_1", "cluster_2"]].values[vals]
    df_QW_ = df_datos_explain[df_datos_explain["cluster"].isin(quick_wins)]
    mis_df2s[f"experiment_{vals}"] = df_QW_

top_experiments = run_experiments(mx, mis_df2s)
audit_df("top_experiments", top_experiments)

mis_df2s["experiment_0"] usually contains two rows because each experiment compares two clusters.

clustercluster_ef_samplecluster_n_samplecluster_descriptionsexpclassfareage
120.7284cat__sex_female > 0.50 AND num__fare > 0.15PERCENTILE 80PERCENTILE 75
270.2891cat__sex_male > 0.50 AND num__pclass > 0.65PERCENTILE 20PERCENTILE 80

top_experiments.head(10) ranks contrasts by effectiveness delta, size, intersection, and action difficulty.

datasetcluster_acluster_bcluster_ef_acluster_ef_bdelta_efvariables_avariables_bvariables_intersectiondifficulty_adifficulty_bscore
experiment_027120.280.720.44cat__sex_male, num__pclasscat__sex_female, num__fare10100.46
experiment_131180.340.690.35num__pclassnum__farecat__alone_0860.42
experiment_24470.210.550.34cat__adult_male_1cat__who_child10100.31
experiment_32290.410.670.26cat__embarked_Scat__embarked_Cnum__fare990.28
experiment_435160.180.430.25num__agecat__who_womannum__pclass10100.22
experiment_52940.360.590.23cat__deck_nancat__deck_Bnum__fare990.20
experiment_641110.240.450.21cat__alone_1cat__alone_0cat__sex_female770.18
experiment_71930.470.640.17num__sibspnum__farecat__class_First760.15
experiment_850230.290.440.15cat__embark_town_Southamptoncat__embark_town_Cherbourg990.11
experiment_91460.520.660.14num__parchcat__who_childcat__alone_07100.10

df_exper, df_meta_sub, and hypothesis

n_experimento = 0
experimento_ = top_experiments.loc[n_experimento, "dataset"]
cols_act = [
    "cluster_ef_a", "cluster_ef_b", "delta_ef", "variables_intersection",
    "variables_a", "variables_b", "intersection", "only_cluster_a",
    "only_cluster_b", "score",
]
df_exper = top_experiments[top_experiments["dataset"] == experimento_][cols_act]

cols_mtd = [
    "metadata_row", "rule_token", "identity.label_i18n.en",
    "identity.description_i18n.en", "type.logical_type",
    "domain.categorical.codes", "actionability.increase_difficulty",
    "actionability.decrease_difficulty", "actionability.side_effects",
]
df_meta_sub = mx.extract(mis_df2s[experimento_])[cols_mtd]
metadata_rowrule_tokenidentity.label_i18n.enidentity.description_i18n.entype.logical_typedomain.categorical.codesincrease_difficultydecrease_difficultyside_effects
sexcat__sex_maleSexRegistered passenger sex.categoricalmale; female1010Sensitive attribute; not actionable.
sexcat__sex_femaleSexRegistered passenger sex.categoricalmale; female1010Sensitive attribute; not actionable.
pclassnum__pclassTicket classSocioeconomic ticket class.ordinal1=first; 2=second; 3=third88Socioeconomic proxy; possible bias.
farenum__fareFareTicket fare paid.numeric66Economic access proxy.
survivedsurvivedSurvivedPassenger survival indicator.categorical0=no; 1=yes1010Target variable; do not intervene directly.
from InsideForest.descrip import generate_hypothesis

hypothesis = generate_hypothesis(df_meta_sub, df_exper, target="survived", lang="en")
print(hypothesis)

Example local output: Group A has an effectiveness of 0.28 versus 0.72 in group B, a difference of 0.44 for Survived. The lower-effectiveness group is distinguished by Sex and Ticket class; the higher-effectiveness group is distinguished by Sex and Fare. Treat this as a descriptive hypothesis, not a direct recommendation, because it includes sensitive attributes and socioeconomic proxies.

For reproducible reports, store df_clusters_description_, df_datos_explain, frontiers, metadata.df, top_experiments, df_exper, and df_meta_sub next to the notebook.