splitpilot 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1531 @@
1
+ Metadata-Version: 2.5
2
+ Name: splitpilot
3
+ Version: 0.1.0
4
+ Summary: An explainable toolkit for designing and validating machine learning evaluation splits.
5
+ Project-URL: Homepage, https://github.com/krishgupta129/splitpilot
6
+ Project-URL: Repository, https://github.com/krishgupta129/splitpilot
7
+ Project-URL: Issues, https://github.com/krishgupta129/splitpilot/issues
8
+ Author: Krish Gupta
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: cross-validation,data-leakage,data-science,dataset,machine-learning,mlops,train-test-split
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: pandas>=2.0
22
+ Requires-Dist: scikit-learn>=1.4
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=8.0; extra == 'dev'
25
+ Description-Content-Type: text/markdown
26
+
27
+ # SplitPilot
28
+
29
+ **An explainable toolkit for designing and validating machine-learning evaluation splits**
30
+
31
+ SplitPilot is a Python package for choosing, executing, and validating train/test splitting strategies based on the structural characteristics of a dataset.
32
+
33
+ Instead of treating `train_test_split()` as a universal solution, SplitPilot considers signals such as repeated entities, temporal information, and grouping structure to recommend a more appropriate evaluation strategy.
34
+
35
+ > **Project status:** Early development / alpha
36
+ > **License:** MIT
37
+ > **Python:** 3.10+
38
+
39
+ ---
40
+
41
+ ## Table of Contents
42
+
43
+ 1. [Project Overview](#1-project-overview)
44
+ 2. [The Problem](#2-the-problem)
45
+ 3. [Project Objective](#3-project-objective)
46
+ 4. [Core Idea](#4-core-idea)
47
+ 5. [How SplitPilot Works](#5-how-splitpilot-works)
48
+ 6. [Supported Split Strategies](#6-supported-split-strategies)
49
+ 7. [Why Split Strategy Matters](#7-why-split-strategy-matters)
50
+ 8. [Architecture](#8-architecture)
51
+ 9. [Project Structure](#9-project-structure)
52
+ 10. [Installation](#10-installation)
53
+ 11. [Quick Start](#11-quick-start)
54
+ 12. [Recommendation Workflow](#12-recommendation-workflow)
55
+ 13. [SplitResult](#13-splitresult)
56
+ 14. [Validation and Leakage Checks](#14-validation-and-leakage-checks)
57
+ 15. [Testing](#15-testing)
58
+ 16. [Design Decisions](#16-design-decisions)
59
+ 17. [Current Limitations](#17-current-limitations)
60
+ 18. [Roadmap](#18-roadmap)
61
+ 19. [Example Use Cases](#19-example-use-cases)
62
+ 20. [Frequently Asked Questions](#20-frequently-asked-questions)
63
+ 21. [Interview Questions and Answers](#21-interview-questions-and-answers)
64
+ 22. [Contributing](#22-contributing)
65
+ 23. [License](#23-license)
66
+
67
+ ---
68
+
69
+ # 1. Project Overview
70
+
71
+ SplitPilot addresses an important but frequently overlooked part of machine-learning experimentation:
72
+
73
+ > **How should a dataset be divided so that model evaluation reflects the way the model will actually be used?**
74
+
75
+ A random split is convenient, but convenience does not guarantee a valid evaluation.
76
+
77
+ For example, if a dataset contains multiple observations for the same customer, randomly distributing rows can place the same customer in both training and testing data. The model may then benefit from information about an entity during training that also appears in the test set.
78
+
79
+ Likewise, if the data represents events over time, randomly mixing older and newer observations can allow future information to influence evaluation of the past.
80
+
81
+ SplitPilot therefore treats dataset splitting as an **evaluation-design problem**, not merely a preprocessing operation.
82
+
83
+ ---
84
+
85
+ # 2. The Problem
86
+
87
+ A conventional workflow often looks like this:
88
+
89
+ ```text
90
+ Raw Dataset
91
+ |
92
+ v
93
+ train_test_split()
94
+ |
95
+ v
96
+ Train Model
97
+ |
98
+ v
99
+ Evaluate Model
100
+ ```
101
+
102
+ The problem is that the default random split may not respect the structure of the data.
103
+
104
+ Consider a customer transaction dataset:
105
+
106
+ | Customer | Date | Transaction | Churn |
107
+ |---|---|---:|---:|
108
+ | A | Jan 01 | 100 | 0 |
109
+ | A | Jan 15 | 150 | 0 |
110
+ | A | Feb 20 | 120 | 1 |
111
+ | B | Jan 03 | 80 | 0 |
112
+ | B | Mar 02 | 210 | 1 |
113
+
114
+ A random row-level split could place observations from customer A into both training and test sets.
115
+
116
+ That produces an evaluation question that may not match the intended real-world question.
117
+
118
+ Instead of:
119
+
120
+ > "Can the model generalize to unseen customers or future observations?"
121
+
122
+ the experiment may effectively ask:
123
+
124
+ > "Can the model predict another observation from an entity it has already seen?"
125
+
126
+ These are different evaluation problems.
127
+
128
+ ---
129
+
130
+ # 3. Project Objective
131
+
132
+ The primary objective of SplitPilot is to make dataset splitting more deliberate and explainable.
133
+
134
+ The package is designed around three principles:
135
+
136
+ ### 3.1 Inspect dataset structure
137
+
138
+ Identify useful structural signals such as:
139
+
140
+ - repeated entity identifiers
141
+ - temporal columns
142
+ - potential grouping columns
143
+ - target structure
144
+
145
+ ### 3.2 Recommend an evaluation strategy
146
+
147
+ Use those signals to recommend an appropriate splitting approach.
148
+
149
+ ### 3.3 Execute and validate the split
150
+
151
+ Produce train/test datasets while enforcing the selected structural constraints.
152
+
153
+ The intended workflow is:
154
+
155
+ ```mermaid
156
+ flowchart TD
157
+ A[Dataset] --> B[Inspect Structure]
158
+ B --> C[Detect Repeated Entities]
159
+ B --> D[Detect Temporal Information]
160
+ C --> E[Recommendation]
161
+ D --> E
162
+ E --> F[Select Split Strategy]
163
+ F --> G[Execute Split]
164
+ G --> H[Validate Evaluation Boundaries]
165
+ H --> I[Train and Evaluate Model]
166
+ ```
167
+
168
+ ---
169
+
170
+ # 4. Core Idea
171
+
172
+ SplitPilot separates two related but different responsibilities:
173
+
174
+ | Responsibility | Purpose |
175
+ |---|---|
176
+ | **Recommendation** | Decide which splitting strategy is appropriate |
177
+ | **Splitting** | Actually construct train/test datasets |
178
+ | **Validation** | Check whether important boundaries were respected |
179
+ | **Explanation** | Tell the user why a strategy was recommended |
180
+
181
+ This distinction is important because a splitting library should not simply return arrays. It should help the user understand **why the split exists in its particular form**.
182
+
183
+ ---
184
+
185
+ # 5. How SplitPilot Works
186
+
187
+ At a high level:
188
+
189
+ ```mermaid
190
+ flowchart LR
191
+ A[DataFrame] --> B[Profiler]
192
+ B --> C[Recommender]
193
+ C --> D[Recommendation]
194
+ D --> E[DatasetSplitter]
195
+ E --> F[SplitResult]
196
+ F --> G[Validation]
197
+ ```
198
+
199
+ The current package is organized around several components:
200
+
201
+ | Component | Role |
202
+ |---|---|
203
+ | `Profiler` | Examines dataset characteristics |
204
+ | `Recommender` | Produces a split recommendation |
205
+ | `DatasetSplitter` | Executes the requested split |
206
+ | `SplitResult` | Provides a structured result |
207
+ | `Recommendation` model | Represents recommendation information |
208
+ | `Pilot` | Provides the higher-level user-facing workflow |
209
+
210
+ The exact implementation is intentionally modular so that recommendation logic and splitting logic can evolve independently.
211
+
212
+ ---
213
+
214
+ # 6. Supported Split Strategies
215
+
216
+ SplitPilot currently supports the following strategies.
217
+
218
+ ## 6.1 Random Split
219
+
220
+ The conventional row-level train/test split.
221
+
222
+ ```text
223
+ Rows
224
+ |
225
+ +-- Training
226
+ |
227
+ +-- Testing
228
+ ```
229
+
230
+ Conceptually:
231
+
232
+ ```python
233
+ train_test_split(
234
+ X,
235
+ y,
236
+ test_size=0.2,
237
+ random_state=42
238
+ )
239
+ ```
240
+
241
+ ### Appropriate when
242
+
243
+ - observations can reasonably be treated as independent
244
+ - there is no important entity boundary
245
+ - there is no meaningful temporal ordering
246
+ - random sampling represents the intended deployment scenario
247
+
248
+ ### Risk
249
+
250
+ If repeated entities or temporal dependencies exist, a random split can create leakage or an overly optimistic evaluation.
251
+
252
+ ---
253
+
254
+ ## 6.2 Group Split
255
+
256
+ Group splitting keeps all observations belonging to the same group on one side of the split.
257
+
258
+ Example:
259
+
260
+ ```text
261
+ Customer A ───────────────> TRAIN
262
+ Customer B ───────────────> TRAIN
263
+ Customer C ───────────────> TEST
264
+ Customer D ───────────────> TEST
265
+ ```
266
+
267
+ For customer-level data, the group column might be:
268
+
269
+ ```python
270
+ group_column="customer_id"
271
+ ```
272
+
273
+ The important property is:
274
+
275
+ ```text
276
+ TRAIN groups ∩ TEST groups = ∅
277
+ ```
278
+
279
+ ### Appropriate when
280
+
281
+ - observations belong to entities
282
+ - the model should generalize to unseen entities
283
+ - repeated observations exist for the same entity
284
+
285
+ ---
286
+
287
+ ## 6.3 Group-Stratified Split
288
+
289
+ Group-stratified splitting attempts to maintain a target-related distribution while still separating groups.
290
+
291
+ The implementation creates a group-level target summary and uses that summary to form strata before splitting groups.
292
+
293
+ Conceptually:
294
+
295
+ ```text
296
+ Individual observations
297
+ |
298
+ v
299
+ Aggregate by group
300
+ |
301
+ v
302
+ Group-level target distribution
303
+ |
304
+ v
305
+ Stratified group split
306
+ |
307
+ +-------- TRAIN GROUPS
308
+ |
309
+ +-------- TEST GROUPS
310
+ ```
311
+
312
+ ### Appropriate when
313
+
314
+ - groups must remain isolated
315
+ - target distribution matters
316
+ - the dataset contains enough groups to support stratification
317
+
318
+ ### Important consideration
319
+
320
+ The current implementation uses a median-based binary stratum derived from the group-level target mean. This is a practical initial approach, not a universal stratification algorithm.
321
+
322
+ ---
323
+
324
+ ## 6.4 Time Split
325
+
326
+ Time splitting respects chronological ordering.
327
+
328
+ ```text
329
+ PAST ------------------------------------> FUTURE
330
+
331
+ |---------------- TRAIN -----------------|--- TEST ---|
332
+ ```
333
+
334
+ The implementation:
335
+
336
+ 1. converts the specified time column to datetime
337
+ 2. rejects invalid dates
338
+ 3. sorts observations chronologically
339
+ 4. creates the train/test boundary according to `test_size`
340
+
341
+ Example:
342
+
343
+ ```python
344
+ strategy="time"
345
+ time_column="transaction_date"
346
+ ```
347
+
348
+ ### Appropriate when
349
+
350
+ - future predictions are the real deployment scenario
351
+ - historical data is used to predict later observations
352
+ - temporal ordering contains meaningful information
353
+
354
+ ### Core principle
355
+
356
+ The test set should represent a later period than the training set.
357
+
358
+ ---
359
+
360
+ ## 6.5 Group-Time Split
361
+
362
+ Group-time splitting combines entity separation with chronological ordering.
363
+
364
+ This strategy is useful when both conditions matter:
365
+
366
+ 1. groups should not appear in both train and test
367
+ 2. the split should respect time
368
+
369
+ Conceptually:
370
+
371
+ ```text
372
+ TIME
373
+ -------------------------------->
374
+
375
+ TRAIN GROUPS |-----------------------------|
376
+
377
+ TEST GROUPS |--------|
378
+ ^ ^
379
+ | |
380
+ train boundary future test period
381
+ ```
382
+
383
+ The current implementation first orders the data by the supplied time column, derives the ordered group sequence, and assigns groups to train/test according to the requested test proportion.
384
+
385
+ This strategy was specifically tested for:
386
+
387
+ - no group overlap
388
+ - chronological ordering
389
+ - leakage prevention
390
+
391
+ ---
392
+
393
+ # 7. Why Split Strategy Matters
394
+
395
+ A model score is only meaningful relative to the evaluation design that produced it.
396
+
397
+ Suppose two experiments report:
398
+
399
+ | Experiment | Split | Accuracy |
400
+ |---|---|---:|
401
+ | A | Random | 94% |
402
+ | B | Group | 82% |
403
+
404
+ It would be incorrect to immediately conclude that experiment A produced a better model.
405
+
406
+ The two experiments may be answering different questions.
407
+
408
+ A useful mental model is:
409
+
410
+ ```text
411
+ Model Performance
412
+ |
413
+ v
414
+ Evaluation Protocol
415
+ |
416
+ v
417
+ Split Strategy
418
+ |
419
+ v
420
+ Assumptions About Real-World Data
421
+ ```
422
+
423
+ Therefore, SplitPilot focuses on making the evaluation protocol explicit.
424
+
425
+ ---
426
+
427
+ # 8. Architecture
428
+
429
+ The current source architecture follows a `src` layout:
430
+
431
+ ```text
432
+ splitpilot/
433
+ |
434
+ +-- src/
435
+ | |
436
+ | +-- splitpilot/
437
+ | |
438
+ | +-- __init__.py
439
+ | |
440
+ | +-- core/
441
+ | | +-- __init__.py
442
+ | | +-- pilot.py
443
+ | | +-- profiler.py
444
+ | | +-- recommender.py
445
+ | | +-- splitter.py
446
+ | |
447
+ | +-- models/
448
+ | +-- __init__.py
449
+ | +-- recommendation.py
450
+ |
451
+ +-- tests/
452
+ | +-- test_splitter.py
453
+ |
454
+ +-- CHANGELOG.md
455
+ +-- LICENSE
456
+ +-- README.md
457
+ +-- pyproject.toml
458
+ ```
459
+
460
+ The `src/splitpilot` directory is the actual Python package. The outer `splitpilot` directory is the project/repository root.
461
+
462
+ This structure helps keep package source code separate from project-level files.
463
+
464
+ ---
465
+
466
+ # 9. Project Structure
467
+
468
+ | Path | Purpose |
469
+ |---|---|
470
+ | `src/splitpilot/core/pilot.py` | High-level package workflow |
471
+ | `src/splitpilot/core/profiler.py` | Dataset structure analysis |
472
+ | `src/splitpilot/core/recommender.py` | Strategy recommendation |
473
+ | `src/splitpilot/core/splitter.py` | Train/test split execution |
474
+ | `src/splitpilot/models/recommendation.py` | Recommendation data model |
475
+ | `tests/test_splitter.py` | Splitter test suite |
476
+ | `pyproject.toml` | Package metadata and build configuration |
477
+ | `README.md` | Project documentation |
478
+ | `CHANGELOG.md` | Version/change history |
479
+ | `LICENSE` | MIT license |
480
+
481
+ ---
482
+
483
+ # 10. Installation
484
+
485
+ ## From the source repository
486
+
487
+ Clone the repository and install the package in editable mode:
488
+
489
+ ```bash
490
+ git clone https://github.com/krishgupta129/splitpilot.git
491
+ cd splitpilot
492
+ pip install -e .
493
+ ```
494
+
495
+ For development dependencies:
496
+
497
+ ```bash
498
+ pip install -e ".[dev]"
499
+ ```
500
+
501
+ The development extra currently includes `pytest`.
502
+
503
+ ## Dependencies
504
+
505
+ The package currently depends on:
506
+
507
+ - Python 3.10+
508
+ - pandas
509
+ - scikit-learn
510
+
511
+ ---
512
+
513
+ # 11. Quick Start
514
+
515
+ A high-level workflow can be used through `Pilot`.
516
+
517
+ ```python
518
+ import pandas as pd
519
+ from splitpilot import Pilot
520
+
521
+ df = pd.read_excel("customer_churn.xlsx")
522
+
523
+ pilot = Pilot(
524
+ df,
525
+ target="churn"
526
+ )
527
+
528
+ recommendation = pilot.recommend()
529
+
530
+ print(recommendation)
531
+
532
+ result = pilot.split()
533
+
534
+ print(result.X_train.shape)
535
+ print(result.X_test.shape)
536
+ print(result.strategy)
537
+ ```
538
+
539
+ A recommendation may identify a strategy such as:
540
+
541
+ ```text
542
+ SplitRecommendation(
543
+ strategy='group_time',
544
+ ...
545
+ group_column='customer_id',
546
+ time_column='transaction_date'
547
+ )
548
+ ```
549
+
550
+ The exact recommendation depends on the structure of the supplied dataset.
551
+
552
+ ---
553
+
554
+ # 12. Recommendation Workflow
555
+
556
+ The recommended workflow is:
557
+
558
+ ```mermaid
559
+ sequenceDiagram
560
+ participant U as User
561
+ participant P as Pilot
562
+ participant F as Profiler
563
+ participant R as Recommender
564
+ participant S as Splitter
565
+
566
+ U->>P: Provide DataFrame and target
567
+ P->>F: Inspect dataset
568
+ F-->>P: Structural signals
569
+ P->>R: Evaluate signals
570
+ R-->>P: SplitRecommendation
571
+ U->>P: Request split
572
+ P->>S: Execute recommended strategy
573
+ S-->>P: SplitResult
574
+ P-->>U: Train/test data
575
+ ```
576
+
577
+ This workflow separates **decision-making** from **execution**.
578
+
579
+ That makes the package easier to test and makes recommendations easier to explain during experimentation.
580
+
581
+ ---
582
+
583
+ # 13. SplitResult
584
+
585
+ The splitter returns a `SplitResult` dataclass containing:
586
+
587
+ | Attribute | Type | Meaning |
588
+ |---|---|---|
589
+ | `X_train` | `pd.DataFrame` | Training features |
590
+ | `X_test` | `pd.DataFrame` | Testing features |
591
+ | `y_train` | `pd.Series` | Training target |
592
+ | `y_test` | `pd.Series` | Testing target |
593
+ | `strategy` | `str` | Strategy used |
594
+
595
+ Example:
596
+
597
+ ```python
598
+ result = pilot.split()
599
+
600
+ print(result.X_train)
601
+ print(result.X_test)
602
+ print(result.y_train)
603
+ print(result.y_test)
604
+ print(result.strategy)
605
+ ```
606
+
607
+ The result also supports tuple-style unpacking:
608
+
609
+ ```python
610
+ X_train, X_test, y_train, y_test = result
611
+ ```
612
+
613
+ This provides a familiar interface while retaining metadata through the structured result object.
614
+
615
+ ---
616
+
617
+ # 14. Validation and Leakage Checks
618
+
619
+ A useful split should be validated rather than trusted simply because the function executed successfully.
620
+
621
+ For grouped data, an important validation is:
622
+
623
+ ```python
624
+ train_ids = set(result.X_train["customer_id"])
625
+ test_ids = set(result.X_test["customer_id"])
626
+
627
+ print(train_ids.intersection(test_ids))
628
+ ```
629
+
630
+ Expected result:
631
+
632
+ ```text
633
+ set()
634
+ ```
635
+
636
+ This verifies that no customer appears in both partitions.
637
+
638
+ For temporal splits, inspect the date ranges:
639
+
640
+ ```python
641
+ print(result.X_train["transaction_date"].min())
642
+ print(result.X_train["transaction_date"].max())
643
+
644
+ print(result.X_test["transaction_date"].min())
645
+ print(result.X_test["transaction_date"].max())
646
+ ```
647
+
648
+ The expected relationship is:
649
+
650
+ ```text
651
+ max(train_time) <= min(test_time)
652
+ ```
653
+
654
+ when the evaluation design requires a strictly chronological boundary.
655
+
656
+ ### Validation philosophy
657
+
658
+ SplitPilot treats successful execution and valid evaluation as related but distinct concepts:
659
+
660
+ ```text
661
+ Function runs successfully
662
+ |
663
+ v
664
+ Correct shapes?
665
+ |
666
+ v
667
+ Correct group boundaries?
668
+ |
669
+ v
670
+ Correct temporal ordering?
671
+ |
672
+ v
673
+ Acceptable evaluation design
674
+ ```
675
+
676
+ ---
677
+
678
+ # 15. Testing
679
+
680
+ The splitter has a dedicated `pytest` test suite.
681
+
682
+ The current test run reports:
683
+
684
+ ```text
685
+ 12 passed in 1.15s
686
+ ```
687
+
688
+ The tested areas include:
689
+
690
+ | Test | Purpose |
691
+ |---|---|
692
+ | Random split shapes | Checks expected train/test dimensions |
693
+ | Group split overlap | Verifies group separation |
694
+ | Group-stratified split overlap | Verifies group separation |
695
+ | Time split chronology | Verifies temporal ordering |
696
+ | Group-time split overlap | Verifies group separation |
697
+ | Group-time chronology | Verifies temporal ordering |
698
+ | Group-time leakage prevention | Verifies structural isolation |
699
+ | Missing target | Validates error handling |
700
+ | Invalid test size | Validates parameter checking |
701
+ | Missing group column | Validates group configuration |
702
+ | Missing time column | Validates temporal configuration |
703
+ | Unknown strategy | Validates unsupported strategy handling |
704
+
705
+ Run the full suite with:
706
+
707
+ ```bash
708
+ pytest tests/test_splitter.py -v
709
+ ```
710
+
711
+ A successful run should report all tests as passed.
712
+
713
+ ---
714
+
715
+ # 16. Design Decisions
716
+
717
+ ## 16.1 Why use a recommendation layer?
718
+
719
+ A user should not need to manually remember every condition under which a random split becomes questionable.
720
+
721
+ The recommendation layer converts structural observations into an explicit suggestion.
722
+
723
+ This also makes the reasoning inspectable:
724
+
725
+ ```text
726
+ Dataset
727
+ |
728
+ +-- repeated customer_id
729
+ |
730
+ +-- transaction_date detected
731
+ |
732
+ v
733
+ Recommendation:
734
+ group_time
735
+ ```
736
+
737
+ ---
738
+
739
+ ## 16.2 Why return a dataclass?
740
+
741
+ Returning a raw tuple is familiar, but it loses useful metadata.
742
+
743
+ A `SplitResult` provides:
744
+
745
+ - named outputs
746
+ - explicit strategy information
747
+ - easier debugging
748
+ - clearer documentation
749
+ - compatibility with tuple-style unpacking
750
+
751
+ ---
752
+
753
+ ## 16.3 Why validate columns before splitting?
754
+
755
+ Failing early is preferable to allowing a cryptic downstream error.
756
+
757
+ For example:
758
+
759
+ ```python
760
+ if target not in df.columns:
761
+ raise ValueError(...)
762
+ ```
763
+
764
+ The same principle is applied to group and time columns.
765
+
766
+ ---
767
+
768
+ ## 16.4 Why validate temporal values?
769
+
770
+ A column may exist while containing invalid dates.
771
+
772
+ Therefore, the time-based implementation converts values with:
773
+
774
+ ```python
775
+ pd.to_datetime(..., errors="coerce")
776
+ ```
777
+
778
+ and raises an explicit error if invalid values are detected.
779
+
780
+ This prevents silent corruption of the chronological split.
781
+
782
+ ---
783
+
784
+ # 17. Current Limitations
785
+
786
+ SplitPilot is intentionally an early-stage project. Its current behavior should therefore be understood in that context.
787
+
788
+ ### 17.1 Recommendation heuristics are not universal truth
789
+
790
+ A recommendation is a structured suggestion based on detected dataset characteristics. It does not replace domain knowledge.
791
+
792
+ ### 17.2 Group-stratification is currently simplified
793
+
794
+ The current implementation creates a group-level target summary and uses a median-based binary stratum.
795
+
796
+ This is useful for the initial implementation, but more sophisticated target-distribution strategies may be appropriate for some datasets.
797
+
798
+ ### 17.3 Group-time behavior depends on the data structure
799
+
800
+ Combining grouping and chronology is inherently more complicated than a conventional random split.
801
+
802
+ The implementation currently uses ordered groups derived after sorting by the supplied time column. Different real-world longitudinal structures may require more specialized strategies.
803
+
804
+ ### 17.4 Small datasets can impose practical constraints
805
+
806
+ Strategies involving groups or stratification require enough groups and sufficiently varied target information to construct meaningful partitions.
807
+
808
+ ### 17.5 Recommendation does not guarantee absence of every form of leakage
809
+
810
+ SplitPilot focuses on structural split boundaries. Feature engineering, preprocessing, target construction, duplicated information, and other parts of an ML pipeline can also introduce leakage.
811
+
812
+ The split is one part of a broader evaluation protocol.
813
+
814
+ ---
815
+
816
+ # 18. Roadmap
817
+
818
+ Potential future development areas include:
819
+
820
+ | Area | Possible improvement |
821
+ |---|---|
822
+ | Recommendation engine | More robust dataset-structure heuristics |
823
+ | Group stratification | More flexible target-distribution handling |
824
+ | Temporal splitting | More sophisticated longitudinal strategies |
825
+ | Validation | Dedicated automated leakage diagnostics |
826
+ | Diagnostics | Human-readable validation reports |
827
+ | API | More configurable splitting policies |
828
+ | Testing | Broader edge-case and property-based testing |
829
+ | Documentation | More real-world datasets and case studies |
830
+ | Packaging | Continued PyPI release improvements |
831
+ | CI | Automated testing across supported Python versions |
832
+
833
+ The roadmap is intentionally open. Future features should be driven by real evaluation problems rather than adding complexity for its own sake.
834
+
835
+ ---
836
+
837
+ # 19. Example Use Cases
838
+
839
+ ## 19.1 Customer churn
840
+
841
+ **Dataset structure**
842
+
843
+ - multiple transactions per customer
844
+ - transaction timestamps
845
+ - customer-level prediction target
846
+
847
+ Potential recommendation:
848
+
849
+ ```text
850
+ group_time
851
+ ```
852
+
853
+ because both customer boundaries and temporal ordering may matter.
854
+
855
+ ---
856
+
857
+ ## 19.2 Medical records
858
+
859
+ If several records belong to the same patient, a random row-level split may allow the same patient to appear in both partitions.
860
+
861
+ Potential strategy:
862
+
863
+ ```text
864
+ group
865
+ ```
866
+
867
+ with:
868
+
869
+ ```python
870
+ group_column="patient_id"
871
+ ```
872
+
873
+ The appropriate choice ultimately depends on the intended deployment scenario.
874
+
875
+ ---
876
+
877
+ ## 19.3 Financial forecasting
878
+
879
+ When historical observations are used to predict future outcomes:
880
+
881
+ ```text
882
+ time
883
+ ```
884
+
885
+ may be more appropriate than a random split.
886
+
887
+ Example:
888
+
889
+ ```python
890
+ time_column="date"
891
+ ```
892
+
893
+ ---
894
+
895
+ ## 19.4 Independent tabular observations
896
+
897
+ For genuinely independent observations where no meaningful entity or temporal boundary exists:
898
+
899
+ ```text
900
+ random
901
+ ```
902
+
903
+ may be appropriate.
904
+
905
+ The key is not to avoid random splitting. The key is to avoid using it automatically when the dataset structure says otherwise.
906
+
907
+ ---
908
+
909
+ # 20. Frequently Asked Questions
910
+
911
+ ## What is SplitPilot?
912
+
913
+ SplitPilot is an explainable Python toolkit for designing and validating machine-learning evaluation splits.
914
+
915
+ Its goal is to help users select a train/test strategy based on dataset structure rather than defaulting to a random split.
916
+
917
+ ---
918
+
919
+ ## Is SplitPilot a machine-learning library?
920
+
921
+ No.
922
+
923
+ SplitPilot does not train models or replace libraries such as scikit-learn.
924
+
925
+ Its focus is the **evaluation-split layer** of the machine-learning workflow.
926
+
927
+ ---
928
+
929
+ ## Does SplitPilot replace `train_test_split()`?
930
+
931
+ No.
932
+
933
+ Random splitting is still a valid strategy when the dataset assumptions support it.
934
+
935
+ SplitPilot provides additional strategies and a recommendation layer for situations where ordinary random sampling may not represent the intended evaluation scenario.
936
+
937
+ ---
938
+
939
+ ## What problem does SplitPilot solve?
940
+
941
+ It addresses structural problems in train/test evaluation, particularly:
942
+
943
+ - repeated entities
944
+ - group overlap
945
+ - temporal ordering
946
+ - potential evaluation leakage caused by inappropriate partitioning
947
+
948
+ ---
949
+
950
+ ## What is data leakage in this context?
951
+
952
+ Data leakage occurs when information that should not be available to the model during evaluation influences the training process.
953
+
954
+ A common structural example is placing observations from the same entity into both training and testing partitions when the intended task is generalization to unseen entities.
955
+
956
+ SplitPilot reduces this particular class of risk by enforcing appropriate group or temporal boundaries.
957
+
958
+ ---
959
+
960
+ ## Why not always use random splitting?
961
+
962
+ Because rows are not always independent.
963
+
964
+ If ten rows represent ten measurements from one customer, randomly splitting those rows is different from splitting ten independent customers.
965
+
966
+ The correct split depends on the question the model is supposed to answer.
967
+
968
+ ---
969
+
970
+ ## What is the difference between group and group-time splitting?
971
+
972
+ ### Group split
973
+
974
+ Separates entities:
975
+
976
+ ```text
977
+ TRAIN: Customer A, B, C
978
+ TEST: Customer D, E
979
+ ```
980
+
981
+ ### Group-time split
982
+
983
+ Attempts to respect both entity boundaries and temporal ordering:
984
+
985
+ ```text
986
+ TRAIN: earlier groups
987
+ TEST: later groups
988
+ ```
989
+
990
+ The latter is useful when both repeated entities and time are important to the evaluation design.
991
+
992
+ ---
993
+
994
+ ## Does group splitting guarantee no leakage?
995
+
996
+ No.
997
+
998
+ It guarantees the specific group-boundary constraint implemented by the splitter.
999
+
1000
+ Other forms of leakage can still come from:
1001
+
1002
+ - feature engineering
1003
+ - preprocessing
1004
+ - target-derived features
1005
+ - duplicated records
1006
+ - improperly constructed labels
1007
+ - external information
1008
+
1009
+ Therefore, group isolation should be considered one leakage-control mechanism rather than a complete leakage detector.
1010
+
1011
+ ---
1012
+
1013
+ ## Does a recommended strategy have to be followed?
1014
+
1015
+ No.
1016
+
1017
+ The recommendation is guidance.
1018
+
1019
+ A domain expert may deliberately choose another strategy if the actual deployment scenario calls for it.
1020
+
1021
+ The important part is that the decision should be intentional and explainable.
1022
+
1023
+ ---
1024
+
1025
+ ## Why is explainability important for a data-splitting library?
1026
+
1027
+ Because evaluation methodology directly affects model metrics.
1028
+
1029
+ If a model achieves 95% accuracy, the next question should be:
1030
+
1031
+ > "95% under what evaluation protocol?"
1032
+
1033
+ A recommendation that explains its reasoning makes that protocol easier to understand, reproduce, and discuss.
1034
+
1035
+ ---
1036
+
1037
+ ## Why use a `src` directory?
1038
+
1039
+ The project uses the standard `src` layout:
1040
+
1041
+ ```text
1042
+ project/
1043
+ └── src/
1044
+ └── splitpilot/
1045
+ ```
1046
+
1047
+ The repository root contains project-level files, while `src/splitpilot` contains the actual Python package.
1048
+
1049
+ This creates a clear separation between source code and repository tooling.
1050
+
1051
+ ---
1052
+
1053
+ ## What does `SplitResult` provide?
1054
+
1055
+ `SplitResult` stores:
1056
+
1057
+ ```text
1058
+ X_train
1059
+ X_test
1060
+ y_train
1061
+ y_test
1062
+ strategy
1063
+ ```
1064
+
1065
+ It therefore provides both the split datasets and information about how the split was generated.
1066
+
1067
+ ---
1068
+
1069
+ ## Can `SplitResult` still be unpacked like a tuple?
1070
+
1071
+ Yes.
1072
+
1073
+ The implementation provides `__iter__`, so this is supported:
1074
+
1075
+ ```python
1076
+ X_train, X_test, y_train, y_test = result
1077
+ ```
1078
+
1079
+ ---
1080
+
1081
+ ## How does SplitPilot handle invalid dates?
1082
+
1083
+ The time-based splitter converts the specified time column to datetime.
1084
+
1085
+ Invalid values are detected and result in a `ValueError` rather than silently producing an invalid chronological split.
1086
+
1087
+ ---
1088
+
1089
+ ## Is SplitPilot production-ready?
1090
+
1091
+ Not yet.
1092
+
1093
+ The project is currently in early development / alpha.
1094
+
1095
+ The current implementation is suitable for development, experimentation, learning, and continued package engineering, but users should review its assumptions before relying on it for critical production evaluation pipelines.
1096
+
1097
+ ---
1098
+
1099
+ # 21. Interview Questions and Answers
1100
+
1101
+ This section is intentionally written as an interview-preparation guide. The goal is to explain the project from both a software-engineering and machine-learning perspective.
1102
+
1103
+ ---
1104
+
1105
+ ## Q1. Explain your project in one minute.
1106
+
1107
+ **Answer:**
1108
+
1109
+ SplitPilot is a Python toolkit I built to make machine-learning train/test splitting more deliberate and explainable.
1110
+
1111
+ The basic problem is that random splitting is not always appropriate. If a dataset contains repeated entities, such as multiple transactions from the same customer, random row-level splitting can place the same customer in both training and testing data. Similarly, time-dependent data should generally respect chronological ordering.
1112
+
1113
+ SplitPilot profiles the dataset, recommends a strategy such as random, group, group-stratified, time, or group-time splitting, and then executes that strategy while exposing the resulting train/test partitions through a structured `SplitResult`.
1114
+
1115
+ The main idea is that dataset splitting is part of evaluation design, not just a preprocessing command.
1116
+
1117
+ ---
1118
+
1119
+ ## Q2. Why did you build SplitPilot?
1120
+
1121
+ **Answer:**
1122
+
1123
+ I wanted to address a practical machine-learning problem that is easy to overlook.
1124
+
1125
+ A model can have an impressive evaluation score while the evaluation protocol itself is unrealistic.
1126
+
1127
+ Instead of building another model-training wrapper, I focused on the data-splitting stage because the split determines what kind of generalization is actually being measured.
1128
+
1129
+ ---
1130
+
1131
+ ## Q3. Why is `train_test_split()` sometimes insufficient?
1132
+
1133
+ **Answer:**
1134
+
1135
+ `train_test_split()` performs a random partition by default. That is appropriate when observations can reasonably be treated as independent and identically distributed for the intended evaluation.
1136
+
1137
+ However, many real datasets contain structure.
1138
+
1139
+ For example, multiple rows may belong to the same customer, patient, device, or account. Other datasets have a meaningful temporal order.
1140
+
1141
+ Randomly mixing those observations can produce an evaluation that does not represent the deployment scenario.
1142
+
1143
+ ---
1144
+
1145
+ ## Q4. What is group leakage?
1146
+
1147
+ **Answer:**
1148
+
1149
+ Group leakage in this context occurs when the same entity appears in both training and testing partitions even though the intended evaluation requires unseen entities.
1150
+
1151
+ For example, if customer A has ten transactions and random splitting places six transactions in training and four in testing, the model has already seen customer A during training.
1152
+
1153
+ A group split prevents this by ensuring:
1154
+
1155
+ ```text
1156
+ TRAIN groups ∩ TEST groups = ∅
1157
+ ```
1158
+
1159
+ ---
1160
+
1161
+ ## Q5. What is temporal leakage?
1162
+
1163
+ **Answer:**
1164
+
1165
+ Temporal leakage occurs when information from the future influences an evaluation that is supposed to represent prediction of future observations.
1166
+
1167
+ For a forecasting-style problem, training should generally use historical information and testing should represent later observations.
1168
+
1169
+ A time split therefore establishes a chronological boundary rather than randomly mixing all observations.
1170
+
1171
+ ---
1172
+
1173
+ ## Q6. Why did you implement both group and time strategies?
1174
+
1175
+ **Answer:**
1176
+
1177
+ They address different structural assumptions.
1178
+
1179
+ A group split answers an entity-generalization question:
1180
+
1181
+ > Can the model generalize to groups it has not seen?
1182
+
1183
+ A time split answers a temporal-generalization question:
1184
+
1185
+ > Can the model use historical data to predict later observations?
1186
+
1187
+ Some datasets require both constraints, which motivated the group-time strategy.
1188
+
1189
+ ---
1190
+
1191
+ ## Q7. Why is group-time splitting difficult?
1192
+
1193
+ **Answer:**
1194
+
1195
+ Because two constraints must be satisfied simultaneously.
1196
+
1197
+ The split should respect group boundaries while also maintaining temporal ordering.
1198
+
1199
+ A naive implementation can accidentally solve one problem while violating the other.
1200
+
1201
+ That is why I added explicit tests for group overlap, chronology, and leakage prevention rather than checking only whether the function returned without an exception.
1202
+
1203
+ ---
1204
+
1205
+ ## Q8. How did you validate your group-time implementation?
1206
+
1207
+ **Answer:**
1208
+
1209
+ I wrote tests covering three important properties:
1210
+
1211
+ 1. No group appears in both train and test.
1212
+ 2. The split respects chronological ordering.
1213
+ 3. The resulting partition prevents the intended structural leakage.
1214
+
1215
+ The current splitter test suite contains 12 tests, and the latest run completed with all 12 passing.
1216
+
1217
+ ---
1218
+
1219
+ ## Q9. Why return a dataclass instead of a tuple?
1220
+
1221
+ **Answer:**
1222
+
1223
+ A tuple is convenient, but it does not communicate what each value represents and does not provide room for metadata.
1224
+
1225
+ `SplitResult` gives named fields:
1226
+
1227
+ ```text
1228
+ X_train
1229
+ X_test
1230
+ y_train
1231
+ y_test
1232
+ strategy
1233
+ ```
1234
+
1235
+ At the same time, I preserved tuple-style unpacking through `__iter__`.
1236
+
1237
+ So the API provides structure without sacrificing familiarity.
1238
+
1239
+ ---
1240
+
1241
+ ## Q10. Why does SplitPilot have a profiler and recommender?
1242
+
1243
+ **Answer:**
1244
+
1245
+ I wanted to separate observation from decision.
1246
+
1247
+ The profiler identifies characteristics of the dataset, such as repeated entities and temporal information.
1248
+
1249
+ The recommender uses those signals to produce a strategy recommendation.
1250
+
1251
+ That separation makes the architecture easier to test and allows the recommendation logic to evolve independently from the actual splitting implementation.
1252
+
1253
+ ---
1254
+
1255
+ ## Q11. Is the recommendation always correct?
1256
+
1257
+ **Answer:**
1258
+
1259
+ No.
1260
+
1261
+ It is a recommendation based on detectable structural signals, not a replacement for domain knowledge.
1262
+
1263
+ For example, detecting a customer identifier does not automatically tell us whether the real deployment scenario involves unseen customers.
1264
+
1265
+ The user still needs to understand the prediction task and choose an evaluation protocol accordingly.
1266
+
1267
+ ---
1268
+
1269
+ ## Q12. Why did you use pandas and scikit-learn?
1270
+
1271
+ **Answer:**
1272
+
1273
+ Pandas is the natural interface for tabular dataset manipulation, and scikit-learn already provides reliable primitives for several splitting operations.
1274
+
1275
+ Instead of reimplementing every low-level operation, SplitPilot builds an explainable layer around those primitives and adds strategies that enforce the structural constraints required by the project.
1276
+
1277
+ ---
1278
+
1279
+ ## Q13. How does the time splitter work?
1280
+
1281
+ **Answer:**
1282
+
1283
+ It first validates the time column, converts it to datetime, checks for invalid values, sorts the dataset chronologically, and then calculates a train/test boundary using the requested test size.
1284
+
1285
+ The important property is that the temporal ordering is established before the partition is created.
1286
+
1287
+ ---
1288
+
1289
+ ## Q14. Why do you validate missing columns yourself?
1290
+
1291
+ **Answer:**
1292
+
1293
+ Explicit validation produces clearer errors.
1294
+
1295
+ For example, if the target column is missing, I raise:
1296
+
1297
+ ```text
1298
+ Target column '...' was not found in the dataset.
1299
+ ```
1300
+
1301
+ This is more useful to the user than allowing a later pandas or scikit-learn operation to fail with a less contextual error.
1302
+
1303
+ ---
1304
+
1305
+ ## Q15. What happens if an invalid strategy is provided?
1306
+
1307
+ **Answer:**
1308
+
1309
+ The splitter raises a `ValueError` identifying the unknown strategy.
1310
+
1311
+ This prevents silent fallback to another splitting method.
1312
+
1313
+ ---
1314
+
1315
+ ## Q16. What was one challenge you encountered while developing the project?
1316
+
1317
+ **Answer:**
1318
+
1319
+ One important challenge was making group-time splitting satisfy both entity and temporal constraints.
1320
+
1321
+ An implementation can appear reasonable while still producing a misleading evaluation boundary.
1322
+
1323
+ I therefore treated the behavior as something to test explicitly rather than assuming that a successful function call meant the strategy was correct.
1324
+
1325
+ ---
1326
+
1327
+ ## Q17. How would you improve SplitPilot?
1328
+
1329
+ **Answer:**
1330
+
1331
+ I would improve it in several stages.
1332
+
1333
+ First, I would strengthen the recommendation heuristics.
1334
+
1335
+ Second, I would make group-stratification more flexible because the current median-based binary grouping is intentionally simple.
1336
+
1337
+ Third, I would add stronger automated leakage diagnostics.
1338
+
1339
+ Finally, I would expand testing across more edge cases and Python versions and add continuous integration.
1340
+
1341
+ ---
1342
+
1343
+ ## Q18. What would you do if a user disagrees with the recommendation?
1344
+
1345
+ **Answer:**
1346
+
1347
+ The recommendation should be treated as guidance rather than an enforced decision.
1348
+
1349
+ I would ask what the intended deployment scenario is and determine whether the recommendation matches that scenario.
1350
+
1351
+ For example, repeated customer records could justify group splitting in one project but not necessarily in another if the model is specifically intended to personalize predictions for existing customers.
1352
+
1353
+ ---
1354
+
1355
+ ## Q19. What makes this project different from simply writing a wrapper around scikit-learn?
1356
+
1357
+ **Answer:**
1358
+
1359
+ The core idea is not merely wrapping an existing function.
1360
+
1361
+ The project separates:
1362
+
1363
+ ```text
1364
+ Dataset analysis
1365
+
1366
+ Strategy recommendation
1367
+
1368
+ Split execution
1369
+
1370
+ Validation
1371
+ ```
1372
+
1373
+ The emphasis is on making evaluation design explainable and structurally aware.
1374
+
1375
+ The package therefore treats splitting as a decision problem rather than only an API call.
1376
+
1377
+ ---
1378
+
1379
+ ## Q20. What is the biggest limitation of the current project?
1380
+
1381
+ **Answer:**
1382
+
1383
+ The recommendation system is heuristic and the project is still in alpha.
1384
+
1385
+ It can identify useful structural signals, but it cannot understand every domain-specific reason why a particular evaluation protocol may be appropriate.
1386
+
1387
+ Also, split-level controls cannot eliminate every form of data leakage.
1388
+
1389
+ ---
1390
+
1391
+ ## Q21. If the model gets 99% accuracy after using SplitPilot, does that mean the model is good?
1392
+
1393
+ **Answer:**
1394
+
1395
+ Not necessarily.
1396
+
1397
+ SplitPilot can help make the evaluation split more appropriate, but model quality still depends on the entire experimental pipeline.
1398
+
1399
+ I would investigate:
1400
+
1401
+ - the deployment scenario
1402
+ - target construction
1403
+ - feature leakage
1404
+ - preprocessing
1405
+ - duplicates
1406
+ - class imbalance
1407
+ - appropriate evaluation metrics
1408
+ - temporal and group boundaries
1409
+
1410
+ The split is an important part of evaluation validity, but it is not the entire evaluation protocol.
1411
+
1412
+ ---
1413
+
1414
+ ## Q22. How would you explain the project to a non-technical interviewer?
1415
+
1416
+ **Answer:**
1417
+
1418
+ Imagine testing a student using questions from a chapter they already memorized.
1419
+
1420
+ The score might be high, but it would not tell us how well they handle new material.
1421
+
1422
+ Machine-learning datasets can have a similar problem.
1423
+
1424
+ If the same customer or future information appears on both sides of the evaluation, the score may not represent the real problem.
1425
+
1426
+ SplitPilot helps design the test so that the evaluation better matches the question we actually want to answer.
1427
+
1428
+ ---
1429
+
1430
+ ## Q23. What software-engineering concepts does this project demonstrate?
1431
+
1432
+ **Answer:**
1433
+
1434
+ The project demonstrates:
1435
+
1436
+ - Python package structure
1437
+ - modular architecture
1438
+ - dataclasses
1439
+ - input validation
1440
+ - exception handling
1441
+ - unit testing with pytest
1442
+ - dependency management
1443
+ - Git version control
1444
+ - semantic project organization
1445
+ - documentation
1446
+ - package metadata through `pyproject.toml`
1447
+
1448
+ It also demonstrates translating machine-learning methodology into reusable software.
1449
+
1450
+ ---
1451
+
1452
+ ## Q24. What would you say if an interviewer asks whether this is "just a small utility"?
1453
+
1454
+ **Answer:**
1455
+
1456
+ I would agree that the current implementation is relatively focused, but the problem it addresses is fundamental to machine-learning experimentation.
1457
+
1458
+ The value is not in the number of lines of code. It is in formalizing an evaluation decision that is often handled informally.
1459
+
1460
+ The project also gives me a foundation for extending the system into automated split diagnostics, leakage analysis, richer recommendations, and reproducible evaluation reports.
1461
+
1462
+ ---
1463
+
1464
+ ## Q25. What did you learn from building SplitPilot?
1465
+
1466
+ **Answer:**
1467
+
1468
+ The main lesson was that machine-learning engineering is not only about model algorithms.
1469
+
1470
+ The reliability of an experiment also depends on how the data is prepared and evaluated.
1471
+
1472
+ Building SplitPilot made me think more carefully about assumptions behind train/test splitting, software interfaces, validation, testing, package structure, and how to communicate technical decisions clearly.
1473
+
1474
+ ---
1475
+
1476
+ # 22. Contributing
1477
+
1478
+ Contributions are welcome.
1479
+
1480
+ A typical development workflow is:
1481
+
1482
+ ```bash
1483
+ git clone https://github.com/krishgupta129/splitpilot.git
1484
+ cd splitpilot
1485
+ pip install -e ".[dev]"
1486
+ pytest tests/test_splitter.py -v
1487
+ ```
1488
+
1489
+ Before opening a pull request:
1490
+
1491
+ 1. Add or update tests for behavioral changes.
1492
+ 2. Run the test suite.
1493
+ 3. Update documentation where necessary.
1494
+ 4. Keep changes focused.
1495
+ 5. Explain the reasoning behind non-obvious implementation decisions.
1496
+
1497
+ ---
1498
+
1499
+ # 23. License
1500
+
1501
+ SplitPilot is distributed under the MIT License.
1502
+
1503
+ See [`LICENSE`](LICENSE) for the full license text.
1504
+
1505
+ ---
1506
+
1507
+ ## Project Philosophy
1508
+
1509
+ SplitPilot is built around a simple principle:
1510
+
1511
+ > **A model evaluation is only as meaningful as the assumptions behind its evaluation protocol.**
1512
+
1513
+ A train/test split is not merely a line of preprocessing code.
1514
+
1515
+ It is a statement about what the model is expected to generalize to:
1516
+
1517
+ ```text
1518
+ Random split
1519
+ → independent observations
1520
+
1521
+ Group split
1522
+ → unseen entities
1523
+
1524
+ Time split
1525
+ → future observations
1526
+
1527
+ Group-time split
1528
+ → future observations from structurally isolated groups
1529
+ ```
1530
+
1531
+ SplitPilot aims to make those assumptions visible, testable, and reusable.