Data Analyst · Aalborg, Denmark

Checks the result
before trusting it.

Several years building Power BI dashboards and SQL pipelines at 2Operate, alongside a Computer Science Master's spent testing whether a more complex model actually performs better, and figuring out why when it doesn't.

I like making sense of complex data, whether through SQL, Excel, or Power BI, and turning it into clear conclusions. Outside of that: exploring new places, learning more math than is strictly necessary, and mixing a decent cocktail.

Selected Work ↓

Selected Work

ROLE: Data Analyst & Software Tester·ORG: 2Operate, Aalborg·PERIOD: 2020–2025

Network Performance Dashboard

2Operate needed a reliable way to track telecom network performance across many sites, using time-series data, in a format the wider team could actually use day to day rather than something only a database query could answer.

Gathered requirements directly from the colleagues who'd use it, then built an interactive Power BI dashboard on top of SQL Server data: hourly and 10-minute granularity controls, state filters, and a power-vs-time view for spotting anomalies. Wrote the DAX and Power Query logic to keep the data accurate as new records landed daily.

Power BI dashboard showing telecom network performance over time, with hour and date filters and a power-versus-time scatter plot
Live filters: hour range, date, 10-minute granularity, and network state

Delivered as a working tool for a specific stakeholder's operational monitoring needs, tracking user throughput through 2–3 KPIs with adjustable time granularity. Built and refined over about two months around a part-time schedule.

Code: repository pending

PROJECT: Bachelor's Graduation Project·PROGRAM: UCN, Computer Science·YEAR: 2023

Inventory Management Database

An inventory system needed a database that stayed accurate and fast as it grew: a schema clean enough to hold up under joins across items, stores, buyers, and employees without slowing down.

Designed a fully normalized relational schema, populated it with structured test data (CSV and Mockaroo) under strict constraint validation, then wrote and tuned the SQL: queries, stored procedures, and indexes, using execution plans to find and fix the slow paths.

Entity-relationship diagram showing Store, Buys, Employee, Buyer, and Items tables and their relationships
Normalized schema: Store, Buys, Employee, Buyer, Items
SQL query joining Items, Author, and AuthorOfBook tables with filtering and sorting
One of the tuned queries, joined across three tables

↓ 98% query row scans after indexing and optimization, on a schema clean enough to serve as a real master-data structure rather than a working prototype.

Code: repository pending

PROJECT: Master's Thesis·PROGRAM: Aalborg University, Computer Science·YEAR: 2025

GSRec: Hybrid Sequential Recommendation

Sequential recommendation models need to balance two signals: what a user is into right now, and how their taste has shifted over a longer history. Transformer-based models like SASRec are strong on the first; recurrent models like GRU are built for the second. The question was whether combining them in one pipeline would outperform using either alone.

Built GSRec: SASRec's self-attention runs first to capture short-term patterns, then feeds into a GRU to model longer-term dependencies. Implemented in Python with PyTorch, and benchmarked against four established baselines, POP, GRU4Rec, SASRec, and BERT4Rec, across two public datasets with different characteristics: Amazon Beauty (sparse, around 7.6 interactions per user) and MovieLens 1M (dense, around 165 interactions per user).

Diagram of the GSRec architecture: input flows through item embedding, positional embedding, multiple self-attention blocks (SASRec), then a gated recurrent unit (GRU), to output
Proposed architecture: SASRec's self-attention stage feeds into a GRU

Standard ranking metrics (HR@10, NDCG@5, NDCG@10, MRR) across both datasets:

Bold = GSRec. Higher is better on every metric.
DatasetMetricPOPGRU4RecSASRecBERT4RecGSRec
BeautyHR@100.08230.21330.24500.27150.2357
NDCG@50.03510.07250.13230.13870.1296
NDCG@100.04420.10350.15170.17730.1349
MRR0.03470.09970.14880.15290.1348
ML-1MHR@100.12830.54070.64290.63850.4212
NDCG@50.03240.30580.40110.41620.2633
NDCG@100.05880.34040.43920.46110.2841
MRR0.04590.29020.35430.39430.2209

GSRec beat the naive popularity baseline and GRU4Rec on the sparse dataset, but didn't outperform SASRec or BERT4Rec outright on either one, and notably underperformed its own SASRec component on the dense dataset. The likely explanation: the GRU stage may be reprocessing a signal SASRec's attention layer had already refined, acting as a bottleneck rather than adding new information, particularly where recent behaviour is most predictive.

Code: repository pending