x Cory Ziller - Portfolio
Cory Ziller

Hey there,

I'm Cory Ziller

Current

MBA candidate at Ohio State (Class of 2027).

Previously

5+ years in marketing, data and analytics, and product thinking.

Early Experience

AI startup experience supporting law enforcement adoption of anti-trafficking technology.

I love turning ideas into automation. Check it out!

Writing Samples & Side Projects

Technical writing and automation workflows that showcase data skills

TRACK NVIDIA GPU SENTIMENT ACROSS REDDIT & HACKER NEWS

Automates NVIDIA GPU sentiment tracking. Scrapes Reddit and Hacker News daily, analyzes feedback, and emails a 30-second audio summary.

Automation Workflow

The Idea

NVIDIA product managers need to track user sentiment to prioritize product features and understand pain points, but checking Reddit and Hacker News daily takes hours. Automate the entire process.

What I Built

Scrape
Reddit & HN
Python + APIs
→
Analyze
Sentiment
OpenAI API
→
Generate
Audio Brief
Google TTS
→
Email
Auto-deliver
Brevo API

The Impact

Daily automated insights from Reddit & Hacker News on product sentiment. Zero manual work. Always fresh data.

See It In Action

Try the workflow

Get a sample email with real sentiment data and an audio briefing.

SPOTIFY TRENDS: 13 YEARS OF LISTENING

Visualizes 13+ years of Spotify data showing artist evolution, top tracks, and listening patterns from 2012-2026. 6,500+ hours analyzed.

Automation Workflow & Real-Time Dashboard

The Idea

Spotify Wrapped only gives you a snapshot of one year. What if you could reimagine it across a time horizon of over a decade, updated daily? That is what this project does. It takes 13+ years of listening data and turns it into a living, interactive dashboard that reveals how taste actually evolves over time.

What I Built

Collect
Spotify Web API
OAuth + History
→
Analyze
Python
Trends & Patterns
→
Visualize
Chart.js
Dashboard
→
Deploy
GitHub Pages
Live Site
→
Automate
Spotify API
CI/CD Daily

The Impact

A fully interactive dashboard analyzing 6,500+ hours across 4,800+ unique artists. Updated daily via CI/CD. Reveals clear listening eras, from A Cappella and Broadway roots to R&B and Indie dominance, across a 13-year time horizon that Spotify Wrapped never shows you.

See It In Action

Dive into the data

Experience the full dashboard with interactive charts and deep listening history.

PANDAS VS RAPIDS cuDF: GPU SPEED TEST

Run a live simulation comparing CPU-based Pandas with NVIDIA's GPU-accelerated RAPIDS cuDF on 100M rows of paid media data.

Technical Writing Example 1

The Idea

Most data teams don't realize how much performance they leave on the table with CPU-based Pandas. I wanted to make that gap impossible to ignore. So I built an interactive speed test that visualizes CPU vs GPU performance in real time, then wrote a technical blog post breaking down RAPIDS cuDF for data engineering audiences.

What's Inside

▶
Interactive Speed Test
Racing bar simulation comparing Pandas vs cuDF on 5 operations across 100M rows
✎
Product Blog Post
Hypothetical NVIDIA developer blog — code samples, benchmarks, and RAPIDS ecosystem positioning

Key Result

41x faster groupby on 100M rows. What takes Pandas 12.4 seconds completes in 0.3 seconds on a single NVIDIA T4 GPU — with one import change. The article walks data teams through exactly why and how to switch.

Try It Yourself

Run the Speed Test & Read the Article

Click to launch the interactive Pandas vs cuDF benchmark, then read the full product blog post I wrote in NVIDIA's voice.

RECONSTRUCTING CONVERSION PATHS IN SQL

A parameterized SQL query that reconstructs full conversion paths by joining raw impression and click logs within configurable lookback windows. Outputs ranked touchpoints and path-level metrics for downstream multi-touch attribution analysis.

Technical Writing Example 2

The Idea

