Back to Workout Gallery View on Github

Workout Detail: 58

🔍 Feature Engineering Playground

Mix and match different time-series metrics as RGB channels to discover hidden patterns. The same workout data reveals different "fingerprints" from different perspectives! NEW: Use the Alpha channel to encode a fourth metric (like Cadence), data quality/confidence, or global RPE (Rated Perceived Exertion)!

Workout Fingerprint

Generated feature image from JSON data

🎯 The Feature Engineering Magic

This 8x8 "visual fingerprint" transforms 64 minutes of time-series data into a 192-element searchable vector. The same workout can reveal completely different patterns when viewed through different RGB channel combinations. Common combinations (like Power/Cadence/HR) are pre-indexed for blazing-fast searches, while others use intelligent pre-filtering. Try changing the channels above to discover hidden similarities!

R: Heart Rate (50-200)
G: Calories Per Min (0-20)
B: Speed Kph (0-15)

Original JSON Data

Note the workout_vector field was added automatically during generation. (And power/cadence are now included!)

{
  "_id": "workout_rad_58",
  "time_series": {
    "heart_rate": [
      100.32,
      105.34,
      106.5,
      114.81,
      116.68,
      150.87,
      155.99,
      164.43,
      169.53,
      170.81,
      169.28,
      169.1,
      174.38,
      177.55,
      173.0,
      176.18,
      171.68,
      167.52,
      174.59,
      163.99,
      162.09,
      161.4,
      157.09,
      147.83,
      143.8,
      144.22,
      132.7,
      129.46,
      120.09,
      119.17,
      110.43,
      107.12,
      99.89,
      94.14,
      89.13,
      85.37,
      80.58,
      75.36,
      74.13,
      64.73,
      59.38,
      62.32,
      55.66,
      59.98,
      54.57,
      56.81,
      59.18,
      57.58,
      61.39,
      63.5,
      61.93,
      68.95,
      65.14,
      71.93,
      70.79,
      82.86,
      89.24,
      85.96,
      94.56,
      87.65,
      99.77,
      103.42,
      108.56,
      116.92
    ],
    "calories_per_min": [
      10.28,
      10.27,
      10.83,
      10.66,
      10.62,
      11.06,
      11.43,
      12.81,
      12.32,
      11.72,
      13.0,
      11.95,
      13.74,
      13.12,
      13.52,
      13.86,
      12.01,
      11.93,
      13.11,
      12.61,
      12.63,
      11.75,
      11.6,
      11.71,
      12.11,
      9.99,
      9.75,
      9.92,
      10.16,
      8.56,
      9.69,
      7.94,
      7.14,
      7.08,
      7.14,
      6.14,
      7.22,
      5.91,
      5.48,
      6.55,
      5.48,
      4.52,
      5.65,
      5.56,
      4.31,
      4.26,
      5.69,
      5.92,
      5.04,
      6.08,
      5.45,
      5.82,
      6.75,
      7.06,
      6.93,
      5.92,
      7.1,
      6.38,
      8.52,
      7.77,
      9.11,
      9.32,
      8.44,
      9.96
    ],
    "speed_kph": [
      2.12,
      2.17,
      2.32,
      2.31,
      2.22,
      5.59,
      5.87,
      5.64,
      5.9,
      5.61,
      5.54,
      5.58,
      5.75,
      5.51,
      5.64,
      5.69,
      5.86,
      5.79,
      5.87,
      5.73,
      5.81,
      5.6,
      5.67,
      5.86,
      5.78,
      5.86,
      5.78,
      5.83,
      5.8,
      5.63,
      5.72,
      5.86,
      5.71,
      5.76,
      5.59,
      5.84,
      5.8,
      5.64,
      5.77,
      5.62,
      5.62,
      5.54,
      5.75,
      5.76,
      5.66,
      5.77,
      5.69,
      5.52,
      5.51,
      5.58,
      5.53,
      5.52,
      5.54,
      5.76,
      5.54,
      5.52,
      5.62,
      5.82,
      5.78,
      1.44,
      1.25,
      1.33,
      1.24,
      1.45
    ],
    "power": [
      171.43,
      185.64,
      191.47,
      192.09,
      193.08,
      208.09,
      213.52,
      212.47,
      219.41,
      218.61,
      217.37,
      220.36,
      224.58,
      219.7,
      226.85,
      229.55,
      225.59,
      231.92,
      231.91,
      220.82,
      215.45,
      220.55,
      211.96,
      207.69,
      207.52,
      209.51,
      201.51,
      192.1,
      186.93,
      192.64,
      178.56,
      177.89,
      167.59,
      164.0,
      162.57,
      164.81,
      158.28,
      156.61,
      143.62,
      135.47,
      144.9,
      142.08,
      134.4,
      133.17,
      125.39,
      135.92,
      123.71,
      122.96,
      125.68,
      128.49,
      123.05,
      130.31,
      126.74,
      136.16,
      143.11,
      135.57,
      151.74,
      147.78,
      150.38,
      160.13,
      157.41,
      170.11,
      178.04,
      179.98
    ],
    "cadence": [
      91.96,
      92.54,
      91.43,
      90.51,
      91.56,
      92.79,
      90.99,
      92.88,
      91.87,
      91.3,
      0.0,
      0.0,
      0.0,
      0.0,
      0.0,
      90.28,
      91.39,
      90.55,
      92.97,
      90.03,
      91.6,
      92.41,
      91.76,
      90.33,
      90.02,
      92.57,
      91.81,
      91.41,
      91.27,
      90.38,
      92.65,
      91.58,
      92.71,
      91.98,
      90.52,
      92.32,
      90.62,
      90.15,
      92.76,
      92.92,
      0.0,
      0.0,
      0.0,
      0.0,
      0.0,
      92.44,
      90.46,
      90.22,
      91.16,
      91.7,
      90.85,
      91.62,
      90.49,
      92.51,
      90.11,
      91.3,
      90.42,
      90.76,
      92.36,
      92.82,
      92.82,
      91.02,
      92.98,
      90.47
    ]
  },
  "data_quality": [
    0,
    255,
    255,
    255,
    255,
    0,
    255,
    255,
    255,
    255,
    0,
    0,
    0,
    0,
    0,
    255,
    255,
    255,
    255,
    255,
    255,
    255,
    255,
    255,
    255,
    255,
    255,
    255,
    255,
    255,
    255,
    255,
    0,
    0,
    255,
    255,
    255,
    255,
    255,
    255,
    0,
    0,
    0,
    0,
    0,
    255,
    255,
    255,
    255,
    255,
    255,
    255,
    255,
    255,
    255,
    255,
    255,
    0,
    255,
    255,
    255,
    255,
    255,
    255
  ],
  "rpe": 9.0,
  "start_time": "2025-10-27 10:28:00",
  "workout_type": "Yoga",
  "session_tag": "Threshold",
  "post_session_notes": {
    "hydration_ml": 1145,
    "notes": "Felt good"
  },
  "gear_used": [
    {
      "item": "shoes_v3",
      "kilometers": 52.0
    },
    {
      "item": "hrm_strap",
      "battery_life_percent": 19
    }
  ],
  "ai_classification": "Pending Analysis",
  "ai_summary": "Click 'Generate AI Summary' to analyze",
  "llm_analysis_prompt": "(not yet generated)",
  "workout_vector": "[85.00... 191 more elements]",
  "workout_vector_power_cadence_hr": [
    109,
    195,
    85,
    118,
    196,
    94,
    122,
    194,
    96,
    122,
    192,
    110,
    123,
    194,
    113,
    132,
    197,
    171,
    136,
    193,
    180,
    135,
    197,
    194,
    139,
    195,
    203,
    139,
    194,
    205,
    138,
    0,
    202,
    140,
    0,
    202,
    143,
    0,
    211,
    140,
    0,
    216,
    144,
    0,
    209,
    146,
    191,
    214,
    143,
    194,
    206,
    147,
    192,
    199,
    147,
    197,
    211,
    140,
    191,
    193,
    137,
    194,
    190,
    140,
    196,
    189,
    135,
    194,
    182,
    132,
    191,
    166,
    132,
    191,
    159,
    133,
    196,
    160,
    128,
    195,
    140,
    122,
    194,
    135,
    119,
    193,
    119,
    122,
    192,
    117,
    113,
    196,
    102,
    113,
    194,
    97,
    106,
    197,
    84,
    104,
    195,
    75,
    103,
    192,
    66,
    105,
    196,
    60,
    100,
    192,
    51,
    99,
    191,
    43,
    91,
    197,
    41,
    86,
    197,
    25,
    92,
    0,
    15,
    90,
    0,
    20,
    85,
    0,
    9,
    84,
    0,
    16,
    79,
    0,
    7,
    86,
    196,
    11,
    78,
    192,
    15,
    78,
    191,
    12,
    80,
    193,
    19,
    81,
    194,
    22,
    78,
    193,
    20,
    83,
    194,
    32,
    80,
    192,
    25,
    86,
    196,
    37,
    91,
    191,
    35,
    86,
    194,
    55,
    96,
    192,
    66,
    94,
    192,
    61,
    95,
    196,
    75,
    102,
    197,
    64,
    100,
    197,
    84,
    108,
    193,
    90,
    113,
    197,
    99,
    114,
    192,
    113
  ],
  "workout_vector_power_speed_hr": [
    109,
    36,
    85,
    118,
    36,
    94,
    122,
    39,
    96,
    122,
    39,
    110,
    123,
    37,
    113,
    132,
    95,
    171,
    136,
    99,
    180,
    135,
    95,
    194,
    139,
    100,
    203,
    139,
    95,
    205,
    138,
    94,
    202,
    140,
    94,
    202,
    143,
    97,
    211,
    140,
    93,
    216,
    144,
    95,
    209,
    146,
    96,
    214,
    143,
    99,
    206,
    147,
    98,
    199,
    147,
    99,
    211,
    140,
    97,
    193,
    137,
    98,
    190,
    140,
    95,
    189,
    135,
    96,
    182,
    132,
    99,
    166,
    132,
    98,
    159,
    133,
    99,
    160,
    128,
    98,
    140,
    122,
    99,
    135,
    119,
    98,
    119,
    122,
    95,
    117,
    113,
    97,
    102,
    113,
    99,
    97,
    106,
    97,
    84,
    104,
    97,
    75,
    103,
    95,
    66,
    105,
    99,
    60,
    100,
    98,
    51,
    99,
    95,
    43,
    91,
    98,
    41,
    86,
    95,
    25,
    92,
    95,
    15,
    90,
    94,
    20,
    85,
    97,
    9,
    84,
    97,
    16,
    79,
    96,
    7,
    86,
    98,
    11,
    78,
    96,
    15,
    78,
    93,
    12,
    80,
    93,
    19,
    81,
    94,
    22,
    78,
    94,
    20,
    83,
    93,
    32,
    80,
    94,
    25,
    86,
    97,
    37,
    91,
    94,
    35,
    86,
    93,
    55,
    96,
    95,
    66,
    94,
    98,
    61,
    95,
    98,
    75,
    102,
    24,
    64,
    100,
    21,
    84,
    108,
    22,
    90,
    113,
    21,
    99,
    114,
    24,
    113
  ],
  "workout_vector_speed_cadence_hr": [
    36,
    195,
    85,
    36,
    196,
    94,
    39,
    194,
    96,
    39,
    192,
    110,
    37,
    194,
    113,
    95,
    197,
    171,
    99,
    193,
    180,
    95,
    197,
    194,
    100,
    195,
    203,
    95,
    194,
    205,
    94,
    0,
    202,
    94,
    0,
    202,
    97,
    0,
    211,
    93,
    0,
    216,
    95,
    0,
    209,
    96,
    191,
    214,
    99,
    194,
    206,
    98,
    192,
    199,
    99,
    197,
    211,
    97,
    191,
    193,
    98,
    194,
    190,
    95,
    196,
    189,
    96,
    194,
    182,
    99,
    191,
    166,
    98,
    191,
    159,
    99,
    196,
    160,
    98,
    195,
    140,
    99,
    194,
    135,
    98,
    193,
    119,
    95,
    192,
    117,
    97,
    196,
    102,
    99,
    194,
    97,
    97,
    197,
    84,
    97,
    195,
    75,
    95,
    192,
    66,
    99,
    196,
    60,
    98,
    192,
    51,
    95,
    191,
    43,
    98,
    197,
    41,
    95,
    197,
    25,
    95,
    0,
    15,
    94,
    0,
    20,
    97,
    0,
    9,
    97,
    0,
    16,
    96,
    0,
    7,
    98,
    196,
    11,
    96,
    192,
    15,
    93,
    191,
    12,
    93,
    193,
    19,
    94,
    194,
    22,
    94,
    193,
    20,
    93,
    194,
    32,
    94,
    192,
    25,
    97,
    196,
    37,
    94,
    191,
    35,
    93,
    194,
    55,
    95,
    192,
    66,
    98,
    192,
    61,
    98,
    196,
    75,
    24,
    197,
    64,
    21,
    197,
    84,
    22,
    193,
    90,
    21,
    197,
    99,
    24,
    192,
    113
  ],
  "experiment_id": "data_imaging"
}

