A few weeks ago, I set out to build a small portfolio project: a Streamlit app that could take any tabular dataset, understand something about its structure, and give honest guidance on how to model it.
I called it ContextLens.
I expected it to be a practical exercise in Python, machine learning, and deployment.
What I didn't expect was how closely it would connect with the same questions I work with every day in my PhD research on context-aware intelligent systems.
Most introductory machine-learning tutorials follow a familiar sequence:
Load a CSV. Choose a model. Train it. Check the accuracy.
What often gets skipped is the layer of judgment that should come before any of that:
Experienced practitioners make these judgments almost automatically. But that reasoning usually remains invisible—it sits in someone's head rather than inside the system, where another person can inspect it.
ContextLens is my attempt to make that layer visible.
Upload a dataset, and it profiles the data, flags structural risks—missingness, duplicate rows, likely identifier columns, class imbalance, and high-dimensional settings—and adapts its evaluation guidance to what it finds before training a single model.
The point is not simply to train a model.
The point is to ask whether the modelling process makes sense in the first place.
I was deliberate about this distinction, just as I have been throughout my PhD work, and it turned out to be the most important design decision in the whole project.
ContextLens does not claim to be intelligent in the way a human expert is. It does not hide its decisions behind a vague "AI-powered" label.
Its context engine is built using explicit, inspectable rules. Anyone can open analysis.py
and read exactly why the application classified a target as categorical, flagged a likely identifier, or recommended macro-F1 instead of relying only on accuracy.
Nothing is hidden behind a black box.
That design choice came directly from my research.
Context-awareness, in the systems I study, isn't about a model mysteriously "understanding" a situation. It is about a system correctly using the structure already present in its environment: the shape of the data, the statistical footprint of a target variable, the number of distinct target values, the distribution of its classes, the amount of missing data, the relationship between the number of features and observations, and sometimes the semantic hints contained in a column name.
In ContextLens, that environment is the dataset itself.
It doesn't need to know what "cholesterol" means to flag that a column called patient_id
probably shouldn't be used as a feature. It also doesn't need to understand a disease to recognise that a disease-outcome column with a 9-to-1 class split needs macro-F1, rather than accuracy, as its headline metric.
That is a limited form of context-awareness, but it is also an honest one.
The first version of ContextLens used an enriched Iris dataset.
I added more than the usual flower measurements: a record identifier, a categorical field, a date-like column, and some missing values. This gave the application more than a clean textbook dataset to work with.
The target was correctly interpreted as a multiclass classification problem. The identifier was flagged. Missing values were detected. Date-like information was recognised and transformed into components usable by the modelling pipeline.
More importantly, none of this depended on the application knowing anything about flowers.
The rules responded to the structure of the dataset rather than its subject.
That was encouraging, but a generic sample wasn't enough.
The real test of that idea came when I added a second sample dataset: the Pima Indians Diabetes dataset.
I didn't build a separate healthcare mode, and I didn't write rules specific to diabetes data. I wanted to see whether the same domain-agnostic checks would still behave sensibly on clinical measurements and a binary health outcome.
They did.
ContextLens recognised the outcome column as a classification target, examined its distribution, and adjusted its evaluation guidance accordingly. It still reported macro-F1, precision, and recall rather than collapsing everything into a single accuracy number.
It didn't need to know what glucose, insulin, body mass index, or blood pressure actually mean to recognise the structural shape of the dataset: a binary target, moderate class imbalance, mostly numerical features, and data that still required careful review before any result could be treated as meaningful.
That was the moment the project started to feel genuinely connected to my research.
The system adapted to the structural context of a healthcare dataset without needing healthcare-specific modelling rules.
But the same test also made the system's limits more visible.
A statistically unusual value isn't automatically a medically invalid one. Missingness can carry clinical meaning rather than simply being noise to impute away. A column that looks harmless from a programming perspective may still require domain expertise to interpret correctly.
A structural profiling tool can surface questions.
It can't answer all of them.
That distinction matters—and it is one I keep circling back to in the research itself.
ContextLens works, but it is still a baseline.
A single train-test split is not equivalent to robust cross-validation. Global feature importance is not the same as causality, and it isn't the same as a local explanation of why a model produced one particular prediction.
Automatic task detection can be useful, but it will not always understand how a variable is intended to be used.
A date column may require chronological validation rather than a random split. Multiple rows may belong to the same patient or device and therefore require group-aware validation. An apparently unique field may still carry legitimate contextual information.
And no amount of automated flagging replaces a domain expert reviewing a dataset before it touches a real decision—especially in healthcare, where the cost of a wrong assumption isn't hypothetical.
These limitations are stated openly in the project's README.
I don't think that weakens the project. It defines its actual scope.
I think about this constantly in my research: the goal is not to automate judgment out of the modelling process; the goal is to make hidden assumptions easier to notice.
One of the most useful lessons came from the modelling pipeline itself.
It is easy to build a dashboard that looks impressive while quietly introducing data leakage.
If missing-value imputation, scaling, or categorical encoding is performed before the train-test split, information from the test data can influence the training process.
To avoid that, ContextLens uses scikit-learn Pipeline
and ColumnTransformer
objects so that preprocessing is learned only from the training data.
That decision isn't visually dramatic. Most users will never see it.
But it is exactly the kind of technical detail that determines whether a machine-learning result is credible.
Building the app reminded me that trustworthy systems are often shaped less by flashy features and more by the decisions that remain invisible to the end user.
I came to this project as someone who has spent time on both sides of the classroom: teaching machine learning and compiler design to students, and now building the research that I hope eventually reshapes what gets taught.
ContextLens began as a portfolio project.
It ended up being a small, working answer to a question my research asks in a much bigger way:
Can a system respond meaningfully to context without pretending to understand more than it actually does?
For now, my answer is yes—but only if its reasoning is visible, its limitations are stated, and its recommendations are treated as support for human judgment rather than a replacement for it. That is the direction I want to take ContextLens next: stronger validation, group- and time-aware splitting, better explainability, more domain testing, and eventually feature-selection methods connected to my research.
The application is live, and the source code is available under the MIT License.