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

Your runs. Your questions.

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

RUNNING QUERYAn analysis language for your running history.
Checking query...Ctrl / Cmd Enter to run
{% if error %}{% endif %}
Whole-run metrics · {{ 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

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 .
START WITHBiggest run gapsWeekday patternsRolling volumeWeekly volumeLong-run progressionPace at similar heart rateRunning form
{% if results %}

{{ results.count }} matching {{ 'run' if results.count == 1 else 'runs' }}

{{ 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 }} runs 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 %}
{% if query.chart != 'none' %}
TRENDS / {{ query.group|upper }}

{{ 'Your question over time' if query.group in ('day','week','month','year') else 'Your question by ' ~ query.group }}

Solid: current · Dashed: previous
{% for series in results.chart.series %}

{{ series.label }} {{ series.unit }}

{% endfor %}

Select a current-period point to inspect its runs. {% if query.trend %}Trend: {{ query.trend }} · Summary cards and the grouped table show the untransformed values.{% else %}Missing data leaves a gap.{% endif %} Time comparisons align by elapsed days; categorical comparisons use the same categories.

{% endif %} {% endif %} {% if results.chart.scatter %}
RELATIONSHIP / RUN AVERAGES

{{ results.chart.scatter.x[0] }} vs {{ results.chart.scatter.y[0] }}

{% if results.chart.scatter.correlation is not none %}Pearson r = {{ '%.3f'|format(results.chart.scatter.correlation) }}{% else %}Correlation unavailable{% endif %}

Each point is one run. Select a point to open that run. Correlation describes association; it does not establish cause.

{% 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 %}
GROUPRUNS{{ card.label|upper }} · {{ card.unit }}
{{ group.count }}{{ value }}
BEHIND THE NUMBERS

Contributing runs

Export runs
{% for card in results.evidence_columns %}{% endfor %}{% for row in results.rows %}{% for value in row['values'] %}{% endfor %}{% else %}{% endfor %}
RUN / 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 runs per page{% if results.page > 1 %}Previous{% endif %}{% if results.page < results.pages %}Next{% endif %}
{% endif %}

Gaps, rolling metrics and streaks use your complete 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 %}