Jekyll2021-07-13T09:46:40+00:00https://limegimlet.github.io/feed.xmlLearning, unsupervisedTeaching myself to train my mind, my body, and datasets as part of a mid-life career change.Sarah HoskingEtape du Tour 1012019-01-31T14:10:24+00:002019-01-31T14:10:24+00:00https://limegimlet.github.io/etape-du-tour-101<p>So last year my big hairy athletic goal was to complete the Etape du Tour. Half a year later I’m still being asked about it (at least, by other sporty people. Non-sporty types eyes just glaze over when I say the word “cyclosportive”).</p>
<p>Here are some of the most frequent questions:</p>
<h2 id="so-what-is-etape-du-tour-exactly">So what is Etape du Tour, exactly?</h2>
<p>Well, you’ve heard of the Tour de France, where the pros ride around France over 3 weeks? Each day during this 3-week period is known as a ‘stage’, or ‘Etape’ in French. The Etape du Tour follows the route from one of the mountain stages, and opens it up as a timed event for amateurs a few days before the pro’s ride though.</p>
<p>The cool thing about it is riders benefit from many of the perks of the actual Tour, namely:</p>
<ul>
<li>closed course, which makes descending reallly fun</li>
<li>
<h2 id="how-long-was-it">How long was it?</h2>
</li>
</ul>
<p>In 2018 it was 169km, with 3700m elevation gain. It started in Annecy, and followed a circuituous route to end in Le Grand Bornand.</p>
<h2 id="is-it-the-same-course-every-year">Is it the same course every year?</h2>
<p>Does the Tour de France follow the same route every year? No. Ergo, the route for the Etape du Tour is in a different location, and distance and elevation gain vary too.</p>
<h2 id="if-youre-a-lawyer-dr-c-suite-type-or-simply-hyper-competitive-and-dont-already-know-me-so-how-long-did-it-take-to-finish">[If you’re a lawyer, dr, C-suite type or simply hyper-competitive and don’t already know me] So how long did it take to finish?</h2>
<p>Please see next question.</p>
<h2 id="if-you-already-know-me-so-how-did-you-find-it">[If you already know me] So how did you find it?</h2>
<p>It was by far the most enjoyable sporting event I’d ever done. It was also the hardest by a long shot, but that’s why I had signed up and would have been disappointed if it hadn’t felt hard.</p>
<p>And since I usually have a good time during events, so that’s saying a lot.</p>
<h2 id="how-high-were-the-climbs">How high were the climbs?</h2>
<p>Last year’s cols were all pretty low: the final col, Col de la Columbière, was the highest at only ~1600m. They were also short: the longest climb was the first, to Col de la Croix-Fry, which was 11km. The 3 other major climbs were less than 10k.</p>
<p>But what they lacked in length and height was made up by the average grades. The easiest col has an average grade of 7.5%, the others had average grades from 8% to 11%. And that’s average, as in, they’ll be stretches well above the average. The steepness is what, by far, terrified me the most for EdT2018.</p>
<p>[For 2019, they’ve gone the other way: the final climb for 2019 EdT is 34km and the col is at 2300m, but the average grade is in the 4-5% range.]</p>
<p>The second thing that terrified me was….</p>
<h2 id="is-there-a-time-limit">Is there a time limit?</h2>
<p>F*ck yeah. Just Google ‘Etape broom’ to find out.</p>
<p>A chillingly common refrain in previous Etape race reports was the pressure to stay ahead of the broom wagon (la voiture balai). The broom wagon (supposedly) creeps along as the lowest average speed to make the cut-off. If the wagon passes you at any point, a race official comes out to cross out your race number, then forces you to stow your bike & board the wagon for the long slow ride of shame back to the finish area, stopping of course to sweep up other defeated riders along the way. It’s the cycling equivalent of the ‘Dementor’s Kiss’.</p>
<p>I’d even seen a forum comment about someone who got swept up after 7km. Ugh. Was really hoping that was just an Etape myth.</p>
<p>The extra sadistic twist to the time limit is you don’t know what it is until a few weeks before the event. I think this is partly due to how negotiations with local officials for road closures pan out.</p>
<p>Since a major reason to do EdT was to bolster my confidence (athletic & otherwise), getting swept up by the broom wagon would be the best way for that plan to backfire, spectactularly.</p>
<p>So between the prospect of steep grades & broom wagon humiliation, I was <em>really</em> motivated to train.</p>
<h2 id="so-how--how-much-did-you-train">So how & how much did you train?</h2>
<p>This would be a post or a series unto itself. To be honest I would never go into this kind of detail in real-person conversatiion but if you’re read this far, presumably you have a more than casual interest in Etape.</p>
<p>Also, keep in mind this was for me: a 40-something injury-prone recreational cyclist who’s longest endurance event up that point was doing an olympic-distance triathlon. My only strengths going into this was I’m used to sharing the road, either with cars or lots of other cyclists, am (finally) a decent descender, and a husband who a) loves bike maintenance and b) has already done a couple of EdT, and c) is happy to ride at wife-friendly pace.</p>
<p>Endurance, and particularly strength endurance, were my major weaknesses. And yeah, a ruptured lumbar disk. They make climbing rather uncomfortable.</p>
<p>From early Dec 2017 right up to Etape on July 8, I followed a 28 week program by doing the TrainerRoad base, build & specialized plans. But I in fact started my training the day after I signed up, in late Oct 2017, which was simply getting on my bike for first time since early September.</p>
<insert a="" time="" series="" plot="" of="" bars="" showing="" training="" hours="">
## Would you do it again?
Definitely. The combination of scenery, ambiance and organization is hard to beat. That being said, the different start and end location can make logistics a right royal pain in the arse. 2018 was pretty good, logistics-wise, since the finish was only 30km from the start.
## So really, how DID you do?
![Typical riding in the Chevreuse](https://limegimlet.github.io/assets/images/chevreuse.jpg)
And for those of you for whom the title of this post made you think of a certain hit song from the late 80s, here's your reward for reading to the end of the post.
<iframe src="https://open.spotify.com/embed/track/369JKykZZr1qzv9pg5zAhz" width="300" height="300" frameborder="0" allowtransparency="true" allow="encrypted-media“&view=coverart&theme=white"></iframe>
Check out the [Jekyll docs][jekyll-docs] for more info on how to get the most out of Jekyll. File all bugs/feature requests at [Jekyll’s GitHub repo][jekyll-gh]. If you have questions, you can ask them on [Jekyll Talk][jekyll-talk].
[StravistiX]: https://chrome.google.com/webstore/detail/stravistix-for-strava/dhiaggccakkgdfcadnklkbljcgicpckn
[jekyll-gh]: https://github.com/jekyll/jekyll
[jekyll-talk]: https://talk.jekyllrb.com/
</insert>Sarah HoskingSo last year my big hairy athletic goal was to complete the Etape du Tour. Half a year later I’m still being asked about it (at least, by other sporty people. Non-sporty types eyes just glaze over when I say the word “cyclosportive”).Strava ride data with Python & Pandas2019-01-09T00:00:00+00:002019-01-09T00:00:00+00:00https://limegimlet.github.io/strava_ride_data<p>After spending the past 2.5 months madly finishing up projects, cycling took a serious back seat. Which is OK, because as you will see, cycling took the driver’s seat for much of 2018.</p>
<p>Now that the projects are done (or at least at v1), I’ve been itching to look at my Strava data again, this time with Python. In fact, I’ve been itching to look at my ride data a helluva lot more than to go riding.</p>
<p>That’s a bit of a problem, fitness-wise, although it gave me even more incentive to connect to the Strava API.</p>
<p>Here are some simple overview plots using variations on Pandas’ <code class="language-plaintext highlighter-rouge">df.plot(kind = 'barh')</code> that show my annual progressions for some ride stats.</p>
<p>For the sake of clarity, you’ll find all the code to get, clean & plot Strava data <a href="#code">after the plots</a>.</p>
<h3 id="how-much-time-did-i-spend-on-the-bike">How much time did I spend on the bike?</h3>
<p><img src="/images/strava_data_files/strava_data_14_0.png" alt="png" /></p>
<h3 id="how-many-rides-were-done-on-the-trainer">How many rides were done on the trainer?</h3>
<p><img src="/images/strava_data_files/strava_data_17_0.png" alt="png" /></p>
<p>This could explain why I found myself dreading the trainer by the end of the year.</p>
<h3 id="how-far-did-i-go">How far did I go?</h3>
<p><img src="/images/strava_data_files/strava_data_18_0.png" alt="png" /></p>
<h3 id="and-how-much-climbing-was-involved">And how much climbing was involved?</h3>
<p><img src="/images/strava_data_files/strava_data_19_0.png" alt="png" /></p>
<p>Admittedly, these can hardly compete with the slick video Strava sends you at the end of the year. However, it has been satisfying being able to look at stats that Strava doesn’t talk about much, if at all, like rides done on the trainer, as well as being able to see my annual progression.</p>
<h1 id="code">Code</h1>
<h2 id="get-the-data">Get the data</h2>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># import librairies
</span>
<span class="kn">import</span> <span class="nn">urllib2</span>
<span class="kn">import</span> <span class="nn">json</span>
<span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="n">pd</span>
<span class="kn">import</span> <span class="nn">matplotlib</span> <span class="k">as</span> <span class="n">plt</span>
<span class="o">%</span><span class="n">matplotlib</span> <span class="n">inline</span>
<span class="kn">import</span> <span class="nn">seaborn</span> <span class="k">as</span> <span class="n">sns</span>
<span class="n">sns</span><span class="p">.</span><span class="nb">set</span><span class="p">()</span>
<span class="c1"># For the base URL we use for activities
</span><span class="n">token</span> <span class="o">=</span> <span class="s">'c3f*********'</span> <span class="c1"># replace with your token
</span><span class="n">base_url</span> <span class="o">=</span> <span class="s">"https://www.strava.com/api/v3/activities?access_token=%s&per_page=200&page="</span> <span class="o">%</span> <span class="n">token</span>
<span class="n">page_num</span> <span class="o">=</span> <span class="mi">1</span>
<span class="c1"># To store the json response
</span><span class="n">my_acts</span> <span class="o">=</span> <span class="s">"my_activities.txt"</span>
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">cols</span> <span class="o">=</span> <span class="p">[</span><span class="s">"start_date"</span><span class="p">,</span><span class="s">"type"</span><span class="p">,</span><span class="s">"name"</span><span class="p">,</span><span class="s">"id"</span><span class="p">,</span><span class="s">"average_cadence"</span><span class="p">,</span>
<span class="s">"average_heartrate"</span><span class="p">,</span><span class="s">"average_speed"</span><span class="p">,</span> <span class="s">"distance"</span><span class="p">,</span>
<span class="s">"moving_time"</span><span class="p">,</span> <span class="s">"location_city"</span><span class="p">,</span> <span class="s">"pr_count"</span><span class="p">,</span><span class="s">"suffer_score"</span><span class="p">,</span>
<span class="s">"trainer"</span><span class="p">,</span> <span class="s">"total_elevation_gain"</span><span class="p">,</span> <span class="s">"workout_type"</span><span class="p">,</span><span class="s">"map"</span><span class="p">]</span>
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">### Starter code adapted from http://pdwhomeautomation.blogspot.fr/2014/11/raspberry-pi-and-strava-api-1.html
</span>
<span class="k">def</span> <span class="nf">get_strava_data</span><span class="p">(</span><span class="n">token</span> <span class="o">=</span> <span class="n">token</span><span class="p">,</span> <span class="n">filename</span> <span class="o">=</span> <span class="n">my_acts</span><span class="p">):</span>
<span class="c1">#Open the my_file to use
</span> <span class="n">my_file</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span><span class="s">'w'</span><span class="p">)</span>
<span class="c1">#Loop extracting data. Remember it comes in pages
</span> <span class="n">end_found</span> <span class="o">=</span> <span class="bp">False</span>
<span class="n">page_num</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">strava_df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">DataFrame</span><span class="p">(</span><span class="n">columns</span> <span class="o">=</span> <span class="n">cols</span><span class="p">)</span>
<span class="c1">#Main loop - Getting all activities
</span> <span class="k">while</span> <span class="p">(</span><span class="n">end_found</span> <span class="o">==</span> <span class="bp">False</span><span class="p">):</span>
<span class="c1">#Do a HTTP Get - First form the full URL
</span> <span class="n">act_url</span> <span class="o">=</span> <span class="n">base_url</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">page_num</span><span class="p">)</span>
<span class="n">strava_json</span> <span class="o">=</span> <span class="n">urllib2</span><span class="p">.</span><span class="n">urlopen</span><span class="p">(</span><span class="n">act_url</span><span class="p">).</span><span class="n">read</span><span class="p">()</span>
<span class="k">if</span> <span class="n">strava_json</span> <span class="o">!=</span> <span class="s">"[]"</span><span class="p">:</span> <span class="c1"># Checks for empty JSON response, i.e. end of activities
</span> <span class="c1">#Now we process the JSON
</span> <span class="n">act_json</span> <span class="o">=</span> <span class="n">json</span><span class="p">.</span><span class="n">loads</span><span class="p">(</span><span class="n">strava_json</span><span class="p">)</span>
<span class="n">page_df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">DataFrame</span><span class="p">(</span><span class="n">act_json</span><span class="p">)</span>
<span class="c1"># reduce number of fields in df
</span> <span class="c1"># page_df = page_df[cols] TO FIX: key error with avg HR
</span> <span class="n">strava_df</span> <span class="o">=</span> <span class="n">strava_df</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">page_df</span><span class="p">)</span>
<span class="c1">#Set up for next loop
</span> <span class="n">cum_sum</span> <span class="o">=</span> <span class="n">page_num</span> <span class="o">*</span> <span class="mi">200</span>
<span class="k">print</span><span class="p">(</span><span class="s">"{} activities printed..."</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">cum_sum</span><span class="p">))</span>
<span class="n">page_num</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">strava_json</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">end_found</span> <span class="o">=</span> <span class="bp">True</span>
<span class="k">print</span><span class="p">(</span><span class="s">"*** No more activities. ***"</span><span class="p">)</span>
<span class="k">return</span> <span class="n">strava_df</span>
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="c1"># create data frame
</span>
<span class="n">strava_df</span> <span class="o">=</span> <span class="n">get_strava_data</span><span class="p">()</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>200 activities printed...
400 activities printed...
600 activities printed...
800 activities printed...
1000 activities printed...
1200 activities printed...
*** No more activities. ***
</code></pre></div></div>
<h2 id="data-wrangling">Data wrangling</h2>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">## Helper functions
</span>
<span class="k">def</span> <span class="nf">s_to_hrs</span><span class="p">(</span><span class="n">secs</span><span class="p">):</span>
<span class="n">m</span><span class="p">,</span> <span class="n">s</span> <span class="o">=</span> <span class="nb">divmod</span><span class="p">(</span><span class="n">secs</span><span class="p">,</span> <span class="mi">60</span><span class="p">)</span>
<span class="n">h</span><span class="p">,</span> <span class="n">m</span> <span class="o">=</span> <span class="nb">divmod</span><span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="mi">60</span><span class="p">)</span>
<span class="c1">#d, h = divmod(h, 24)
</span> <span class="k">return</span><span class="p">(</span><span class="s">"{} hrs, {} m"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">h</span><span class="p">,</span><span class="n">m</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">clean_strava</span><span class="p">(</span><span class="n">df</span> <span class="o">=</span> <span class="n">strava_df</span><span class="p">,</span> <span class="n">cols</span> <span class="o">=</span> <span class="n">cols</span><span class="p">):</span>
<span class="c1"># limit columns
</span> <span class="n">mystrava</span> <span class="o">=</span> <span class="n">df</span><span class="p">.</span><span class="n">copy</span><span class="p">()[</span><span class="n">cols</span><span class="p">]</span>
<span class="n">mystrava</span><span class="p">[</span><span class="s">'start_date'</span><span class="p">]</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">to_datetime</span><span class="p">(</span><span class="n">mystrava</span><span class="p">[</span><span class="s">'start_date'</span><span class="p">],</span> <span class="n">yearfirst</span> <span class="o">=</span> <span class="bp">True</span><span class="p">,</span> <span class="n">infer_datetime_format</span> <span class="o">=</span> <span class="bp">True</span><span class="p">)</span>
<span class="c1"># extract month & year
</span> <span class="n">mystrava</span><span class="p">[</span><span class="s">'year'</span><span class="p">]</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">DatetimeIndex</span><span class="p">(</span><span class="n">mystrava</span><span class="p">[</span><span class="s">'start_date'</span><span class="p">]).</span><span class="n">year</span>
<span class="n">mystrava</span><span class="p">[</span><span class="s">'month'</span><span class="p">]</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">DatetimeIndex</span><span class="p">(</span><span class="n">mystrava</span><span class="p">[</span><span class="s">'start_date'</span><span class="p">]).</span><span class="n">month</span>
<span class="c1"># convert moving time to integer
</span> <span class="n">convert_cols</span> <span class="o">=</span> <span class="p">[</span><span class="s">'moving_time'</span><span class="p">,</span> <span class="s">'pr_count'</span><span class="p">,</span> <span class="s">'suffer_score'</span><span class="p">,</span> <span class="s">'trainer'</span><span class="p">]</span>
<span class="n">mystrava</span><span class="p">[</span><span class="n">convert_cols</span><span class="p">]</span> <span class="o">=</span> <span class="n">mystrava</span><span class="p">[</span><span class="n">convert_cols</span><span class="p">].</span><span class="n">fillna</span><span class="p">(</span><span class="mi">0</span><span class="p">).</span><span class="n">astype</span><span class="p">(</span><span class="nb">int</span><span class="p">)</span>
<span class="c1"># convert distance to KMs
</span> <span class="n">mystrava</span><span class="p">[</span><span class="s">'distance'</span><span class="p">]</span> <span class="o">=</span> <span class="n">mystrava</span><span class="p">[</span><span class="s">'distance'</span><span class="p">].</span><span class="n">divide</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
<span class="k">return</span> <span class="n">mystrava</span>
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># clean up & filter data
</span><span class="n">types</span> <span class="o">=</span> <span class="p">[</span><span class="s">'Ride'</span><span class="p">]</span>
<span class="n">q</span> <span class="o">=</span> <span class="s">"type in @types & year < 2019 & year > 2009"</span>
<span class="n">mystrava</span> <span class="o">=</span> <span class="n">clean_strava</span><span class="p">().</span><span class="n">query</span><span class="p">(</span><span class="n">q</span><span class="p">)</span>
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># sum activities by year
</span><span class="n">mystrava_by_yr</span> <span class="o">=</span> <span class="n">mystrava</span><span class="p">.</span><span class="n">set_index</span><span class="p">([</span><span class="s">'id'</span><span class="p">]).</span><span class="n">groupby</span><span class="p">([</span><span class="s">'year'</span><span class="p">,</span><span class="s">'type'</span><span class="p">]).</span><span class="nb">sum</span><span class="p">()</span>
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># create moving_hrs
</span><span class="n">mystrava_by_yr</span><span class="p">[</span><span class="s">'moving_hrs'</span><span class="p">]</span> <span class="o">=</span> <span class="n">mystrava_by_yr</span><span class="p">[</span><span class="s">'moving_time'</span><span class="p">].</span><span class="nb">apply</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">s_to_hrs</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">## plot function
</span><span class="n">types_str</span> <span class="o">=</span> <span class="s">", "</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">types</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">act_bar</span><span class="p">(</span><span class="n">df</span> <span class="o">=</span> <span class="n">mystrava_by_yr</span><span class="p">,</span> <span class="n">metric</span> <span class="o">=</span> <span class="s">'moving_time'</span><span class="p">,</span> <span class="n">unit</span> <span class="o">=</span> <span class="s">'seconds'</span><span class="p">,</span> <span class="n">legend</span> <span class="o">=</span> <span class="bp">False</span><span class="p">):</span>
<span class="n">ax</span> <span class="o">=</span> <span class="n">df</span><span class="p">.</span><span class="n">reset_index</span><span class="p">().</span><span class="n">query</span><span class="p">(</span><span class="n">q</span><span class="p">).</span><span class="n">set_index</span><span class="p">(</span><span class="s">'year'</span><span class="p">)[[</span><span class="s">'type'</span><span class="p">,</span> <span class="n">metric</span><span class="p">]]</span>\
<span class="p">.</span><span class="n">pivot</span><span class="p">(</span><span class="n">columns</span> <span class="o">=</span> <span class="s">'type'</span><span class="p">,</span> <span class="n">values</span> <span class="o">=</span> <span class="n">metric</span><span class="p">)</span>\
<span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">kind</span><span class="o">=</span><span class="s">'barh'</span><span class="p">,</span> <span class="n">legend</span> <span class="o">=</span> <span class="n">legend</span><span class="p">,</span>
<span class="n">title</span> <span class="o">=</span> <span class="s">"{} {}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="nb">str</span><span class="p">.</span><span class="n">title</span><span class="p">(</span><span class="n">types_str</span><span class="p">),</span> <span class="n">metric</span><span class="p">))</span>
<span class="n">ax</span><span class="p">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s">""</span><span class="p">)</span>
<span class="n">ax</span><span class="p">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="n">unit</span><span class="p">);</span>
</code></pre></div></div>
<h2 id="plots">plots</h2>
<h1 id="summary-plots">Summary plots</h1>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># how much time did I spend on the bike?
</span><span class="n">act_bar</span><span class="p">()</span>
</code></pre></div></div>
<p><img src="/images/strava_data_files/strava_data_14_0.png" alt="png" /></p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># how much is that in hours & minutes?
</span><span class="n">mystrava_by_yr</span><span class="p">.</span><span class="n">query</span><span class="p">(</span><span class="n">q</span><span class="p">)[</span><span class="s">'moving_hrs'</span><span class="p">]</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>year type
2014 Ride 103 hrs, 50 m
2015 Ride 146 hrs, 29 m
2016 Ride 69 hrs, 55 m
2017 Ride 167 hrs, 32 m
2018 Ride 319 hrs, 35 m
Name: moving_hrs, dtype: object
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># how many rides?
</span><span class="n">mystrava</span><span class="p">.</span><span class="n">query</span><span class="p">(</span><span class="n">q</span><span class="p">).</span><span class="n">groupby</span><span class="p">([</span><span class="s">'year'</span><span class="p">]).</span><span class="n">count</span><span class="p">()[</span><span class="s">'id'</span><span class="p">].</span><span class="n">sort_index</span><span class="p">(</span><span class="n">ascending</span> <span class="o">=</span> <span class="bp">False</span><span class="p">)</span>
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>year
2018 133
2017 80
2016 43
2015 72
2014 35
Name: id, dtype: int64
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># how many of those were on the trainer?
</span><span class="n">mystrava</span><span class="p">.</span><span class="n">query</span><span class="p">(</span><span class="n">q</span><span class="p">).</span><span class="n">groupby</span><span class="p">([</span><span class="s">'year'</span><span class="p">,</span> <span class="s">'trainer'</span><span class="p">]).</span><span class="n">count</span><span class="p">()[</span><span class="s">'id'</span><span class="p">].</span><span class="n">unstack</span><span class="p">()</span>\
<span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">kind</span> <span class="o">=</span> <span class="s">'barh'</span><span class="p">,</span>
<span class="n">title</span> <span class="o">=</span> <span class="s">"{} count"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">types_str</span><span class="p">));</span>
</code></pre></div></div>
<p><img src="/images/strava_data_files/strava_data_17_0.png" alt="png" /></p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># how far did I go?
</span><span class="n">act_bar</span><span class="p">(</span><span class="n">metric</span> <span class="o">=</span> <span class="s">"distance"</span><span class="p">,</span> <span class="n">unit</span> <span class="o">=</span> <span class="s">'Kilometers'</span><span class="p">)</span>
</code></pre></div></div>
<p><img src="/images/strava_data_files/strava_data_18_0.png" alt="png" /></p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># how high did I climb?
</span><span class="n">act_bar</span><span class="p">(</span><span class="n">metric</span> <span class="o">=</span> <span class="s">'total_elevation_gain'</span><span class="p">,</span> <span class="n">unit</span> <span class="o">=</span> <span class="s">"Meters"</span><span class="p">)</span>
</code></pre></div></div>
<p><img src="/images/strava_data_files/strava_data_19_0.png" alt="png" /></p>Sarah HoskingAfter spending the past 2.5 months madly finishing up projects, cycling took a serious back seat. Which is OK, because as you will see, cycling took the driver’s seat for much of 2018.How I trained for EdT 20182018-12-31T09:00:00+00:002018-12-31T09:00:00+00:00https://limegimlet.github.io/etape/cycling/training/big-bike-year<iframe width="900" height="500" frameborder="0" scrolling="no" src="//plot.ly/~limegimlet/71.embed"></iframe>
<p>In October 2017 I was diagnosed with a herniated lumbar disc, and learned along the way that my left hip had early-stage arthritis. I shelved—yet again!—plans to run a half-marathon (perhaps, um, forever). Running in my 40s has been a story of unrequited love.</p>
<p>Fortunately, I didn’t have to sulk too long. Around the same time, the Etape du Tour registrations opened up. Time to return to my old faithful, the bike!</p>
<p>[screenshot of confirmation]</p>
<p>169 km, 3800m elevation gain. OMG WHAT HAVE I SIGNED UP FOR?</p>
<p><img src="/assets/2019-02/edt_2018_profil.jpg" alt="jpg" /></p>
<h2 id="reality-check">Reality check</h2>
<p>The challenges:</p>
<ul>
<li>
<p>Living in central Paris means long rides are only realistic on weekends. It also means those rides are essentially flat.</p>
</li>
<li>
<p>My longest ride, ever, was 138km and going along the Loire, was dead flat. My climby-est ride, around Girona, was 123km with 1900m elevation. Both took me all day, and featured lunch as well as multiple coffee stops.</p>
</li>
</ul>
<iframe height="405" width="590" frameborder="0" allowtransparency="true" scrolling="no" src="https://www.strava.com/activities/305780576/embed/2888fe3783fb151d703ec9e2afe6c8fd125f08fd"></iframe>
<p>99></p>
<ul>
<li>
<p>I’m 46 years old. So what (so asks those in their 20s & early 30s)? Well, although I exercise far more consistently than when I was younger, I’m now prone to overuse injuries, so my fitness progression in since turning 40 can be best summed up as two steps forward, one step back. Perhaps even more significantly, it’s made me very leery about pushing myself too much.</p>
</li>
<li>
<p>And yeah, on that note, my back pain was far from over.</p>
</li>
</ul>
<p>But, lest I sound like a total Eeyore, I still had a few aces up my sleeve:</p>
<ul>
<li>
<p>Living in Paris also means it’s only a few hours to many rideable mountainous roads via TGV.</p>
</li>
<li>
<p>Husband is also a cyclist, of the mountain goat variety. And he is a dab hand with bike maintenance.</p>
</li>
<li>
<p>I actually enjoy figuring out new routes in unknown areas, and am fine with riding on my own if need be.</p>
</li>
</ul>
<h2 id="how-much-did-i-train">How much did I train?</h2>
<p>The short answer? A lot.</p>
<p>[bike hours]</p>
<p>[bike kms]</p>
<p>[bike elevation]</p>
<p>Keep in mind, your mileage may vary! There were two guys from my club, both with classic distance running bods & fitness to match who put in far fewer hours on the bike (especially in the mountains) than I did, yet finished hours ahead of me.</p>
<p>And another guy in my club, also young and fit but with a more muscular build, who hardly had ridden at all who did manage to finish, but took even longer than I did.</p>
<h2 id="how-did-i-do-it">How did I do it?</h2>
<p>Home trainer, baby.</p>
<p>[bike outdoors/indoors]</p>
<p>Not on its own, of course. To keep it easy, I just followed TrainerRoad’s Base/Build/Speciality training plans - 27 weeks of training. To make it fun(ish), I subscribed to Netflix and was only allowed to watch my ‘workout shows’ if I was on the trainer.</p>
<p>Every single trip I did between Oct 17 and July 8 involved biking. From January onward, in fact, ever trip was FOR biking.</p>
<p>Pre-build</p>
<p>Oct & Nov</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Get on trainer Barcelona UB
</code></pre></div></div>
<p>Build (12 weeks)</p>
<p>Dec
Jan & Feb:</p>
<p>Mar: Strada Bianche</p>
<p>April: Mallorca, Languedoc, La Provencale</p>
<p>May: Les Marcaires</p>
<p>June: Gallibier/Alpe d’Huez/Croix de Fer</p>
<h2 id="gas-guzzling-north-american">Gas-guzzling North American</h2>
<p>Despite the long lunches, both rides also featured pretty big bonks around the 100k mark. In fact, out of all my years cycling, I think I could count on two hands the number of rides I’d done that were longer than 100k. Almost all had lunch stops. All of them involved bonking.</p>
<p>In other words, I needed to train to go a bit further, and a helluva lot higher than I was used to riding on a bike, and figure out how to feed myself so that I didn’t collapse on a mountainside and get swept up by the broom wagon.</p>Sarah HoskingStrade Bianche Gran Fondo 20182018-03-04T00:00:00+00:002018-03-04T00:00:00+00:00https://limegimlet.github.io/strade-bianche-gran-fondo-2018<p>When I signed up for Etape du Tour last October–and the 28-week training plan that goes along with it–I knew a few interim cycling events would need to happen, to maintain training momentum and to get me used to negotiating mass starts and crowded roads.</p>
<p>Reading my friend Gerry’s race report of Strade Bianche 2017 last March, with its epic black and white photos of rain-soaked, mud-encrusted riders, had stuck with me.</p>
<p>[insert gerry pic]</p>
<h2 id="prologue">Prologue</h2>
<p>The description was 139km, 1300m elevation gain. To date, my longest ride (which included lunch, coffee, & photo stops) was 138 km, so this seemed a very doable distance. All I wanted was to prove that I could survive riding the distance without a lunch break.</p>
<p>But it was also the same day as the Semi-marathon de Paris, a long-dreamed-for event with a course that passes within 200m of our apartment. I had been semi-seriously doing it, figuring the training would give me great aerobic fitness for EdT. Then I learned that the odd sensation in my left groin was not another soft-tissue injury but early-stage hip arthritis. Suddenly the choice was easy: to Siena we would go (I registered my husband as well as myself)</p>
<p>Around 6 weeks into base training I uploaded the course GPX file into RidewithGPS: 2000m. A bit more than I bargained for (my long rides tend to be flat), but face it, the more elevation gain, the better for Etape.</p>
<p>In the month leading up to the race, the Beast from the East, aka Le Grand Froid descended, and I chose pain cave over HTFU. My vision of a series increasingly longer Sunday rides didn’t quite pan out.</p>
<p>Then I read the fine print of the racing regs and saw there was a 7 hour cut-off. 7 hours from gun-time. Eeek.</p>
<h3 id="the-route">The route</h3>
<p>As you can see by all the green, it’s a flattish course punctuated 8 gravel segments, the eponymous strade bianche, or white roads. These are also where the climbs are, short but steep brutes. Was very grateful for my compact chainring + 11-32 cassette.</p>
<p><a href="http://veloviewer.com"><img src="https://limegimlet.github.io/assets/2018-03-04-veloviewer_3d.png" alt="Veloviewer's 3D version of the route" /></a></p>
<h2 id="start">Start</h2>
<p><img src="https://limegimlet.github.io/assets/images/sb2018/sb_start.jpg" alt="And we're off!" /></p>
<h2 id="km-24-first-gravel-sector">KM 24: First gravel sector</h2>
<p>The first <em>strada biancha</em> was a dead-flat 2k stretch soon after the 20k mark. The most challenging aspect was steering clear of dropped bottles, gloves, and toolkit bags littering the first few hundred meters.</p>
<p><img src="https://limegimlet.github.io/assets/images/sb2018/sb_race_1.jpg" alt="" />
<em>Layered up the yazoo.</em></p>
<h2 id="km-30-here-comes-the-sun">KM 30: Here comes the sun</h2>
<p>And off came the first of many layers throughout the day.</p>
<p><img src="https://limegimlet.github.io/assets/images/sb2018/sb_landscape_midday1.jpg" alt="" /></p>
<h2 id="km-80-these-are-getting-really-steep">KM 80: These are getting really steep</h2>
<p><img src="https://limegimlet.github.io/assets/images/sb2018/sb_steep_sector.jpg" alt="" />
<em>Still smiling naturally at this point.</em></p>
<p><img src="https://limegimlet.github.io/assets/images/sb2018/sb_landscape_midday.jpg" alt="" />
<em>And able to enjoy the views</em></p>
<h2 id="km-120---sector-7-the-brutal-climb">KM 120 - Sector 7: the brutal climb</h2>
<p><img src="https://limegimlet.github.io/assets/images/sb2018/sb_sector7_a.jpg" alt="Feeling tired" />
<em>Feeling a bit tired now.</em></p>
<p><img src="https://limegimlet.github.io/assets/images/sb2018/sb_sector7_another_view.jpg" alt="The view I didn't appreciate at the time" />
<em>The view that I can only appreciate now.</em></p>
<p><img src="https://limegimlet.github.io/assets/images/sb2018/sb_heavy_arm.jpg" alt="Karsten's arm is like lead weight" />
<em>Karsten puts arm around my shoulders for the sake of yet another photograher, it feels like lead.</em></p>
<h2 id="km-138-the-final-climbs">KM 138: The Final climbs</h2>
<p>I’d seen it in photos, on TV, and just the day before, saw it in the flesh as I watched women pros climb up it in the rain. In true Strade form, there was a photographer perched right by the nastiest 15% pitch at the end.</p>
<p>Before the race there’d been a video in my head of me gliding effortlessly up to standing and powering up those last few meters of Santa ????, all captured in photos.</p>
<p>But when my front wheel hit the 10% section further down, I knew would be happy just to be able to keep the cranks turning. Meanwhile, a Belgian who we’d passed approaching Siena made his attack and spectacularly photobombed my final climb pics. Either he was really determined to not be chicked, or simply overgeared</p>
<p><img src="https://limegimlet.github.io/assets/images/sb2018/sb_final_climb1.jpg" alt="" />
<em>Belgian Dude makes his move while Karsten goes into MountainGoat mode.</em></p>
<p><img src="https://limegimlet.github.io/assets/images/sb2018/sb_final_climb2.jpg" alt="" /></p>
<p><img src="https://limegimlet.github.io/assets/images/sb2018/sb_final_climb3.jpg" alt="" />
<em>Aw, how cute, we’re swaying in sync!</em></p>
<p><img src="https://limegimlet.github.io/assets/images/sb2018/sb_final_climb4.jpg" alt="" />
<em>I need to work on my Climbing Face</em></p>
<p><img src="https://limegimlet.github.io/assets/images/sb2018/sb_final_climb5.jpg" alt="" /></p>
<p><img src="https://limegimlet.github.io/assets/images/sb2018/sb_final_climb7.jpg" alt="" />
<em>Ah good he’s leaving. The frame is MINE now.</em></p>
<p><img src="https://limegimlet.github.io/assets/images/sb2018/sb_final_climb6.jpg" alt="" />
<em>Dude, just go, K?</em></p>
<p><img src="https://limegimlet.github.io/assets/images/sb2018/sb_final_climb8.jpg" alt="" />
*My face by now is completely on-brand, glowing Burnt Siena and blending in perfectly with the local architecture.</p>
<p>My consolation was despite being jealous that he managed a much more epic-looking climb, after I turned the corner for the last 400m flat stretch to the finish, I was gratified to see him stopped in a doorway and so I did at least cross the finish line before him.</p>
<p><img src="https://limegimlet.github.io/assets/images/sb2018/sb_towards_finish.jpg" alt="" /></p>
<p><img src="https://limegimlet.github.io/assets/images/sb2018/sb_full_finish.jpg" alt="" /></p>
<p><img src="https://limegimlet.github.io/assets/images/sb2018/sb_piazza_campanile.jpg" alt="" /></p>
<!-- begin ig snippet -->
<blockquote class="instagram-media" data-instgrm-captioned="" data-instgrm-permalink="https://www.instagram.com/p/Bf9KwYxBHWp/" data-instgrm-version="8" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:8px;"> <div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;"> <div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div> <p style=" margin:8px 0 0 0; padding:0 4px;"> <a href="https://www.instagram.com/p/Bf9KwYxBHWp/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_blank">After 140km, finally across the finish line. Bravo! #stradabianche #cyclingtuscany</a></p> <p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">A post shared by @<a href="https://www.instagram.com/herrkaa/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px;" target="_blank"> herrkaa</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2018-03-05T20:57:07+00:00">Mar 5, 2018 at 12:57pm PST</time></p></div></blockquote>
<script async="" defer="" src="//www.instagram.com/embed.js"></script>
<!-- end ig snippet -->Sarah HoskingWhen I signed up for Etape du Tour last October–and the 28-week training plan that goes along with it–I knew a few interim cycling events would need to happen, to maintain training momentum and to get me used to negotiating mass starts and crowded roads.