{% extends 'base.html' %} {% block title %}Stats studio · Stride{% endblock %} {% block head %}{% endblock %} {% block content %}
YOUR RUNNING / STATS STUDIO

{{ selected_analysis.name if selected_analysis else "Your runs. Your questions." }}

Write a query, explore the pattern, and inspect the runs behind it.

{% include '_analysis_workspace.html' %}
RUNNING QUERYAn analysis language for your running history.
Checking query...Ctrl / Cmd Enter to run
{% if error %}{% endif %}
{{ 'Whole-run metrics' if query.source == 'runs' else 'Recorded laps' if query.source == 'laps' else 'Five-minute moving segments' }} · {{ current_user.timezone }}
Lower pace values mean faster running.
Language reference & examples

Filter and compare

during last 6 months
where distance between 8 and 20 km
and pace <= 5:30 /km
and has cadence
and title contains "River"
sort by pace asc

Periods: rolling days/weeks/months, explicit dates, or all time. Groups: day, week, month, year, weekday, hour, run, all, or metric bins. Operators: >, >=, <, <=, =, !=. AND binds before OR. Comments start with # on their own line.

Analysis operations

summarize count(), mean(distance), p90(pace)
having count() >= 3 runs
rank by mean(distance) desc
limit 10
trend rolling 4 buckets
let efficiency = speed / heart_rate
plot pace vs heart_rate
color by elevation_per_km
size by distance
baseline previous 90 days
flag above baseline 1.5 sd min 3 runs sustained 2 buckets
let hr_14d = rolling_mean(heart_rate, 14 days)
summarize mean(heart_rate) as "Matched HR"

Sources: runs, laps, segments. Charts: auto, line, bar, area, histogram, box, heatmap, stacked, none. Use display overlay to combine series. Functions: sum, mean, weighted_mean, median, min, max, count, population stddev, and p10/p25/p75/p90/p95/p99. take limits ranked runs before grouping; limit limits groups. Trends also accept cumulative, change and percent_change. Arithmetic uses km, hours and seconds/km. Missing inputs and zero divisors give missing values.

Metrics · select up to six

{% for key, info in metrics.items() %}{% endfor %}

duration accepts h, min or s; distance accepts km or m. Use has / missing to select recorded sensor coverage.

Guided controls

Adjust the basic clauses without removing additional metrics and filters.

Show and
{% if query.all_time or query.start or query.days or query.months %}Change the period to the last{% else %}from runs during the last{% endif %} weeks, grouped by .
{% if results %}

{{ results.count }} matching {{ query.source }}

{{ results.start }} – {{ results.end }} · {{ '%.1f'|format(results.distance) }} km{% if query.compare %}
Compared with {{ results.previous_start }} – {{ results.previous_end }} · {{ results.prior_count }} runs{% endif %}

{% if not results.count %}

No runs match this question.

Try a longer period or widen the distance and heart rate filters.

{% else %}
{% for card in results.cards %}
{{ card.label }}
{{ card.value }}{{ card.unit }}
{% if query.compare %}

Previous: {{ card.previous }} {{ card.unit }}{% if card.delta %} · {{ card.delta }} {{ 'sec/km' if card.unit == '/km' else card.unit }}{% endif %}

{% endif %}

{{ card.coverage }} of {{ results.count }} {{ query.source }} with usable data

How this is calculated

{{ card.definition }}{% if card.key in ('cadence','power','ground_contact','stride_length') %} Coverage counts runs, not sensor recording time.{% endif %}

{% endfor %}
EXPLORE / VISUALIZE

Your question, visualized

Select a point, box or calendar day to inspect contributing runs. Distribution charts show individual run inputs. Solid: current; dashed: previous. {% if query.trend %}Trend: {{ query.trend }}; cards and tables retain untransformed values.{% endif %}

{% endif %}
ANALYSIS / GROUPED RESULTS

{{ query.group|capitalize }} breakdown

Export CSV
{% for card in results.cards %}{% endfor %}{% for group in results.groups %}{% for value in group['values'] %}{% endfor %}{% endfor %}
GROUP{{ query.source|upper }}{{ card.label|upper }} · {{ card.unit }}
{{ group.count }}{{ value }}
BEHIND THE NUMBERS

Contributing {{ query.source }}

Export runs
{% for card in results.evidence_columns %}{% endfor %}{% for row in results.rows %}{% for value in row['values'] %}{% endfor %}{% else %}{% endfor %}
{{ query.source|upper }} / LOCAL DATEDISTANCE{{ card.label|upper }} · {{ card.unit }}
{{ row.title }}
{{ row.date }}
{% if results.show_previous %}Previous: {% if row.previous_run %}{{ row.previous_run.date }} · {{ row.previous_run.title }}{% else %}No earlier recorded run{% endif %}{% endif %}
{{ row.distance }} km{{ value }}
No matching runs.
Page {{ results.page }} of {{ results.pages }} · up to 25 {{ query.source }} per page{% if results.page > 1 %}Previous{% endif %}{% if results.page < results.pages %}Next{% endif %}
{% endif %}

Sources: runs, recorded laps, or five-minute moving segments. Segments exclude recording gaps and pauses; partial segments remain queryable. Gaps, rolling metrics and streaks use complete run history before filters. Arithmetic and filters apply per run; summaries apply per group. Contributing runs show the individual input values for each summary. Empty sums remain missing; count is zero. Group ranking/limits and having select the displayed cohort before chart trends.

{% endblock %} {% block scripts %}{% endblock %}