Most attribution reporting stops at last-click. I wrote a structured SQL query that reconstructs the full path to conversion by pulling every impression and click that occurred within configurable lookback windows before a conversion event. The README documents each transformation stage so any analyst can understand, adapt, and run the logic independently.

What the Query Does

Given a set of conversion events, the SQL query joins against raw impression and click logs to find every touchpoint that preceded a conversion within the lookback window. It then deduplicates overlapping events, ranks touchpoints by recency, and computes path-level metrics like touch position, path length, and time since first/last interaction.

The output is a flattened, touch-level dataset ready for multi-touch attribution models, channel sequencing analysis, and creative performance reporting.

What the README Covers

6-Step Query Structure
Filter, match, pull lookback interactions, deduplicate, rank, and output
Edge Cases + Scaling
Deduplication logic, long paths, missing metadata, partition-aware filtering
GPU Acceleration Relevance
How large joins and GROUP BY operations can scale with accelerated data processing frameworks
Read the Full Doc

Open the README

The full SQL query documentation. Walks through every step of reconstructing conversion paths from raw impression and click data.

Spotify Listening Analytics

Data through August 16, 2026 | 2012-2026

Loading analytics...

Work History & Education

5+ years in marketing, data and analytics, and product thinking

Work
June – August 2027 • NYC, NY
Bank of America
Summer Internship
Strategy and Management
2021 – 2025 • Chicago, IL
Basis Technologies
Chicago-based advertising technology company
Data Analyst → Senior Data Analyst
2019 – 2020 • Washington D.C.
Yelp
Account Executive
2017 – 2019 • Pittsburgh, PA
Marinus Analytics
AI startup combating human trafficking with technology
Impact & Business Development Intern
Education
Expected May 2027 • Columbus, OH
The Ohio State University
MBA — Fisher College of Business
2015 – 2019 • State College, PA
Penn State University
BS Information Science & Technology (App Design and Development) • BS Applied Spanish

Pandas vs RAPIDS cuDF

GPU-Accelerated Data Processing Speed Test

Hypothetical Writing Sample

Below is a hypothetical NVIDIA developer blog post I wrote to demonstrate how I would market RAPIDS cuDF to data engineering teams. This is the type of product-focused technical content this role produces.

Your CPU Is the Bottleneck. The GPU Is the Fix.

If your team runs ETL jobs in Pandas, you are leaving massive performance on the table. A groupby() on 100M rows takes 12.4 seconds on CPU. On an NVIDIA GPU, it takes 0.3 seconds. That is a 41x speedup with zero code changes. Your dashboards could be real-time. Your pipelines could finish before the next one starts. The hardware already exists in every major cloud provider. You just need to use it.

RAPIDS cuDF is NVIDIA's open-source GPU DataFrame library. It implements the Pandas API on top of NVIDIA CUDA cores, so your existing Python code works with a single import swap. No CUDA programming required. No new syntax. Same API. Same code. GPU speed.

One Import Change. Massive Speedup.

PANDAS (CPU)
import pandas as pd
df = pd.read_csv('campaign_data.csv')
result = df.groupby('campaign_id').agg({
  'spend': 'sum',
  'conversions': 'sum'
})
RAPIDS cuDF (GPU)
import cudf
df = cudf.read_csv('campaign_data.csv')
result = df.groupby('campaign_id').agg({
  'spend': 'sum',
  'conversions': 'sum'
})

The code is identical. cuDF implements the full Pandas API surface, so your existing workflows, Jupyter notebooks, and scikit-learn preprocessing pipelines run on GPU without refactoring. You do not need to write any CUDA kernels or learn a new framework.

Benchmarks: 100M Rows of Paid Media Data

We benchmarked five core DataFrame operations on a 100-million-row ad performance dataset (the kind of table you would process for campaign reporting). Environment: Pandas 2.1 on a 4-core CPU vs. cuDF 24.02 on a single NVIDIA T4 GPU.