AI Radiologist Summary (Mixed/Variable)

This qualitative summary is generated by an LLM (OpenAI API). Crucially, the AI does not perform the math. This app first uses Python (NumPy) to deterministically calculate all key metrics (Avg HR, speed deviation, etc.). The AI is then given these pre-computed facts (viewable via 'Inspect LLM Prompt') to provide a qualitative, radiologist-style interpretation. It must be manually generated by clicking the button.

Click 'Generate AI Summary' to analyze

Atlas Vector Search (Workout Twins)

Powered by VoyageAI

Using a $vectorSearch pipeline in MongoDB Atlas with index data_imaging_workout_vector_index to find the 3 workouts most similar to this one's 192-element vector (excluding itself). Higher score means more similar pattern.

Why VoyageAI Reranking?

We use a two-stage retrieval pipeline for the most accurate "Workout Twins":

  1. Fast Candidate Generation: MongoDB's vector search finds 25 structurally similar workouts in milliseconds
  2. Semantic Reranking: VoyageAI's advanced model reads the text descriptions and finds the 3 most semantically similar workouts

This combines the speed of vector search with the deep semantic understanding of VoyageAI's reranker, giving you more accurate and relevant workout matches.

💡 Feature Engineering Insight: The vector below uses the indexed default channels (HR, Calories, Speed) for fast vector search. But different channel combinations reveal different patterns! Common combinations like Power/Cadence/HR are also indexed for blazing-fast searches. Try selecting Power, Cadence, or other metrics above and click "Update View & Find Similar" to discover workouts that are similar in completely different ways.

