User Guide
This guide provides comprehensive documentation for using Luxin.
Core Concepts
TrackedDataFrame
TrackedDataFrame is a pandas DataFrame subclass that automatically tracks which source rows contribute to each aggregated row during groupby operations.
from luxin import TrackedDataFrame
df = TrackedDataFrame({
'category': ['A', 'A', 'B', 'B'],
'value': [10, 20, 30, 40]
})
# When you aggregate, tracking happens automatically
agg = df.groupby('category').sum()
Inspector
The Inspector class provides the main interface for interactive data exploration. It works with both TrackedDataFrame and regular pandas DataFrames.
from luxin import Inspector
inspector = Inspector(agg_df)
inspector.render() # Must be called within Streamlit app context
Basic Usage Patterns
Pattern 1: Automatic Tracking (Recommended)
Use TrackedDataFrame for automatic source row tracking:
from luxin import Inspector, TrackedDataFrame
df = TrackedDataFrame(your_data)
agg = df.groupby(['column1', 'column2']).agg({'value': 'sum'})
inspector = Inspector(agg)
inspector.render()
Pattern 2: Regular DataFrame
You can also use regular pandas DataFrames, but you'll need to convert to TrackedDataFrame before aggregating:
import pandas as pd
from luxin import Inspector, TrackedDataFrame
df = pd.DataFrame(your_data)
tracked_df = TrackedDataFrame(df)
agg = tracked_df.groupby('category').sum()
inspector = Inspector(agg)
inspector.render()
Advanced Usage
Multi-Column Grouping
Luxin fully supports multi-column groupby operations:
df = TrackedDataFrame({
'region': ['North', 'North', 'South', 'South'],
'product': ['A', 'B', 'A', 'B'],
'sales': [100, 150, 200, 250]
})
agg = df.groupby(['region', 'product']).sum()
inspector = Inspector(agg)
inspector.render()
Custom Aggregations
You can use any pandas aggregation function:
agg = df.groupby('category').agg({
'sales': ['sum', 'mean', 'count'],
'profit': 'sum',
'quantity': ['min', 'max']
})
inspector = Inspector(agg)
inspector.render()
Pre-aggregated data (manual source mapping)
When you already have agg_df and detail_df (for example from SQL or a legacy pandas pipeline), you can build drill-down without re-running TrackedDataFrame groupby:
from luxin import create_drill_table
create_drill_table(agg_df, detail_df, groupby_cols=["category"])
Rules: groupby_cols must exist on detail_df. For a flat aggregate index, pass exactly one column name (the grouping column in the detail frame). For a MultiIndex aggregate index, pass one name per level, in order, matching validate_manual_drill_inputs / luxin_core.drill_table.
Missing values in keys: If aggregates use groupby(..., dropna=False), group keys may contain NaN, NaT, or pd.NA. Manual source mapping matches those rows correctly (NA-aware equality on the detail columns).
For programmatic use without UI, call luxin_core.drill_table.build_manual_source_mapping (or the re-export on luxin.drill_table) after validate_manual_drill_inputs.
Phase 3 — multi-level drill-down (v0.3.0)
Stack additional aggregations by passing a DrillHierarchySpec and enabling the feature flag:
from luxin import Inspector, TrackedDataFrame, DrillHierarchySpec
from luxin.config import InspectorConfig
config = InspectorConfig(enable_multi_level_drill=True, max_drill_depth=4)
def drill_region(region_key, rows):
tracked = TrackedDataFrame(rows)
return tracked.groupby("product").agg({"value": "sum"})
spec = DrillHierarchySpec(
session_key="demo_sales",
max_depth=4,
level_labels=["Region", "Product"],
next_level=lambda key, rows: drill_region(key, rows),
)
df = TrackedDataFrame(
{
"region": ["N", "N", "S"],
"product": ["A", "B", "A"],
"value": [10, 20, 30],
}
)
root = df.groupby("region").agg({"value": "sum"})
Inspector(root, config=config, drill=spec).render()
Breadcrumbs let you jump back to any ancestor slice. Session keys are namespaced (luxin_drill_stack_{session_key}) to avoid collisions across inspectors.
Phase 3 — data quality, comparison, aggregation builder (v0.3.0)
show_data_quality— adds a metrics / outlier-focused expander beside the detail pane.show_comparison_entrypoint— shows a small snippet pointing atluxin.compare.inspect_pair.show_aggregation_builder— footer expander builds a new aggregation from the snapshot of the original_source_dfgathered atInspectorconstruction; results override the main table until cleared.compare_run_significance— when true and SciPy is installed (pip install 'luxin[compare]'), comparison mode can run Welch-style t-tests across aligned numeric columns.
UI Components
The Inspector interface includes:
- Main table — Aggregated rows with interactive selection (Streamlit 1.35+)
- Detail panel — Rows that rolled up into the selected aggregate
- Summary statistics — Optional expander with
describe()on the aggregate - Filters & export — When enabled in
InspectorConfig - Phase 3 (optional) — Breadcrumb drill stack; data-quality expander beside details; footer Build aggregation; API hint expander for
luxin.compare
Tips and Best Practices
- Always use TrackedDataFrame - For automatic tracking, wrap your data in
TrackedDataFramebefore aggregating - Call render() in Streamlit -
Inspector.render()must be called within a Streamlit app context - Handle large datasets - For very large datasets, consider filtering before aggregation
- Use meaningful column names - Clear column names make the drill-down interface more intuitive
Troubleshooting
Inspector shows "No detail rows found"
This usually means:
- The DataFrame wasn't created using TrackedDataFrame
- The aggregation wasn't performed on a TrackedDataFrame
- The source mapping wasn't properly tracked
Solution: Ensure you use TrackedDataFrame from the start and perform aggregations on it. If you use create_drill_table / build_manual_source_mapping, see Troubleshooting — Manual drill and NA group keys.
Streamlit errors
If you see Streamlit-related errors:
- Ensure Streamlit is installed: pip install streamlit
- Make sure render() is called within a Streamlit app context
- Check that you're running the app with streamlit run app.py