Operation Pandas (CPU) cuDF (GPU) Speedup
Read CSV8.2s0.4s20x
GroupBy Aggregation12.4s0.3s41x
Merge / Join15.1s0.6s25x
Sort Values6.8s0.2s34x
Boolean Filter3.2s0.1s32x
41x faster groupby. What took 12.4 seconds on CPU completes in 300ms on GPU. For ETL pipelines running hourly, this compounds into hours saved per day. It is the difference between real-time dashboards and stale data.

Drop Into Any Stack. Accelerate Immediately.

cuDF is not a new architecture. It is a drop-in acceleration layer for the Python data stack your team already uses. A typical pipeline queries a data warehouse (BigQuery, Redshift, Snowflake), transforms in Python, then feeds results into ML models or BI dashboards. cuDF accelerates that Python transformation step on NVIDIA GPUs, turning minutes of processing into seconds without rewriting a single pipeline.

Because cuDF is built on NVIDIA CUDA, it leverages GPU parallelism across every operation: reads, joins, aggregations, sorts, and filters. The performance gap between CPU and GPU widens as data volume increases. At 100M+ rows, there is no comparison.

cuDF also integrates with the broader RAPIDS ecosystem. cuML provides GPU-accelerated machine learning (random forests, XGBoost, k-means). cuGraph handles graph analytics on GPU. Dask-cuDF scales DataFrames across multi-GPU clusters for terabyte-scale workloads. Every layer of the pipeline, from ETL to model training, benefits from GPU acceleration.

The GPU Advantage Compounds

A single ETL job running 41x faster is notable on its own. But the real value compounds at scale. Hourly pipelines finish in real-time. Feature engineering no longer bottlenecks model training. Dashboards refresh in seconds instead of minutes. Every downstream process accelerates because the data layer got faster.

NVIDIA GPUs are already deployed across AWS, GCP, and Azure. They are available free on Google Colab. There is no hardware barrier. The only thing between your team and 10-50x faster data processing is one import change.

README.md

Reconstructing Conversion Paths in SQL

Post-View + Post-Click Lookbacks. Sanitized from documentation from my previous analytics role. All client names, credentials, and proprietary details removed.

conversion_paths.sql sanitized
-- ============================================
-- RECONSTRUCTING CONVERSION PATHS IN SQL
-- Post-View + Post-Click Lookbacks
-- ============================================
-- Sanitized from a previous analytics role.
-- All client names and credentials removed.

DECLARE start_date DATE DEFAULT '2024-01-01';
DECLARE end_date DATE DEFAULT '2024-03-31';
DECLARE click_look INT64 DEFAULT 30;
DECLARE imp_look INT64 DEFAULT 7;
DECLARE campaign_filter STRING DEFAULT r'(?i)(brand|prospecting|retargeting)';

WITH filtered_conversions AS (
  SELECT
    c.user_id,
    c.conversion_id,
    c.activity_id,
    c.activity AS activity_name,
    c.event_time AS conversion_time,
    DATE(c.event_time) AS conversion_date
  FROM `project.dataset.conversions` c
  WHERE DATE(c.event_time) BETWEEN start_date AND end_date
    AND c.advertiser_id = '[REDACTED]'
    AND REGEXP_CONTAINS(c.campaign, campaign_filter)
),

conversion_anchors AS (
  SELECT
    fc.*,
    CONCAT(fc.user_id, '-', fc.conversion_id) AS pseudo_id,
    COALESCE(i.user_id, cl.user_id) AS matched_user_id
  FROM filtered_conversions fc
  LEFT JOIN `project.dataset.impressions` i
    ON fc.user_id = i.user_id
    AND i.event_time < fc.conversion_time
  LEFT JOIN `project.dataset.clicks` cl
    ON fc.user_id = cl.user_id
    AND cl.event_time < fc.conversion_time
  GROUP BY ALL
),