Hybrid Search: The AI Vibe-Check + The Human Filter

🎯 The Problem: Vector search is amazing for fuzzy, "vibe-based" questions ("find more workouts like this"). But it's terrible at precise, factual questions ("...but only the ones on a Monday").

✨ The Solution: Hybrid Search combines vector search (vibe) with text search (facts) and filters (exact matches). This is how people actually think - we blend fuzzy concepts ("vibe," "felt strong") with concrete facts ("Outdoor Run").

💡 Example: "Show me workouts with a vibe similar to my last 'Tempo Run' (vector search)... but only show me ones where my notes said I 'felt strong' (text search)... and where the workout type was 'Outdoor Run' (filter)."

Vector "Magic": Asking Abstract Questions with Math

🎯 The Problem: How do you ask the database for "a harder version of this yoga session"? You can't type that into a search bar. But with vector arithmetic, we can discover abstract concepts like "effort" and use them to find workouts that match abstract queries!

✨ The "Magic" Formula:

  1. Step 1: Find the "essence of effort"
    Vector(Hard Tempo Run) - Vector(Easy Recovery Run) = "Effort Vector"
    This new Effort Vector is a set of 192 numbers that, to our AI, literally represents the abstract concept of 'high effort'.
  2. Step 2: Apply it to find what you want
    Vector(My Easy Yoga Session) + "Effort Vector" = ???
    Take this new, mathematically-created vector and run a search with it.
  3. Step 3: Discover the results
    The database will return "Power Yoga" sessions, "Intense Vinyasa Flows," or other high-intensity yoga workouts!

