I build pipelines, models, and dashboards that sit underneath decisions that matter to real people: I don't ship a number I can't trace back to its source.
Pursuing an MS in Data Science & Analytics at Grand Valley State University (Dec 2026). Currently building the ETL pipeline and Power BI platform behind reporting for 78 charter schools. Previously a data engineer in healthcare analytics, and now building agentic AI tools that ground every answer in real data instead of guesswork.
Every role I've had has had the same catch: get the data wrong, and it's not an abstraction that breaks: it's a real person on the other end of it.
At Cedar Gate, that meant HIPAA-bound healthcare records. At GVSU's Charter Schools Office, it's FERPA-bound student records for 78 schools: data that gets validated and suppressed before it ever reaches a dashboard, because the person behind each row is a kid. Before that, as a Study Abroad Peer Advisor, the "data" was a student in front of me deciding whether to spend a semester on the other side of the world.
That's the thread running under everything I build now, including the AI agents: I don't let a model, a report, or a dashboard say something it can't back up.
What I've built, what it does, and what came out of it: from a BI platform in progress for 78 schools to deployed AI agents and statistical research.
I own the data pipeline behind GVSU's Charter Schools Office reporting: I've already cleaned, integrated, and validated K-12 data across all 78 schools, and I'm now building the interactive Power BI dashboard that unifies compliance, financial, academic achievement, academic growth, enrollment, and peer-benchmarking reporting into one place per school.
Platform architecture
Diagram reflects the pipeline architecture I built and am building; individual student-level data is FERPA-protected and never shown.
An agentic research assistant that reads uploaded papers or abstracts and produces a structured literature-review synthesis (themes, methodologies, conflicting findings, and research gaps) instead of a generic summary.
An agentic study assistant for graduate coursework that decides, per question, whether it needs a term definition, course-note context, or a study plan before it answers.
A classification study on the NCES Parent & Family Involvement in Education survey, predicting a student's usual letter grade from parental engagement and household background.
Merged state-level census and presidential election data (2008–2016) to test whether income, education, and housing cost differ between states won by each party.
A self-directed modeling portfolio built for STA 631 (Statistical Modeling), covering 8 techniques end-to-end in R using the tidymodels framework: multiple linear regression, polynomial regression, interaction models, multinomial logistic regression, LDA, QDA, Poisson regression, and ridge/lasso penalized regression, applied to the Auto and Diamonds datasets with full diagnostics and cross-validation.
Ten interactive D3.js visualizations exploring what physicochemical properties drive wine quality, built for wine producers, sommeliers, and enthusiasts to explore directly.
Every dashboard I ship has been through a data quality and validation pass first. A clean-looking chart built on unvalidated data is worse than no chart at all.
Whether it's a compliance report or an AI agent's answer, I don't let output get ahead of its source. My agents cite what they used; my reports document where the numbers came from.
A school administrator, a healthcare client, a student deciding on a semester abroad: the end user isn't a data person. If they can't act on it, the analysis isn't finished yet.
Real snippets pulled straight from my own scripts, not illustrative pseudocode.
Census & Election analysis (R) · permutation test
# Two-sample t-test on observed groups ttest_result <- t.test(formula = bach_higher ~ party_clean, data = trial_data, alternative = "two.sided") n_permutations <- 5000 permutation_statistics <- vector(length = n_permutations) for(p in 1:n_permutations) { permutation_statistics[p] <- t.test(formula = bach_higher ~ party_clean, data = trial_data |> mutate(party_clean = sample(party_clean))) |> broom::tidy() |> pull(statistic) }
Census & Election analysis (R) · bootstrap CI
income_data <- state_census |> filter(!is.na(median_income)) |> pull(median_income) B <- 10000 boot_medians <- replicate(B, { sample(income_data, size = length(income_data), replace = TRUE) |> median(na.rm = TRUE) }) boot_se <- sd(boot_medians) boot_ci <- quantile(boot_medians, probs = c(0.025, 0.975))
Family Involvement & Grades (R / tidymodels) · model workflow
multi_spec <- multinom_reg(mode = "classification") %>% set_engine("nnet") multi_wf <- workflow() %>% add_recipe(grade_rec) %>% add_model(multi_spec) set.seed(123) multi_res <- multi_wf %>% fit_resamples( resamples = cv_folds, metrics = metric_set(accuracy) )
STA 631 Modeling Portfolio (R / tidymodels) · LDA spec
lda_spec <- discrim_linear(mode = "classification") %>% set_engine("MASS") lda_wf <- workflow() %>% add_model(lda_spec) %>% add_recipe(class_rec) set.seed(123) lda_cv <- fit_resamples( lda_wf, resamples = class_folds, control = control_resamples(save_pred = TRUE) )
Full role-by-role detail lives in my resume, which I tailor per application. Here's the shape of it:
Open to full-time Data Analyst, Data Engineer, and Data Scientist roles and related roles, available starting December 2026. Open to relocation. Reach out any time.