prior_impressions AS (
  SELECT
    ca.pseudo_id,
    ca.conversion_time,
    i.event_time AS interaction_time,
    'impression' AS interaction_type,
    i.campaign_id, i.site_id, i.placement_id, i.creative_id
  FROM conversion_anchors ca
  JOIN `project.dataset.impressions` i
    ON ca.matched_user_id = i.user_id
    AND i.event_time < ca.conversion_time
    AND i.event_time >= TIMESTAMP_SUB(ca.conversion_time,
        INTERVAL imp_look DAY)
),

prior_clicks AS (
  SELECT
    ca.pseudo_id,
    ca.conversion_time,
    cl.event_time AS interaction_time,
    'click' AS interaction_type,
    cl.campaign_id, cl.site_id, cl.placement_id, cl.creative_id
  FROM conversion_anchors ca
  JOIN `project.dataset.clicks` cl
    ON ca.matched_user_id = cl.user_id
    AND cl.event_time < ca.conversion_time
    AND cl.event_time >= TIMESTAMP_SUB(ca.conversion_time,
        INTERVAL click_look DAY)
),

all_interactions AS (
  SELECT * FROM prior_impressions
  UNION ALL
  SELECT * FROM prior_clicks
),

deduped_ranked AS (
  SELECT
    pseudo_id,
    conversion_time,
    interaction_time,
    interaction_type,
    campaign_id, site_id, placement_id, creative_id,
    ROW_NUMBER() OVER (
      PARTITION BY pseudo_id
      ORDER BY interaction_time DESC
    ) AS interaction_rank
  FROM (
    SELECT DISTINCT *
    FROM all_interactions
  )
),

path_metrics AS (
  SELECT
    dr.*,
    COUNT(*) OVER (PARTITION BY pseudo_id) AS path_length,
    MIN(interaction_time) OVER (PARTITION BY pseudo_id)
      AS first_touch_time,
    MAX(interaction_time) OVER (PARTITION BY pseudo_id)
      AS last_touch_time,
    CASE
      WHEN interaction_rank = 1 THEN 'Last'
      WHEN interaction_rank = COUNT(*) OVER (
        PARTITION BY pseudo_id) THEN 'First'
      ELSE 'Middle'
    END AS touch_position
  FROM deduped_ranked dr
),

final_output AS (
  SELECT
    pm.pseudo_id,
    fc.activity_name,
    fc.activity_id,
    fc.conversion_date,
    LEAST(pm.path_length, 30) AS path_length,
    DATE_DIFF(fc.conversion_date,
      DATE(pm.first_touch_time), DAY)
      AS days_since_first_interaction,
    DATE_DIFF(fc.conversion_date,
      DATE(pm.last_touch_time), DAY)
      AS days_since_last_interaction,
    DATE(pm.interaction_time) AS interaction_date,
    pm.interaction_rank AS interaction_number,
    pm.interaction_type,
    COALESCE(camp.campaign_name, 'N/A') AS campaign,
    COALESCE(s.site_name, 'N/A') AS site,
    COALESCE(p.placement_name, 'N/A') AS placement,
    COALESCE(cr.creative_name, 'N/A') AS creative,
    pm.touch_position
  FROM path_metrics pm
  JOIN filtered_conversions fc
    ON pm.pseudo_id = CONCAT(fc.user_id, '-', fc.conversion_id)
  LEFT JOIN `project.dataset.campaigns` camp
    ON pm.campaign_id = camp.campaign_id
  LEFT JOIN `project.dataset.sites` s
    ON pm.site_id = s.site_id
  LEFT JOIN `project.dataset.placements` p
    ON pm.placement_id = p.placement_id
  LEFT JOIN `project.dataset.creatives` cr
    ON pm.creative_id = cr.creative_id
)

SELECT * FROM final_output
ORDER BY pseudo_id, interaction_number;
Context
1 / 7
Parameters
The query starts by declaring configurable inputs: date range, lookback windows for clicks (30 days) and impressions (7 days), and a regex filter for campaign types. An analyst can change these without touching the SQL logic.
Example
An analyst selects a Q1 date range with 30-day click and 7-day impression windows to analyze a specific media campaign across Facebook, Google, and DSP — just by updating the five parameters at the top. No other SQL needs to change.