🚀 Why this is genius: We've stopped just retrieving data. We are now exploring the latent space of our workouts. We can ask abstract questions like "what's the halfway point between a run and a bike ride?" or "show me a workout that's 50% less intense than this one."

🧮 Try Vector Magic

1️⃣ Find Harder Version

Automatically find harder versions using neighbors:

💡 Click below to automatically use neighbors

Manual override

2️⃣ Find Easier Version

Automatically find easier versions using neighbors:

💡 Click below to automatically use neighbors

Manual override

3️⃣ Scale Intensity

Scale this workout's intensity to find variations:

The Encoding Pipeline: From Data to Fingerprint

1 Start: 1D Time-Series Data (64 mins)

This is where the magic begins: We start with raw time-series data—64 data points per metric, representing one workout session. Each metric (heart rate, calories, speed, power, cadence) tells its own story. The challenge? How do we make this searchable? How do we find similar patterns without comparing every single data point?

💡 Feature Engineering Tip: Notice how we have 5 different metrics but only use 3 at a time. This gives us 5×4×3 = 60 different ways to view the same workout! Each combination reveals different patterns.

Heart Rate (1D Array)

Heart Rate Line Chart

Calories (1D Array)

Calories Line Chart

Speed (1D Array)

Speed Line Chart

Power (1D Array)

Power Line Chart

Cadence (1D Array)

Cadence Line Chart

2 "Fold" 1D (64) into 2D (8x8 Grids)

Each 64-point array is reshaped into an 8x8 grid. Think of it like taking a long string of 64 characters and wrapping it every 8 characters to form an 8x8 block of text. This turns temporal patterns into visual textures.

[0,1,2,3,4,5,6,7,8,9,10,...,61,62,63]
01234567 89101112131415 1617181920212223 2425262728293031 3233343536373839 4041424344454647 4849505152535455 5657585960616263

Minutes 0..7 become Row 1 (highlighted). Minutes 8..15 become Row 2, etc. We now have three separate 8x8 grayscale "channels".

3 Stack Channels into 8x8x3 RGB Image

The three 8x8 grayscale grids (channels) generated in Step 2 are combined pixel-by-pixel:

Red channel

Heart Rate data provides the pixel values for the Red channel.

Green channel

Calories Per Min data provides the pixel values for the Green channel.

Blue channel

Speed Kph data provides the pixel values for the Blue channel.

These channels are layered together...

Combined RGB

...to create the final 8x8 RGB color "fingerprint" image.

4 Finish: Flatten to 192-Element Vector

The 8x8x3 image (8 rows * 8 columns * 3 color channels = 192 values) is "flattened" row by row into a single list of 192 numbers. This vector numerically captures the visual pattern.

Vector = [Pixel(0,0)R, Pixel(0,0)G, Pixel(0,0)B, Pixel(0,1)R, ..., Pixel(7,7)B]

This is the vector stored in MongoDB Atlas and used for blazing-fast $vectorSearch!

🎯 The Feature Engineering Breakthrough

Why is this clever? We've transformed time-series data (which is hard to search) into a fixed-size vector (which is fast to search). Instead of comparing 64×5=320 data points for every workout, we compare one 192-element vector. This enables:

  • Indexed search: MongoDB builds an optimized HNSW index on these vectors
  • Millisecond queries: Find similar workouts in milliseconds, even with millions of records
  • Pattern preservation: The visual "fingerprint" captures the shape and progression of effort
  • Multiple perspectives: Different RGB channel combinations reveal different types of similarity

🚀 Try it: Change the RGB channels above and click "Find Similar" to discover workouts that are similar in power/cadence patterns vs. heart rate/calorie patterns. Same data, completely different insights!

A Radiologist's Guide: Reading the Fingerprint

Think of this 8x8 image like a tiny X-ray of your workout's effort structure:

Polymorphic / Additional Fields

This data provides extra context but isn't part of the vector search. The specific fields here vary depending on the workout_type.

Workout Type: Yoga

Session Tag: Threshold

Post-Session Notes: Hydration: 1145 ml, Notes: Felt good