diff --git a/ipynb/Bike-Stats.ipynb b/ipynb/Bike-Stats.ipynb index d1af34e..c0b21a9 100644 --- a/ipynb/Bike-Stats.ipynb +++ b/ipynb/Bike-Stats.ipynb @@ -4,16 +4,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "
Peter Norvig, Oct 2017
Data updated regularly
\n", + "
Peter Norvig, Oct 2017
Last update: Jan 2024
\n", "\n", "# Bicycling Statistics\n", "\n", - "During a pandemic, bicycling is a great way to (1) spend some time, (2) get some exercise, (3) stay outside and be safe, and (4) track how you are progressing against your goals. My data comes from [Strava](https://www.strava.com/athletes/575579). At various times I've had various goals:\n", + "During a pandemic, bicycling is a great way to (1) spend some time, (2) get some exercise, (3) stay outside and be safe. In this notebook I track [my cycling performance](https://www.strava.com/athletes/575579) against various goals:\n", "- **Distance**: I do about 6,000 miles a year.\n", "- **Climbing**: In 2022, I climbed to *space* (100 km of total elevation gain).\n", "- **Explorer Tiles**: In 2022, I started tracking the 1-mile-square [explorer tiles](https://rideeverytile.com/) I have visited.\n", "- **Wandering**: In 2020, I started using [Wandrer.earth](https://wandrer.earth/athletes/3534/) to track what new roads I have ridden.\n", - "- **Eddington Number**: I've done 67 miles or more on 67 different days. So 67 is my Eddington Number.\n", + "- **Eddington Number**: I've done 68 miles or more on 68 different days. So 68 is my Eddington Number.\n", "- **Speed**: I'm not going particularly fast, but I am interested in understanding how my speed varies with the steepness of the hill.\n", "\n", "This notebook is mostly for my own benefit, but if you're a cyclist you're welcome to adapt it to your own data, and if you're a data scientist, you might find it an interesting example of exploratory data analysis. The companion notebook [**BikeCode.ipynb**](BikeCode.ipynb) has the implementation details." @@ -25,12 +25,12 @@ "source": [ "# Yearly Totals\n", "\n", - "Here are my overall stats for each year since I started keeping track in mid-2014. I have done 6,000 miles per year since 2016, except for 2020 when an injury kept me sidelined for two months. The columns keep track of speed/distance (**mph** is the speed, computed as the quotient of **miles* and **hours**; **kms** is the metric conversion of **miles**) and climbing (**feet** is the total height climbed (or **km_up** in metric), **vam** is vertical meters ascended per hour, **fpm** is average feet ascended per mile and **pct** is average grade of climbing (**fpm** × 100 / 5280).\n" + "Here are my overall stats for each year since I started keeping track in mid-2014. I have done 6,000 miles per year since 2016, except for 2020 when an injury kept me sidelined for two months. The columns keep track of the total **hours** on the bike, distance traveled in **miles**, and total **feet** climbed. Then there are some columns that are dervided from these: **mph** is **miles / hour**; **vam** is vertical meters ascended per hour (or **feet × 0.3048 / hours**); **fpmi** is **feet / miles**; **pct** is the grade in percent (or **feet × 100 / miles / 5280**), and finally **kms** and **meters** are the metric equivalents of **miles** and **feet**.\n" ] }, { "cell_type": "code", - "execution_count": 92, + "execution_count": 85, "metadata": {}, "outputs": [ { @@ -60,15 +60,28 @@ " feet\n", " mph\n", " vam\n", - " fpm\n", + " fpmi\n", " pct\n", " kms\n", - " km_up\n", + " meters\n", " \n", " \n", " \n", " \n", " \n", + " 2023\n", + " 541.68\n", + " 6316\n", + " 243100\n", + " 11.66\n", + " 137.0\n", + " 38.0\n", + " 0.73\n", + " 10162.44\n", + " 74097.0\n", + " \n", + " \n", + " \n", " 2022\n", " 532.93\n", " 6028\n", @@ -78,7 +91,7 @@ " 60.0\n", " 1.14\n", " 9699.05\n", - " 110.4\n", + " 110436.0\n", " \n", " \n", " \n", @@ -91,7 +104,7 @@ " 32.0\n", " 0.61\n", " 9756.98\n", - " 59.9\n", + " 59934.0\n", " \n", " \n", " \n", @@ -104,7 +117,7 @@ " 18.0\n", " 0.34\n", " 8593.67\n", - " 28.9\n", + " 28888.0\n", " \n", " \n", " \n", @@ -117,7 +130,7 @@ " 25.0\n", " 0.47\n", " 9679.74\n", - " 45.7\n", + " 45658.0\n", " \n", " \n", " \n", @@ -130,7 +143,7 @@ " 26.0\n", " 0.49\n", " 9816.51\n", - " 48.4\n", + " 48354.0\n", " \n", " \n", " \n", @@ -143,7 +156,7 @@ " 27.0\n", " 0.52\n", " 11835.80\n", - " 61.6\n", + " 61599.0\n", " \n", " \n", " \n", @@ -156,7 +169,7 @@ " 32.0\n", " 0.60\n", " 10199.45\n", - " 61.4\n", + " 61403.0\n", " \n", " \n", " \n", @@ -169,7 +182,7 @@ " 38.0\n", " 0.73\n", " 8772.27\n", - " 64.0\n", + " 63965.0\n", " \n", " \n", " \n", @@ -182,26 +195,27 @@ " 48.0\n", " 0.91\n", " 3972.62\n", - " 36.1\n", + " 36113.0\n", " \n", " \n", "\n", "" ], "text/plain": [ - " year hours miles feet mph vam fpm pct kms km_up\n", - " 2022 532.93 6028 362323 11.31 207.0 60.0 1.14 9699.05 110.4\n", - " 2021 490.53 6064 196634 12.36 122.0 32.0 0.61 9756.98 59.9\n", - " 2020 438.88 5341 94777 12.17 66.0 18.0 0.34 8593.67 28.9\n", - " 2019 476.32 6016 149797 12.63 96.0 25.0 0.47 9679.74 45.7\n", - " 2018 475.93 6101 158642 12.82 102.0 26.0 0.49 9816.51 48.4\n", - " 2017 567.33 7356 202096 12.97 109.0 27.0 0.52 11835.80 61.6\n", - " 2016 486.38 6339 201453 13.03 126.0 32.0 0.60 10199.45 61.4\n", - " 2015 419.95 5452 209859 12.98 152.0 38.0 0.73 8772.27 64.0\n", - " 2014 191.03 2469 118481 12.92 189.0 48.0 0.91 3972.62 36.1" + " year hours miles feet mph vam fpmi pct kms meters\n", + " 2023 541.68 6316 243100 11.66 137.0 38.0 0.73 10162.44 74097.0\n", + " 2022 532.93 6028 362323 11.31 207.0 60.0 1.14 9699.05 110436.0\n", + " 2021 490.53 6064 196634 12.36 122.0 32.0 0.61 9756.98 59934.0\n", + " 2020 438.88 5341 94777 12.17 66.0 18.0 0.34 8593.67 28888.0\n", + " 2019 476.32 6016 149797 12.63 96.0 25.0 0.47 9679.74 45658.0\n", + " 2018 475.93 6101 158642 12.82 102.0 26.0 0.49 9816.51 48354.0\n", + " 2017 567.33 7356 202096 12.97 109.0 27.0 0.52 11835.80 61599.0\n", + " 2016 486.38 6339 201453 13.03 126.0 32.0 0.60 10199.45 61403.0\n", + " 2015 419.95 5452 209859 12.98 152.0 38.0 0.73 8772.27 63965.0\n", + " 2014 191.03 2469 118481 12.92 189.0 48.0 0.91 3972.62 36113.0" ] }, - "execution_count": 92, + "execution_count": 85, "metadata": {}, "output_type": "execute_result" } @@ -216,12 +230,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "And here's the same data on a daily basis, assuming 350 days of riding in a year:" + "And here's the same data on a per day basis, assuming I ride 6 days a week:" ] }, { "cell_type": "code", - "execution_count": 93, + "execution_count": 86, "metadata": {}, "outputs": [ { @@ -251,148 +265,162 @@ " feet\n", " mph\n", " vam\n", - " fpm\n", + " fpmi\n", " pct\n", " kms\n", - " km_up\n", + " meters\n", " \n", " \n", " \n", " \n", " \n", + " 2023\n", + " 1.7\n", + " 20.2\n", + " 779.2\n", + " 11.66\n", + " 137.0\n", + " 38.0\n", + " 0.73\n", + " 32.6\n", + " 237.5\n", + " \n", + " \n", + " \n", " 2022\n", - " 1.5\n", - " 17.2\n", - " 1035.2\n", + " 1.7\n", + " 19.3\n", + " 1161.3\n", " 11.31\n", " 207.0\n", " 60.0\n", " 1.14\n", - " 27.7\n", - " 0.315\n", + " 31.1\n", + " 354.0\n", " \n", " \n", " \n", " 2021\n", - " 1.4\n", - " 17.3\n", - " 561.8\n", + " 1.6\n", + " 19.4\n", + " 630.2\n", " 12.36\n", " 122.0\n", " 32.0\n", " 0.61\n", - " 27.9\n", - " 0.171\n", + " 31.3\n", + " 192.1\n", " \n", " \n", " \n", " 2020\n", - " 1.3\n", - " 15.3\n", - " 270.8\n", + " 1.4\n", + " 17.1\n", + " 303.8\n", " 12.17\n", " 66.0\n", " 18.0\n", " 0.34\n", - " 24.6\n", - " 0.083\n", + " 27.5\n", + " 92.6\n", " \n", " \n", " \n", " 2019\n", - " 1.4\n", - " 17.2\n", - " 428.0\n", + " 1.5\n", + " 19.3\n", + " 480.1\n", " 12.63\n", " 96.0\n", " 25.0\n", " 0.47\n", - " 27.7\n", - " 0.131\n", + " 31.0\n", + " 146.3\n", " \n", " \n", " \n", " 2018\n", - " 1.4\n", - " 17.4\n", - " 453.3\n", + " 1.5\n", + " 19.6\n", + " 508.5\n", " 12.82\n", " 102.0\n", " 26.0\n", " 0.49\n", - " 28.0\n", - " 0.138\n", + " 31.5\n", + " 155.0\n", " \n", " \n", " \n", " 2017\n", - " 1.6\n", - " 21.0\n", - " 577.4\n", + " 1.8\n", + " 23.6\n", + " 647.7\n", " 12.97\n", " 109.0\n", " 27.0\n", " 0.52\n", - " 33.8\n", - " 0.176\n", + " 37.9\n", + " 197.4\n", " \n", " \n", " \n", " 2016\n", - " 1.4\n", - " 18.1\n", - " 575.6\n", + " 1.6\n", + " 20.3\n", + " 645.7\n", " 13.03\n", " 126.0\n", " 32.0\n", " 0.60\n", - " 29.1\n", - " 0.175\n", + " 32.7\n", + " 196.8\n", " \n", " \n", " \n", " 2015\n", - " 1.2\n", - " 15.6\n", - " 599.6\n", + " 1.3\n", + " 17.5\n", + " 672.6\n", " 12.98\n", " 152.0\n", " 38.0\n", " 0.73\n", - " 25.1\n", - " 0.183\n", + " 28.1\n", + " 205.0\n", " \n", " \n", " \n", " 2014\n", - " 0.5\n", - " 7.1\n", - " 338.5\n", + " 0.6\n", + " 7.9\n", + " 379.7\n", " 12.92\n", " 189.0\n", " 48.0\n", " 0.91\n", - " 11.4\n", - " 0.103\n", + " 12.7\n", + " 115.7\n", " \n", " \n", "\n", "" ], "text/plain": [ - " year hours miles feet mph vam fpm pct kms km_up\n", - " 2022 1.5 17.2 1035.2 11.31 207.0 60.0 1.14 27.7 0.315\n", - " 2021 1.4 17.3 561.8 12.36 122.0 32.0 0.61 27.9 0.171\n", - " 2020 1.3 15.3 270.8 12.17 66.0 18.0 0.34 24.6 0.083\n", - " 2019 1.4 17.2 428.0 12.63 96.0 25.0 0.47 27.7 0.131\n", - " 2018 1.4 17.4 453.3 12.82 102.0 26.0 0.49 28.0 0.138\n", - " 2017 1.6 21.0 577.4 12.97 109.0 27.0 0.52 33.8 0.176\n", - " 2016 1.4 18.1 575.6 13.03 126.0 32.0 0.60 29.1 0.175\n", - " 2015 1.2 15.6 599.6 12.98 152.0 38.0 0.73 25.1 0.183\n", - " 2014 0.5 7.1 338.5 12.92 189.0 48.0 0.91 11.4 0.103" + " year hours miles feet mph vam fpmi pct kms meters\n", + " 2023 1.7 20.2 779.2 11.66 137.0 38.0 0.73 32.6 237.5\n", + " 2022 1.7 19.3 1161.3 11.31 207.0 60.0 1.14 31.1 354.0\n", + " 2021 1.6 19.4 630.2 12.36 122.0 32.0 0.61 31.3 192.1\n", + " 2020 1.4 17.1 303.8 12.17 66.0 18.0 0.34 27.5 92.6\n", + " 2019 1.5 19.3 480.1 12.63 96.0 25.0 0.47 31.0 146.3\n", + " 2018 1.5 19.6 508.5 12.82 102.0 26.0 0.49 31.5 155.0\n", + " 2017 1.8 23.6 647.7 12.97 109.0 27.0 0.52 37.9 197.4\n", + " 2016 1.6 20.3 645.7 13.03 126.0 32.0 0.60 32.7 196.8\n", + " 2015 1.3 17.5 672.6 12.98 152.0 38.0 0.73 28.1 205.0\n", + " 2014 0.6 7.9 379.7 12.92 189.0 48.0 0.91 12.7 115.7" ] }, - "execution_count": 93, + "execution_count": 86, "metadata": {}, "output_type": "execute_result" } @@ -407,7 +435,7 @@ "source": [ "# Climbing \n", "\n", - "In 2022 my friend [A. J. Jacobs](https://ajjacobs.com/) set a goal of **walking to space**: climbing a total elevation equal to the distance from the Earth's surface to the top of the atmoshere. [A group](https://www.facebook.com/groups/260966686136038) of about 40 of us joined the quest. The boundary of \"space\" is vague, but could is often reckoned as either 100 km (the [Kármán line](https://en.wikipedia.org/wiki/K%C3%A1rm%C3%A1n_line)) or [50 miles](https://science.nasa.gov/edge-space); in 2022 I surpassed 100 kilometers of climbing (over 1,000 feet per day), but most years I'm closer to 60 kilometers (about 600 feet per day)." + "In 2022 my friend [A. J. Jacobs](https://ajjacobs.com/) set a goal of **walking to space**: climbing a total elevation equal to the distance from the Earth's surface to the top of the atmoshere. [A group](https://www.facebook.com/groups/260966686136038) of about 40 of us joined the quest. The boundary of \"space\" is vague, but the [Kármán line](https://en.wikipedia.org/wiki/K%C3%A1rm%C3%A1n_line)) is 100 kilometers; in 2022 I surpassed 100 kilometers of climbing (over 1,100 feet per day), but most years I'm closer to 60 kilometers (about 600 feet per day)." ] }, { @@ -419,18 +447,18 @@ "# Explorer Tiles\n", "\n", "\n", - "The [OpenStreetMap](https://www.openstreetmap.org/) world map is divided into **[explorer tiles](https://www.statshunters.com/faq-10-what-are-explorer-tiles)** of approximately 1 mile square. Sites like [Veloviewer](https://veloviewer.com) and [Statshunter](https://www.statshunters.com/) challenge bicyclist/hikers to record which tiles they have passed through. The process is gamified to highlight the following statistics:\n", + "The [OpenStreetMap](https://www.openstreetmap.org/) world map is divided into **[explorer tiles](https://www.statshunters.com/faq-10-what-are-explorer-tiles)** of approximately 1 mile square. Sites like [Veloviewer](https://veloviewer.com) and [Statshunter](https://www.statshunters.com/ and [RideEveryTile](https://rideeverytile.com/) and [SquadRats](https://squadrats.com/map) challenge bicyclist/hikers to record which tiles they have passed through. The process is gamified to highlight the following statistics:\n", "- The largest **square** (an *n* × *n* array of visited ties). \n", "- The maximum **cluster** (a set of contiguous interior visited tiles, where \"interior\" means surrounded by visited tiles).\n", "- The **total** number of visited tiles.\n", " \n", "\n", - "Since I live on a penninsula, it is not easy for me to form a large square, and I sometimes have to work hard to connect different parts of my map (such as connecting San Francisco and Marin). I have a [separrate page]() documenting my explorations, but here are a few key points along the way:" + "Since I live on a penninsula, it is not easy for me to form a large square, and I sometimes have to work hard to connect different parts of my map into my main cluster (such as connecting San Francisco and Marin). I have a [separate page](???) documenting my explorations, but here are a few key points along the way:" ] }, { "cell_type": "code", - "execution_count": 94, + "execution_count": 87, "metadata": {}, "outputs": [ { @@ -438,74 +466,98 @@ "text/html": [ "\n", - "\n", + "
\n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", "
 datesquareclustertotalcommentdatesquareclustertotalcomment
06/30/2023136892640Rides in east Bay01/01/20241410563105Start of this year
04/14/2023136302595Black Sands Beach connects Marin to max cluster12/08/20231410423084Benicia ride connects East Bay and Napa clusters
03/04/2023135832574Almaden rides connects Gilroy to max cluster11/05/2023149322914Alum Rock ride gets 14x14 max square
10/22/2022133962495Alviso levees to get to 13x13 max square06/30/2023136892640Rides in east Bay fill in holes
10/16/2022123932492Milpitas ride connects East Bay to max cluster04/14/2023136302595Black Sands Beach low-tide hike connects Marin to max cluster
09/08/2022113002487First started tracking tiles03/04/2023135832574Almaden rides connects Gilroy to max cluster
10/22/2022133962495Alviso levees to get to 13x13 max square
10/16/2022123932492Milpitas ride connects East Bay to max cluster
09/08/2022113002487First started tracking tiles
\n" ], "text/plain": [ - "" + "" ] }, - "execution_count": 94, + "execution_count": 87, "metadata": {}, "output_type": "execute_result" } @@ -522,164 +574,1674 @@ "\n", "The website [**Wandrer.earth**](https://wandrer.earth) tracks the distinct roads a user has biked on. It provides a fun incentive to get out and explore new roads. The site is gamified in a way that there is a reward for first reaching 25% of the road-miles in each city, and further rewards for higher percentages. (You get no credit for repeating a road you've already been on.) \n", "\n", - "The wandrer.earth site does a good job of showing my current status, but it requires clicking around a bit, so I summarize it all in one place here. Each line gives the county (e.g. SMC for San Mateo County); city preceeded by the percentage of roads I've rode there; miles rode/total miles in city; and miles to go to the next big reward level." + "The wandrer.earth site does a good job of showing my current status, but it requires clicking around a bit, so I summarize it all in one place here. Each line gives the percent of roads/trails that I have traveled on for each place (specified by **county** and city **name**), as well as the **total** miles of road in the place, the miles I have **done**, and the amount I need to hit the **next badge**. " ] }, { "cell_type": "code", - "execution_count": 95, + "execution_count": 88, "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "SMC 100.0% Los Trancos OSP 0.3/0.3 mi \n", - "SMC 99.9% Los Trancos Woods 5.3/5.3 mi \n", - "SMC 99.9% Menlo Oaks 3.5/3.5 mi \n", - "SMC 99.9% Kensington Square 0.6/0.6 mi \n", - "SMC 99.9% Ladera 8.1/8.1 mi \n", - "SMC 99.9% Palomar Park 4.0/4.0 mi \n", - "SMC 99.8% North Fair Oaks 27/27 mi \n", - "SMC 99.8% Emerald Lake Hills 25/25 mi \n", - "SMC 99.8% West Menlo Park 11/11 mi \n", - "SMC 99.8% Atherton 56/56 mi \n", - "SMC 99.7% Sequoia Tract 11/11 mi \n", - "SCC 99.6% Los Altos 138/138 mi \n", - "SCC 99.6% Loyola 18/18 mi \n", - "SMC 99.6% East Palo Alto 48/48 mi \n", - "SMC 99.5% Menlo Park 139/140 mi \n", - "SMC 99.5% Woodside 75/75 mi \n", - "SMC 99.5% Portola Valley 48/48 mi \n", - "SMC 99.4% Sky Londa 12/12 mi \n", - "SCC 99.4% Los Altos Hills 91/91 mi \n", - "SMC 99.3% Redwood City 239/240 mi \n", - "SCC 99.2% Mountain View 206/208 mi \n", - "SMC 99.1% Windy Hill Preserve 4.1/4.1 mi \n", - "SMC 99.0% San Carlos 98/99 mi \n", - "SCC 99.0% Palo Alto 294/297 mi \n", - "SMC 91.6% Burleigh Murray Park 1.9/2.1 mi 0.2 mi for 99%\n", - "SCC 86.8% Foothills Preserve 1.0/1.1 mi 0.0 mi for 90%\n", - "SMC 80.7% Foster City 121/150 mi 14 mi for 90%\n", - "SMC 77.9% San Mateo Highlands 14/18 mi 2.2 mi for 90%\n", - "SMC 74.9% Skyline Ridge OSP 0.6/0.8 mi 0.1 mi for 90%\n", - "SMC 71.4% Burlingame Hills 4.3/6.0 mi 1.1 mi for 90%\n", - "SMC 66.7% Coal Creek Preserve 2.6/3.9 mi 0.9 mi for 90%\n", - "SCC 62.7% San Francisco Bay Trail 164/261 mi 71 mi for 90%\n", - "--- 61.6% San Mateo County 1,732/2,814 mi 800 mi for 90%\n", - "SMC 54.3% Burlingame 48/88 mi 32 mi for 90%\n", - "SMC 54.2% Belmont 53/98 mi 35 mi for 90%\n", - "SCC 52.4% Sunnyvale 187/357 mi 134 mi for 90%\n", - "SMC 52.3% Hillsborough 45/85 mi 32 mi for 90%\n", - "SMC 51.8% San Mateo 133/256 mi 98 mi for 90%\n", - "SMC 51.5% Half Moon Bay State Beach 2.3/4.4 mi 1.7 mi for 90%\n", - "SMC 51.2% Russian Ridge Preserve 6.2/12 mi 4.7 mi for 90%\n", - "SMC 51.2% Long Ridge Preserve 5.6/11 mi 4.3 mi for 90%\n", - "SCC 51.2% Castle Rock State Park 5.7/11 mi 4.3 mi for 90%\n", - "NSW 47.3% Barangaroo 0.8/1.7 mi 0.0 mi for 50%\n", - "SCC 47.2% Gardner 11/23 mi 0.7 mi for 50%\n", - "SCC 47.0% Edenvale 14/30 mi 0.9 mi for 50%\n", - "SMC 46.7% Brisbane 19/41 mi 1.3 mi for 50%\n", - "ALA 45.9% Newark 67/147 mi 6.0 mi for 50%\n", - "SCC 44.7% Monte Sereno 9.1/20 mi 1.1 mi for 50%\n", - "SMC 44.3% Moss Beach 8.7/20 mi 1.1 mi for 50%\n", - "SFC 43.9% Presidio Terrace 1.2/2.8 mi 0.2 mi for 50%\n", - "ALA 43.3% Hayward Acres 1.5/3.5 mi 0.2 mi for 50%\n", - "ALA 40.8% San Lorenzo 23/56 mi 5.1 mi for 50%\n", - "SMC 40.8% Millbrae 27/65 mi 6.0 mi for 50%\n", - "SFC 39.6% Lincoln Park 1.8/4.5 mi 0.5 mi for 50%\n", - "SCC 39.5% Communications Hill 11/28 mi 2.9 mi for 50%\n", - "SMC 38.7% Purisima Creek Preserve 6.4/16 mi 1.9 mi for 50%\n", - "MAR 38.7% Mt Tamalpais State Park 12/32 mi 3.6 mi for 50%\n", - "SMC 38.4% El Granada 19/49 mi 5.7 mi for 50%\n", - "SMC 38.3% Colma 5.2/14 mi 1.6 mi for 50%\n", - "SMC 38.2% Broadmoor 3.4/8.8 mi 1.0 mi for 50%\n", - "SFC 37.4% South Beach 1.8/4.8 mi 0.6 mi for 50%\n", - "SCC 37.3% Milpitas 84/224 mi 28 mi for 50%\n", - "MAR 37.1% Muir Beach 1.7/4.6 mi 0.6 mi for 50%\n", - "SFC 36.8% Lake Street 1.4/3.9 mi 0.5 mi for 50%\n", - "SCC 35.7% Spartan Keyes 23/64 mi 9.2 mi for 50%\n", - "ALA 35.7% Ashland 13/35 mi 5.0 mi for 50%\n", - "SCC 34.9% Willow Glen 28/82 mi 12 mi for 50%\n", - "MAS 34.7% MIT 3.3/9.6 mi 1.5 mi for 50%\n", - "NSW 34.3% Millers Point 1.1/3.2 mi 0.5 mi for 50%\n", - "SCC 34.0% Santa Clara 118/348 mi 56 mi for 50%\n", - "SCC 33.6% Seven Trees 14/41 mi 6.7 mi for 50%\n", - "SCC 33.4% Parkview 14/42 mi 7.1 mi for 50%\n", - "SCC 33.2% Cupertino 57/172 mi 29 mi for 50%\n", - "SCC 33.2% Los Gatos 49/148 mi 25 mi for 50%\n", - "MAR 32.9% Stinson Beach 3.7/11 mi 1.9 mi for 50%\n", - "--- 32.8% Santa Clara County 2,483/7,569 mi 1,302 mi for 50%\n", - "SMC 32.7% Montara 9.1/28 mi 4.8 mi for 50%\n", - "SMC 32.7% Pacifica 49/151 mi 26 mi for 50%\n", - "SCC 32.7% Branham 14/44 mi 7.6 mi for 50%\n", - "SMC 32.2% Half Moon Bay 22/68 mi 12 mi for 50%\n", - "ALA 32.2% Fremont 251/780 mi 139 mi for 50%\n", - "MAR 31.9% Marin Headlands GGNRA 21/66 mi 12 mi for 50%\n", - "SCC 31.1% San Martin 11/35 mi 6.7 mi for 50%\n", - "SCC 30.7% Forest of Nisene Marks SP 14/44 mi 8.5 mi for 50%\n", - "ALA 30.7% Union City 64/209 mi 40 mi for 50%\n", - "SCC 30.5% Willow Glen South 19/63 mi 12 mi for 50%\n", - "ALA 30.0% Hayward 133/444 mi 89 mi for 50%\n", - "SCC 29.7% Saratoga 53/180 mi 37 mi for 50%\n", - "SMC 29.5% San Bruno 34/114 mi 23 mi for 50%\n", - "SFC 29.4% Golden Gate Park 12/41 mi 8.4 mi for 50%\n", - "SFC 29.3% Seacliff 1.2/4.1 mi 0.8 mi for 50%\n", - "CCC 29.3% Rosie Riveter Park 1.6/5.5 mi 1.1 mi for 50%\n", - "NSW 29.2% Dawes Point 0.5/1.8 mi 0.4 mi for 50%\n", - "SCC 28.8% Campbell 34/119 mi 25 mi for 50%\n", - "SCC 27.7% San Jose 725/2,619 mi 584 mi for 50%\n", - "ALA 27.5% San Leandro 63/231 mi 52 mi for 50%\n", - "SMC 27.2% South San Francisco 50/185 mi 42 mi for 50%\n", - "SMC 27.2% Daly City 40/148 mi 34 mi for 50%\n", - "ALA 27.1% Cherryland 5.7/21 mi 4.8 mi for 50%\n", - "CAL 26.8% Mokelumne Hill 3.9/15 mi 3.4 mi for 50%\n", - "SFC 26.7% Presidio National Park 12/44 mi 10 mi for 50%\n", - "SON 23.6% Guerneville 5.4/23 mi 0.3 mi for 25%\n", - "SFC 21.6% Presidio Heights 1.4/6.5 mi 0.2 mi for 25%\n", - "SMC 21.4% Bay Area Ridge Trail 85/396 mi 14 mi for 25%\n", - "SFC 20.6% Panhandle 1.5/7.3 mi 0.3 mi for 25%\n", - "SFC 18.2% Polk Gulch 0.7/4.0 mi 0.3 mi for 25%\n", - "SFC 18.2% Balboa Terrace 0.6/3.4 mi 0.2 mi for 25%\n", - "SFC 18.0% Cole Valley 0.3/1.7 mi 0.1 mi for 25%\n", - "SON 17.8% Healdsburg 9.6/54 mi 3.9 mi for 25%\n", - "SON 17.0% Bodega Bay 4.9/29 mi 2.3 mi for 25%\n", - "SFC 15.9% Forest Hill 1.0/6.1 mi 0.6 mi for 25%\n", - "SFC 15.5% Northern Waterfront 0.9/5.6 mi 0.5 mi for 25%\n", - "SFC 15.4% Aquatic Park Fort Mason 1.0/6.4 mi 0.6 mi for 25%\n", - "--- 15.4% Alameda County 895/5,818 mi 560 mi for 25%\n", - "SFC 15.2% Little Hollywood 0.6/3.7 mi 0.4 mi for 25%\n", - "SFC 14.2% Clarendon Heights 0.9/6.0 mi 0.6 mi for 25%\n", - "SFC 13.8% Fisherman's Wharf 0.9/6.2 mi 0.7 mi for 25%\n", - "SFC 13.2% Sutro Heights 0.9/7.1 mi 0.8 mi for 25%\n", - "SFC 13.0% Ashbury Heights 0.5/3.7 mi 0.4 mi for 25%\n", - "MAR 12.9% Corte Madera 6.6/51 mi 6.2 mi for 25%\n", - "MAR 12.9% Sausalito 4.2/33 mi 4.0 mi for 25%\n", - "SFC 12.3% Dogpatch 0.6/5.1 mi 0.6 mi for 25%\n", - "ALA 12.2% Alameda 25/207 mi 26 mi for 25%\n", - "SCC 12.1% Gilroy 23/189 mi 24 mi for 25%\n", - "SFC 11.9% Cow Hollow 1.4/12 mi 1.6 mi for 25%\n", - "--- 10.8% Marin County 251/2,333 mi 332 mi for 25%\n", - "SFC 10.7% Golden Gate Heights 1.9/18 mi 2.5 mi for 25%\n", - "SFC 10.7% Pacific Heights 1.9/18 mi 2.6 mi for 25%\n", - "SFC 10.2% Financial District 1.0/9.4 mi 1.4 mi for 25%\n", - "MAR 9.1% Mill Valley 8.4/92 mi 15 mi for 25%\n", - "SFC 8.6% Mission Bay 1.2/14 mi 2.3 mi for 25%\n", - "ALA 8.0% Emeryville 2.2/28 mi 4.8 mi for 25%\n", - "ALA 7.4% Berkeley 19/260 mi 46 mi for 25%\n", - "--- 7.2% San Francisco County 88/1,217 mi 217 mi for 25%\n", - "--- 7.1% Santa Cruz County 194/2,718 mi 486 mi for 25%\n", - "ALA 6.8% Albany 2.9/43 mi 7.8 mi for 25%\n", - "MAS 6.2% Cambridge 11/181 mi 34 mi for 25%\n", - "SFC 6.0% Central Waterfront 0.6/10 mi 1.9 mi for 25%\n", - "--- 5.1% Sonoma County 251/4,895 mi 973 mi for 25%\n", - "--- 4.8% Napa County 78/1,609 mi 324 mi for 25%\n", - "MAR 3.7% San Rafael 9.6/260 mi 55 mi for 25%\n", - "--- 2.0% Contra Costa County 122/5,945 mi 1,364 mi for 25%\n", - "--- 1.7% California 6,450/0.38M mi 1,090 mi for 2%\n", - "--- 0.1% USA 6,840/6.4M mi 5,973 mi for 0.2%\n", - "--- 0.017% Earth 7,202/42M mi 1,192 mi for 0.02%\n" - ] + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
pctcountynametotaldoneto next badge
100.0%SMCLos Trancos Woods5.35.3
100.0%SMCMenlo Oaks3.53.5
100.0%SMCLos Trancos OSP0.30.3
100.0%SMCLadera8.18.1
100.0%SMCKensington Square0.60.6
100.0%SMCNorth Fair Oaks26.727
100.0%SMCWest Menlo Park11.211
100.0%SMCSequoia Tract11.011
99.9%SCCLoyola18.318
99.9%SMCPalomar Park4.04.0
99.9%SMCEmerald Lake Hills24.625
99.8%SMCAtherton56.356
99.8%SMCWindy Hill Preserve4.14.1
99.7%SMCMenlo Park139.5139
99.7%SMCEast Palo Alto48.348
99.6%SMCSky Londa11.812
99.6%SCCLos Altos138.2138
99.5%SMCPortola Valley48.248
99.4%SMCWoodside75.275
99.3%SMCRedwood City240.5239
99.3%SCCMountain View208.1207
99.2%SCCLos Altos Hills91.391
99.0%SCCPalo Alto297.2294
99.0%SMCSan Carlos99.098
90.9%SMCBurleigh Murray Park2.11.90.2 mi to 99%
90.4%SMCFoster City150.013613 mi to 99%
86.8%SCCFoothills Preserve1.11.00.0 mi to 90%
77.9%SMCSan Mateo Highlands18.0142.2 mi to 90%
74.5%SMCSkyline Ridge OSP0.80.60.1 mi to 90%
74.1%SCCSan Francisco Bay Trail260.819341 mi to 90%
73.2%CCCRosie Riveter Park5.54.00.9 mi to 90%
71.5%SMCBurlingame Hills6.04.31.1 mi to 90%
66.7%SMCCoal Creek Preserve3.92.60.9 mi to 90%
63.5%---San Mateo County2814.01,787746 mi to 90%
59.5%SMCRussian Ridge Preserve12.27.33.7 mi to 90%
59.3%SMCMontara27.8168.5 mi to 90%
56.2%SMCBurlingame88.45030 mi to 90%
54.7%SMCBelmont98.15435 mi to 90%
53.3%SCCSunnyvale357.0190131 mi to 90%
52.3%SMCHillsborough85.34532 mi to 90%
51.8%SMCSan Mateo256.013398 mi to 90%
51.5%SMCHalf Moon Bay State Beach4.42.31.7 mi to 90%
51.2%SMCLong Ridge Preserve11.05.64.3 mi to 90%
51.2%SCCCastle Rock State Park11.25.74.3 mi to 90%
51.0%ALANewark147.07557 mi to 90%
50.2%SMCBrisbane40.92116 mi to 90%
47.7%SCCEdenvale30.0140.7 mi to 50%
47.3%NSWBarangaroo1.70.80.0 mi to 50%
47.2%SCCGardner23.4110.7 mi to 50%
45.1%SCCMonte Sereno20.49.21.0 mi to 50%
43.9%SFCPresidio Terrace2.81.20.2 mi to 50%
43.6%SMCEl Granada49.2213.1 mi to 50%
43.6%SMCMoss Beach19.78.61.3 mi to 50%
43.3%ALAHayward Acres3.51.50.2 mi to 50%
42.6%SMCMillbrae65.0284.8 mi to 50%
40.8%ALASan Lorenzo55.5235.1 mi to 50%
39.6%SFCLincoln Park4.51.80.5 mi to 50%
39.5%SCCCommunications Hill27.8112.9 mi to 50%
39.0%SMCPurisima Creek Preserve16.56.41.8 mi to 50%
38.9%SMCColma13.75.31.5 mi to 50%
38.7%MARMt Tamalpais State Park31.7123.6 mi to 50%
38.2%SMCBroadmoor8.83.41.0 mi to 50%
37.4%SFCSouth Beach4.81.80.6 mi to 50%
37.1%MARMuir Beach4.61.70.6 mi to 50%
36.8%SFCLake Street3.91.40.5 mi to 50%
36.8%SCCSpartan Keyes64.3248.5 mi to 50%
36.7%SCCMilpitas224.08230 mi to 50%
36.1%ALAAshland35.1134.9 mi to 50%
36.0%SCCWillow Glen81.62911 mi to 50%
34.8%SCCSanta Clara348.012153 mi to 50%
34.7%MASMIT9.63.31.5 mi to 50%
34.3%NSWMillers Point3.21.10.5 mi to 50%
33.7%SCCParkview42.5146.9 mi to 50%
33.6%SMCHalf Moon Bay68.02311 mi to 50%
33.6%SCCLos Gatos148.05024 mi to 50%
33.6%SCCCupertino172.05828 mi to 50%
33.2%SMCPacifica150.95025 mi to 50%
33.0%---Santa Clara County7569.02,4981,287 mi to 50%
33.0%SCCSeven Trees40.9137.0 mi to 50%
32.9%ALAUnion City208.86936 mi to 50%
32.9%MARStinson Beach11.23.71.9 mi to 50%
32.9%ALAFremont780.2257133 mi to 50%
32.7%SCCBranham44.0147.6 mi to 50%
32.7%ALAHayward444.514577 mi to 50%
31.9%MARMarin Headlands GGNRA65.72112 mi to 50%
31.5%SCCSan Martin35.3116.5 mi to 50%
31.0%SMCSan Bruno114.03522 mi to 50%
30.9%SCCWillow Glen South63.32012 mi to 50%
30.7%SCCForest of Nisene Marks SP44.0148.5 mi to 50%
30.0%SCCSaratoga180.05436 mi to 50%
29.6%SMCSouth San Francisco185.35538 mi to 50%
29.4%SFCGolden Gate Park40.8128.4 mi to 50%
29.3%SCCCampbell119.03525 mi to 50%
29.3%SFCSeacliff4.11.20.8 mi to 50%
29.2%NSWDawes Point1.80.50.4 mi to 50%
28.2%ALAFairview34.49.77.5 mi to 50%
28.0%SCCSan Jose2618.7733576 mi to 50%
27.8%ALACherryland20.95.84.6 mi to 50%
27.7%ALASan Leandro230.66451 mi to 50%
27.4%SMCDaly City148.14133 mi to 50%
26.8%CALMokelumne Hill14.73.93.4 mi to 50%
26.7%SFCPresidio National Park43.51210 mi to 50%
26.1%ALACastro Valley192.55046 mi to 50%
25.6%SMCBay Area Ridge Trail395.610197 mi to 50%
23.6%SONGuerneville22.75.40.3 mi to 25%
21.6%SFCPresidio Heights6.51.40.2 mi to 25%
20.6%SFCPanhandle7.31.50.3 mi to 25%
18.2%SFCBalboa Terrace3.40.60.2 mi to 25%
18.2%SFCPolk Gulch4.00.70.3 mi to 25%
18.0%SFCCole Valley1.70.30.1 mi to 25%
17.8%SONHealdsburg53.79.63.9 mi to 25%
17.0%SONBodega Bay28.94.92.3 mi to 25%
16.6%---Alameda County5818.0965490 mi to 25%
15.9%SFCForest Hill6.11.00.6 mi to 25%
15.5%SFCNorthern Waterfront5.60.90.5 mi to 25%
15.4%SFCAquatic Park Fort Mason6.41.00.6 mi to 25%
15.2%SFCLittle Hollywood3.70.60.4 mi to 25%
14.2%SFCClarendon Heights6.00.90.6 mi to 25%
13.8%SFCFisherman's Wharf6.20.90.7 mi to 25%
13.2%SFCSutro Heights7.10.90.8 mi to 25%
13.0%SFCAshbury Heights3.70.50.4 mi to 25%
12.9%MARCorte Madera51.06.66.2 mi to 25%
12.9%MARSausalito32.74.24.0 mi to 25%
12.3%SFCDogpatch5.10.60.6 mi to 25%
12.2%ALAAlameda206.72526 mi to 25%
12.1%SCCGilroy188.92324 mi to 25%
11.9%SFCCow Hollow12.01.41.6 mi to 25%
10.9%---Marin County2333.0255328 mi to 25%
10.7%SFCPacific Heights18.01.92.6 mi to 25%
10.7%SFCGolden Gate Heights17.81.92.5 mi to 25%
10.2%SFCFinancial District9.41.01.4 mi to 25%
9.3%---San Francisco County1217.0113192 mi to 25%
9.1%MARMill Valley92.28.415 mi to 25%
8.9%---Napa County1609.0143259 mi to 25%
8.6%SFCMission Bay13.81.22.3 mi to 25%
7.7%ALAEmeryville28.12.24.9 mi to 25%
7.6%ALABerkeley260.32045 mi to 25%
7.1%---Santa Cruz County2718.0194486 mi to 25%
6.8%ALAAlbany42.72.97.8 mi to 25%
6.2%MASCambridge180.81134 mi to 25%
6.0%SFCCentral Waterfront10.20.61.9 mi to 25%
5.1%---Sonoma County4895.0251973 mi to 25%
3.8%---Contra Costa County5945.02261,260 mi to 25%
3.7%MARSan Rafael260.09.655 mi to 25%
1.8%---California377037.06,719822 mi to 2%
0.113%---USA6406754.07,2675,546 mi to 0.2%
0.017%---Earth41974536.07,1591,236 mi to 0.02%
\n", + "
" + ], + "text/plain": [ + " pct county name total done \\\n", + " 100.0% SMC Los Trancos Woods 5.3 5.3 \n", + " 100.0% SMC Menlo Oaks 3.5 3.5 \n", + " 100.0% SMC Los Trancos OSP 0.3 0.3 \n", + " 100.0% SMC Ladera 8.1 8.1 \n", + " 100.0% SMC Kensington Square 0.6 0.6 \n", + " 100.0% SMC North Fair Oaks 26.7 27 \n", + " 100.0% SMC West Menlo Park 11.2 11 \n", + " 100.0% SMC Sequoia Tract 11.0 11 \n", + " 99.9% SCC Loyola 18.3 18 \n", + " 99.9% SMC Palomar Park 4.0 4.0 \n", + " 99.9% SMC Emerald Lake Hills 24.6 25 \n", + " 99.8% SMC Atherton 56.3 56 \n", + " 99.8% SMC Windy Hill Preserve 4.1 4.1 \n", + " 99.7% SMC Menlo Park 139.5 139 \n", + " 99.7% SMC East Palo Alto 48.3 48 \n", + " 99.6% SMC Sky Londa 11.8 12 \n", + " 99.6% SCC Los Altos 138.2 138 \n", + " 99.5% SMC Portola Valley 48.2 48 \n", + " 99.4% SMC Woodside 75.2 75 \n", + " 99.3% SMC Redwood City 240.5 239 \n", + " 99.3% SCC Mountain View 208.1 207 \n", + " 99.2% SCC Los Altos Hills 91.3 91 \n", + " 99.0% SCC Palo Alto 297.2 294 \n", + " 99.0% SMC San Carlos 99.0 98 \n", + " 90.9% SMC Burleigh Murray Park 2.1 1.9 \n", + " 90.4% SMC Foster City 150.0 136 \n", + " 86.8% SCC Foothills Preserve 1.1 1.0 \n", + " 77.9% SMC San Mateo Highlands 18.0 14 \n", + " 74.5% SMC Skyline Ridge OSP 0.8 0.6 \n", + " 74.1% SCC San Francisco Bay Trail 260.8 193 \n", + " 73.2% CCC Rosie Riveter Park 5.5 4.0 \n", + " 71.5% SMC Burlingame Hills 6.0 4.3 \n", + " 66.7% SMC Coal Creek Preserve 3.9 2.6 \n", + " 63.5% --- San Mateo County 2814.0 1,787 \n", + " 59.5% SMC Russian Ridge Preserve 12.2 7.3 \n", + " 59.3% SMC Montara 27.8 16 \n", + " 56.2% SMC Burlingame 88.4 50 \n", + " 54.7% SMC Belmont 98.1 54 \n", + " 53.3% SCC Sunnyvale 357.0 190 \n", + " 52.3% SMC Hillsborough 85.3 45 \n", + " 51.8% SMC San Mateo 256.0 133 \n", + " 51.5% SMC Half Moon Bay State Beach 4.4 2.3 \n", + " 51.2% SMC Long Ridge Preserve 11.0 5.6 \n", + " 51.2% SCC Castle Rock State Park 11.2 5.7 \n", + " 51.0% ALA Newark 147.0 75 \n", + " 50.2% SMC Brisbane 40.9 21 \n", + " 47.7% SCC Edenvale 30.0 14 \n", + " 47.3% NSW Barangaroo 1.7 0.8 \n", + " 47.2% SCC Gardner 23.4 11 \n", + " 45.1% SCC Monte Sereno 20.4 9.2 \n", + " 43.9% SFC Presidio Terrace 2.8 1.2 \n", + " 43.6% SMC El Granada 49.2 21 \n", + " 43.6% SMC Moss Beach 19.7 8.6 \n", + " 43.3% ALA Hayward Acres 3.5 1.5 \n", + " 42.6% SMC Millbrae 65.0 28 \n", + " 40.8% ALA San Lorenzo 55.5 23 \n", + " 39.6% SFC Lincoln Park 4.5 1.8 \n", + " 39.5% SCC Communications Hill 27.8 11 \n", + " 39.0% SMC Purisima Creek Preserve 16.5 6.4 \n", + " 38.9% SMC Colma 13.7 5.3 \n", + " 38.7% MAR Mt Tamalpais State Park 31.7 12 \n", + " 38.2% SMC Broadmoor 8.8 3.4 \n", + " 37.4% SFC South Beach 4.8 1.8 \n", + " 37.1% MAR Muir Beach 4.6 1.7 \n", + " 36.8% SFC Lake Street 3.9 1.4 \n", + " 36.8% SCC Spartan Keyes 64.3 24 \n", + " 36.7% SCC Milpitas 224.0 82 \n", + " 36.1% ALA Ashland 35.1 13 \n", + " 36.0% SCC Willow Glen 81.6 29 \n", + " 34.8% SCC Santa Clara 348.0 121 \n", + " 34.7% MAS MIT 9.6 3.3 \n", + " 34.3% NSW Millers Point 3.2 1.1 \n", + " 33.7% SCC Parkview 42.5 14 \n", + " 33.6% SMC Half Moon Bay 68.0 23 \n", + " 33.6% SCC Los Gatos 148.0 50 \n", + " 33.6% SCC Cupertino 172.0 58 \n", + " 33.2% SMC Pacifica 150.9 50 \n", + " 33.0% --- Santa Clara County 7569.0 2,498 \n", + " 33.0% SCC Seven Trees 40.9 13 \n", + " 32.9% ALA Union City 208.8 69 \n", + " 32.9% MAR Stinson Beach 11.2 3.7 \n", + " 32.9% ALA Fremont 780.2 257 \n", + " 32.7% SCC Branham 44.0 14 \n", + " 32.7% ALA Hayward 444.5 145 \n", + " 31.9% MAR Marin Headlands GGNRA 65.7 21 \n", + " 31.5% SCC San Martin 35.3 11 \n", + " 31.0% SMC San Bruno 114.0 35 \n", + " 30.9% SCC Willow Glen South 63.3 20 \n", + " 30.7% SCC Forest of Nisene Marks SP 44.0 14 \n", + " 30.0% SCC Saratoga 180.0 54 \n", + " 29.6% SMC South San Francisco 185.3 55 \n", + " 29.4% SFC Golden Gate Park 40.8 12 \n", + " 29.3% SCC Campbell 119.0 35 \n", + " 29.3% SFC Seacliff 4.1 1.2 \n", + " 29.2% NSW Dawes Point 1.8 0.5 \n", + " 28.2% ALA Fairview 34.4 9.7 \n", + " 28.0% SCC San Jose 2618.7 733 \n", + " 27.8% ALA Cherryland 20.9 5.8 \n", + " 27.7% ALA San Leandro 230.6 64 \n", + " 27.4% SMC Daly City 148.1 41 \n", + " 26.8% CAL Mokelumne Hill 14.7 3.9 \n", + " 26.7% SFC Presidio National Park 43.5 12 \n", + " 26.1% ALA Castro Valley 192.5 50 \n", + " 25.6% SMC Bay Area Ridge Trail 395.6 101 \n", + " 23.6% SON Guerneville 22.7 5.4 \n", + " 21.6% SFC Presidio Heights 6.5 1.4 \n", + " 20.6% SFC Panhandle 7.3 1.5 \n", + " 18.2% SFC Balboa Terrace 3.4 0.6 \n", + " 18.2% SFC Polk Gulch 4.0 0.7 \n", + " 18.0% SFC Cole Valley 1.7 0.3 \n", + " 17.8% SON Healdsburg 53.7 9.6 \n", + " 17.0% SON Bodega Bay 28.9 4.9 \n", + " 16.6% --- Alameda County 5818.0 965 \n", + " 15.9% SFC Forest Hill 6.1 1.0 \n", + " 15.5% SFC Northern Waterfront 5.6 0.9 \n", + " 15.4% SFC Aquatic Park Fort Mason 6.4 1.0 \n", + " 15.2% SFC Little Hollywood 3.7 0.6 \n", + " 14.2% SFC Clarendon Heights 6.0 0.9 \n", + " 13.8% SFC Fisherman's Wharf 6.2 0.9 \n", + " 13.2% SFC Sutro Heights 7.1 0.9 \n", + " 13.0% SFC Ashbury Heights 3.7 0.5 \n", + " 12.9% MAR Corte Madera 51.0 6.6 \n", + " 12.9% MAR Sausalito 32.7 4.2 \n", + " 12.3% SFC Dogpatch 5.1 0.6 \n", + " 12.2% ALA Alameda 206.7 25 \n", + " 12.1% SCC Gilroy 188.9 23 \n", + " 11.9% SFC Cow Hollow 12.0 1.4 \n", + " 10.9% --- Marin County 2333.0 255 \n", + " 10.7% SFC Pacific Heights 18.0 1.9 \n", + " 10.7% SFC Golden Gate Heights 17.8 1.9 \n", + " 10.2% SFC Financial District 9.4 1.0 \n", + " 9.3% --- San Francisco County 1217.0 113 \n", + " 9.1% MAR Mill Valley 92.2 8.4 \n", + " 8.9% --- Napa County 1609.0 143 \n", + " 8.6% SFC Mission Bay 13.8 1.2 \n", + " 7.7% ALA Emeryville 28.1 2.2 \n", + " 7.6% ALA Berkeley 260.3 20 \n", + " 7.1% --- Santa Cruz County 2718.0 194 \n", + " 6.8% ALA Albany 42.7 2.9 \n", + " 6.2% MAS Cambridge 180.8 11 \n", + " 6.0% SFC Central Waterfront 10.2 0.6 \n", + " 5.1% --- Sonoma County 4895.0 251 \n", + " 3.8% --- Contra Costa County 5945.0 226 \n", + " 3.7% MAR San Rafael 260.0 9.6 \n", + " 1.8% --- California 377037.0 6,719 \n", + " 0.113% --- USA 6406754.0 7,267 \n", + " 0.017% --- Earth 41974536.0 7,159 \n", + "\n", + " to next badge \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " 0.2 mi to 99% \n", + " 13 mi to 99% \n", + " 0.0 mi to 90% \n", + " 2.2 mi to 90% \n", + " 0.1 mi to 90% \n", + " 41 mi to 90% \n", + " 0.9 mi to 90% \n", + " 1.1 mi to 90% \n", + " 0.9 mi to 90% \n", + " 746 mi to 90% \n", + " 3.7 mi to 90% \n", + " 8.5 mi to 90% \n", + " 30 mi to 90% \n", + " 35 mi to 90% \n", + " 131 mi to 90% \n", + " 32 mi to 90% \n", + " 98 mi to 90% \n", + " 1.7 mi to 90% \n", + " 4.3 mi to 90% \n", + " 4.3 mi to 90% \n", + " 57 mi to 90% \n", + " 16 mi to 90% \n", + " 0.7 mi to 50% \n", + " 0.0 mi to 50% \n", + " 0.7 mi to 50% \n", + " 1.0 mi to 50% \n", + " 0.2 mi to 50% \n", + " 3.1 mi to 50% \n", + " 1.3 mi to 50% \n", + " 0.2 mi to 50% \n", + " 4.8 mi to 50% \n", + " 5.1 mi to 50% \n", + " 0.5 mi to 50% \n", + " 2.9 mi to 50% \n", + " 1.8 mi to 50% \n", + " 1.5 mi to 50% \n", + " 3.6 mi to 50% \n", + " 1.0 mi to 50% \n", + " 0.6 mi to 50% \n", + " 0.6 mi to 50% \n", + " 0.5 mi to 50% \n", + " 8.5 mi to 50% \n", + " 30 mi to 50% \n", + " 4.9 mi to 50% \n", + " 11 mi to 50% \n", + " 53 mi to 50% \n", + " 1.5 mi to 50% \n", + " 0.5 mi to 50% \n", + " 6.9 mi to 50% \n", + " 11 mi to 50% \n", + " 24 mi to 50% \n", + " 28 mi to 50% \n", + " 25 mi to 50% \n", + " 1,287 mi to 50% \n", + " 7.0 mi to 50% \n", + " 36 mi to 50% \n", + " 1.9 mi to 50% \n", + " 133 mi to 50% \n", + " 7.6 mi to 50% \n", + " 77 mi to 50% \n", + " 12 mi to 50% \n", + " 6.5 mi to 50% \n", + " 22 mi to 50% \n", + " 12 mi to 50% \n", + " 8.5 mi to 50% \n", + " 36 mi to 50% \n", + " 38 mi to 50% \n", + " 8.4 mi to 50% \n", + " 25 mi to 50% \n", + " 0.8 mi to 50% \n", + " 0.4 mi to 50% \n", + " 7.5 mi to 50% \n", + " 576 mi to 50% \n", + " 4.6 mi to 50% \n", + " 51 mi to 50% \n", + " 33 mi to 50% \n", + " 3.4 mi to 50% \n", + " 10 mi to 50% \n", + " 46 mi to 50% \n", + " 97 mi to 50% \n", + " 0.3 mi to 25% \n", + " 0.2 mi to 25% \n", + " 0.3 mi to 25% \n", + " 0.2 mi to 25% \n", + " 0.3 mi to 25% \n", + " 0.1 mi to 25% \n", + " 3.9 mi to 25% \n", + " 2.3 mi to 25% \n", + " 490 mi to 25% \n", + " 0.6 mi to 25% \n", + " 0.5 mi to 25% \n", + " 0.6 mi to 25% \n", + " 0.4 mi to 25% \n", + " 0.6 mi to 25% \n", + " 0.7 mi to 25% \n", + " 0.8 mi to 25% \n", + " 0.4 mi to 25% \n", + " 6.2 mi to 25% \n", + " 4.0 mi to 25% \n", + " 0.6 mi to 25% \n", + " 26 mi to 25% \n", + " 24 mi to 25% \n", + " 1.6 mi to 25% \n", + " 328 mi to 25% \n", + " 2.6 mi to 25% \n", + " 2.5 mi to 25% \n", + " 1.4 mi to 25% \n", + " 192 mi to 25% \n", + " 15 mi to 25% \n", + " 259 mi to 25% \n", + " 2.3 mi to 25% \n", + " 4.9 mi to 25% \n", + " 45 mi to 25% \n", + " 486 mi to 25% \n", + " 7.8 mi to 25% \n", + " 34 mi to 25% \n", + " 1.9 mi to 25% \n", + " 973 mi to 25% \n", + " 1,260 mi to 25% \n", + " 55 mi to 25% \n", + " 822 mi to 2% \n", + " 5,546 mi to 0.2% \n", + " 1,236 mi to 0.02% " + ] + }, + "execution_count": 88, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ @@ -692,12 +2254,12 @@ "source": [ "As part of my wandering, in April 2022 I was able to get to 25% of every city that rings the San Francisco Bay and is below San Francisco or Oakland (see map [with](ring2.jpeg) or [without](ring1.jpeg) roads traveled; as soon as you get 25% of a city, it lights up with a color).\n", "\n", - "I live at the border of Santa Clara County (SCC) and San Mateo County (SMC), so I ride in both. Wandrer.earth says that Jason Molenda is a whopping 1,700 miles ahead of me in SMC and Megan Gardner is 1,000 miles ahead of me in SCC. Barry Mann is the leader in both total miles in the two counties and average percent. Kudos to all of them! However, I do occupy a small section of the [Pareto front](https://en.wikipedia.org/wiki/Pareto_front) for the two counties together: no single rider on wandrer.earth has done more than me in *both* counties. Here are the leaders (as of September 2023), where the dotted line indicates the five riders on the Pareto front." + "I live at the border of Santa Clara County (SCC) and San Mateo County (SMC), so I ride in both. Wandrer.earth says that Jason Molenda is a whopping 1,700 miles ahead of me in SCC and Megan Gardner is 1,000 miles ahead of me in SMC. Barry Mann is the leader in total miles in the two counties, and Megan leads in average percent. Kudos to all of them! However, I do occupy a small section of the [Pareto front](https://en.wikipedia.org/wiki/Pareto_front) for the two counties together: no single rider on wandrer.earth has done more than me in *both* counties. Here are the leaders (as of December 2023), where the dotted line indicates the Pareto front." ] }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 89, "metadata": {}, "outputs": [ { @@ -734,91 +2296,58 @@ " \n", " \n", " \n", - " Barry Mann\n", - " BM\n", - " 76.97\n", - " 30.21\n", - " 2166\n", - " 2287\n", - " 4453\n", - " 53.590\n", + " Megan Gardner\n", + " MG\n", + " 99.01\n", + " 13.60\n", + " 2786\n", + " 1029\n", + " 3815\n", + " 56.305\n", " \n", " \n", " \n", - " Megan Gardner\n", - " MG\n", - " 97.62\n", - " 8.69\n", - " 2747\n", - " 658\n", - " 3405\n", - " 53.155\n", + " Barry Mann\n", + " BM\n", + " 77.41\n", + " 30.38\n", + " 2178\n", + " 2299\n", + " 4477\n", + " 53.895\n", " \n", " \n", " \n", " Peter Norvig\n", " PN\n", - " 61.56\n", - " 32.80\n", - " 1732\n", - " 2483\n", - " 4215\n", - " 47.180\n", - " \n", - " \n", - " \n", - " Matthew Ring\n", - " MR\n", - " 78.85\n", - " 1.48\n", - " 2219\n", - " 112\n", - " 2331\n", - " 40.165\n", + " 63.50\n", + " 33.00\n", + " 1787\n", + " 2498\n", + " 4285\n", + " 48.250\n", " \n", " \n", " \n", " Brian Feinberg\n", " BF\n", " 32.50\n", - " 43.68\n", + " 43.90\n", " 915\n", - " 3306\n", - " 4221\n", - " 38.090\n", + " 3323\n", + " 4238\n", + " 38.200\n", " \n", " \n", " \n", " Jason Molenda\n", " JM\n", - " 7.13\n", - " 55.39\n", - " 201\n", - " 4192\n", - " 4393\n", - " 31.260\n", - " \n", - " \n", - " \n", - " Elliot Hoff\n", - " EF\n", - " 52.88\n", - " 8.14\n", - " 1488\n", - " 616\n", - " 2104\n", - " 30.510\n", - " \n", - " \n", - " \n", - " Jim Brooks\n", - " JB\n", - " 4.23\n", - " 44.36\n", - " 119\n", - " 3358\n", - " 3477\n", - " 24.295\n", + " 7.56\n", + " 56.25\n", + " 213\n", + " 4258\n", + " 4471\n", + " 31.905\n", " \n", " \n", "\n", @@ -826,33 +2355,27 @@ ], "text/plain": [ " Name Initials SMC % SCC % SMC miles SCC miles Total miles \\\n", - " Barry Mann BM 76.97 30.21 2166 2287 4453 \n", - " Megan Gardner MG 97.62 8.69 2747 658 3405 \n", - " Peter Norvig PN 61.56 32.80 1732 2483 4215 \n", - " Matthew Ring MR 78.85 1.48 2219 112 2331 \n", - " Brian Feinberg BF 32.50 43.68 915 3306 4221 \n", - " Jason Molenda JM 7.13 55.39 201 4192 4393 \n", - " Elliot Hoff EF 52.88 8.14 1488 616 2104 \n", - " Jim Brooks JB 4.23 44.36 119 3358 3477 \n", + " Megan Gardner MG 99.01 13.60 2786 1029 3815 \n", + " Barry Mann BM 77.41 30.38 2178 2299 4477 \n", + " Peter Norvig PN 63.50 33.00 1787 2498 4285 \n", + " Brian Feinberg BF 32.50 43.90 915 3323 4238 \n", + " Jason Molenda JM 7.56 56.25 213 4258 4471 \n", "\n", " Avg % \n", - " 53.590 \n", - " 53.155 \n", - " 47.180 \n", - " 40.165 \n", - " 38.090 \n", - " 31.260 \n", - " 30.510 \n", - " 24.295 " + " 56.305 \n", + " 53.895 \n", + " 48.250 \n", + " 38.200 \n", + " 31.905 " ] }, - "execution_count": 96, + "execution_count": 89, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -873,16 +2396,14 @@ "source": [ "# Eddington Number\n", "\n", - "The British physicist [Sir Arthur Eddington](https://en.wikipedia.org/wiki/Arthur_Eddington), a contemporary of Einstein, was a pre-Strava bicyclist who favored this metric:\n", - "\n", - "> Your [**Eddington Number**](https://www.triathlete.com/2011/04/training/measuring-bike-miles-eddington-number_301789) is the largest integer **e** such that you have cycled at least **e** miles on at least **e** days.\n", + "The physicist/bicyclist [Sir Arthur Eddington](https://en.wikipedia.org/wiki/Arthur_Eddington), a contemporary of Einstein defined the [**Eddington Number**](https://www.triathlete.com/2011/04/training/measuring-bike-miles-eddington-number_301789) as the largest integer **E** such that you have cycled at least **E** miles on at least **E** days.\n", "\n", "My Eddington number progress over the years, in both kilometers and miles:" ] }, { "cell_type": "code", - "execution_count": 97, + "execution_count": 90, "metadata": {}, "outputs": [ { @@ -914,6 +2435,12 @@ " \n", " \n", " \n", + " 2024\n", + " 102\n", + " 68\n", + " \n", + " \n", + " \n", " 2023\n", " 101\n", " 67\n", @@ -978,6 +2505,7 @@ ], "text/plain": [ " year Ed_km Ed_mi\n", + " 2024 102 68\n", " 2023 101 67\n", " 2022 96 66\n", " 2021 93 65\n", @@ -990,7 +2518,7 @@ " 2014 46 35" ] }, - "execution_count": 97, + "execution_count": 90, "metadata": {}, "output_type": "execute_result" } @@ -1003,7 +2531,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "My current Eddington Number is **101** in kilometers and **67** in miles: I've ridden at least 67 miles on at least 67 days (but not 68 miles on 68 days). My number is above [the median for Strava](https://swinny.net/Cycling/-4687-Calculate-your-Eddington-Number), but not nearly as good as Eddington himself: his number was **84** in miles when he died at age 62, and his roads, bicycles, and navigation aids were not nearly as nice as mine, so bravo zulu to him. " + "My current Eddington Number is **102** in kilometers and **68** in miles (I've ridden at least 68 miles on at least 68 days, but not 69 miles on 69 days). My number is above [the median for Strava](https://swinny.net/Cycling/-4687-Calculate-your-Eddington-Number), but not nearly as good as Eddington himself: his number was **84** (in miles) when he died at age 62, and his roads, weather, bicycles, and navigation aids were not nearly as nice as mine, so bravo zulu to him. " ] }, { @@ -1015,7 +2543,7 @@ }, { "cell_type": "code", - "execution_count": 98, + "execution_count": 91, "metadata": {}, "outputs": [ { @@ -1040,111 +2568,73 @@ " \n", " \n", " kms\n", - " km rides\n", " kms gap\n", " miles\n", - " miles rides\n", " miles gap\n", " \n", " \n", " \n", " \n", " \n", - " 100\n", - " 107\n", - " -7\n", - " 67\n", - " 74\n", - " -7\n", - " \n", - " \n", - " \n", - " 101\n", - " 105\n", - " -4\n", - " 68\n", - " 67\n", - " 1\n", - " \n", - " \n", - " \n", - " 102\n", - " 99\n", - " 3\n", - " 69\n", - " 54\n", - " 15\n", - " \n", - " \n", - " \n", " 103\n", - " 97\n", - " 6\n", - " 70\n", - " 44\n", - " 26\n", + " 4\n", + " 69\n", + " 13\n", " \n", " \n", " \n", " 104\n", - " 91\n", - " 13\n", - " 71\n", - " 37\n", - " 34\n", + " 11\n", + " 70\n", + " 24\n", " \n", " \n", " \n", " 105\n", - " 86\n", - " 19\n", - " 72\n", - " 35\n", - " 37\n", + " 17\n", + " 71\n", + " 34\n", " \n", " \n", " \n", " 106\n", - " 83\n", - " 23\n", - " 73\n", - " 32\n", - " 41\n", + " 21\n", + " 72\n", + " 37\n", " \n", " \n", " \n", " 107\n", - " 76\n", - " 31\n", - " 74\n", - " 31\n", - " 43\n", + " 29\n", + " 73\n", + " 41\n", " \n", " \n", " \n", " 108\n", - " 73\n", - " 35\n", - " 75\n", - " 24\n", - " 51\n", + " 33\n", + " 74\n", + " 43\n", " \n", " \n", " \n", " 109\n", - " 68\n", - " 41\n", - " 76\n", - " 23\n", - " 53\n", + " 39\n", + " 75\n", + " 51\n", " \n", " \n", " \n", " 110\n", - " 61\n", - " 49\n", + " 47\n", + " 76\n", + " 53\n", + " \n", + " \n", + " \n", + " 111\n", + " 55\n", " 77\n", - " 21\n", " 56\n", " \n", " \n", @@ -1152,21 +2642,19 @@ "" ], "text/plain": [ - " kms km rides kms gap miles miles rides miles gap\n", - " 100 107 -7 67 74 -7\n", - " 101 105 -4 68 67 1\n", - " 102 99 3 69 54 15\n", - " 103 97 6 70 44 26\n", - " 104 91 13 71 37 34\n", - " 105 86 19 72 35 37\n", - " 106 83 23 73 32 41\n", - " 107 76 31 74 31 43\n", - " 108 73 35 75 24 51\n", - " 109 68 41 76 23 53\n", - " 110 61 49 77 21 56" + " kms kms gap miles miles gap\n", + " 103 4 69 13\n", + " 104 11 70 24\n", + " 105 17 71 34\n", + " 106 21 72 37\n", + " 107 29 73 41\n", + " 108 33 74 43\n", + " 109 39 75 51\n", + " 110 47 76 53\n", + " 111 55 77 56" ] }, - "execution_count": 98, + "execution_count": 91, "metadata": {}, "output_type": "execute_result" } @@ -1179,7 +2667,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "I need three 102-km rides to improve from 101 to 102; In miles, I need one 68-mile ride to improve from 67 to 68.\n", + "I need four 103-km or thirteen 69-mile rides to increase my Eddington numbers.\n", "\n", "Here are some properties of Eddington numbers:\n", "- Your Eddington number is monotonic: it can never decrease over time. \n", @@ -1215,7 +2703,7 @@ }, { "cell_type": "code", - "execution_count": 99, + "execution_count": 92, "metadata": {}, "outputs": [ { @@ -1255,12 +2743,12 @@ }, { "cell_type": "code", - "execution_count": 100, + "execution_count": 93, "metadata": {}, "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -1272,7 +2760,7 @@ } ], "source": [ - "show('fpm', 'mph', rides, 'Speed (miles per hour) versus Ride Grade (feet per mile)')" + "show('fpmi', 'mph', rides, 'Speed (miles per hour) versus Ride Grade (feet per mile)')" ] }, { @@ -1286,7 +2774,7 @@ }, { "cell_type": "code", - "execution_count": 101, + "execution_count": 94, "metadata": {}, "outputs": [ { @@ -1295,7 +2783,7 @@ "'Coast: 70 min, Creek: 64 min.'" ] }, - "execution_count": 101, + "execution_count": 94, "metadata": {}, "output_type": "execute_result" } @@ -1317,14 +2805,14 @@ "source": [ "# VAM\n", "\n", - "Climbing speed measured by vertical ascent in meters per hour is known as [VAM](https://en.wikipedia.org/wiki/VAM_%28bicycling%29), which stands for *velocità ascensionale media* (for native Campagnolo speakers) or *vertical ascent in meters* (for SRAM) or 平均上昇率 (for Shimano), and sometimes by Vm/h. The theory is that for fairly steep climbs, most of your power is going into lifting against gravity, so your VAM should be about constant no matter what the grade. (For flatish climbs power is spent on wind and rolling resistance, and for the very steepest of climbs, in my experience, power goes largely to cursing *sotto voce*, as they say in Italian.) \n", + "Climbing speed is measured by [VAM](https://en.wikipedia.org/wiki/VAM_%28bicycling%29), which stands for *velocità ascensionale media* (for native Campagnolo speakers) or *vertical ascent in meters per hour* (for SRAM) or 平均上昇率 (for Shimano), or *Vm/h* (for physicists). The theory is that for fairly steep climbs, most of your power is going into lifting against gravity, so your VAM should be about constant no matter what the grade. (For flatish segments power is spent on wind and rolling resistance, and for the very steepest of climbs, in my experience, power goes largely to cursing *sotto voce*, as they say in Italian.) \n", "\n", "Here's a plot of my VAM versus grade over short segments:" ] }, { "cell_type": "code", - "execution_count": 102, + "execution_count": 95, "metadata": {}, "outputs": [ { @@ -1348,7 +2836,2402 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Champion cyclists can do over 1800 meters/hour over a 10 km climb, and can sustain [1400 meters/hour for 7 hours](https://www.strava.com/activities/4996833865). My VAM numbers range mostly from 400 to 900 meters/hour, but I can sustain the higher numbers for only a couple of minutes:" + "Champion cyclists can do over 1800 meters/hour over a 10 km climb, and can sustain [1400 meters/hour for 7 hours](https://www.strava.com/activities/4996833865). My VAM numbers range mostly from 400 to 800 meters/hour, and I can sustain the higher numbers for only a couple of minutes:" + ] + }, + { + "cell_type": "code", + "execution_count": 96, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
titlehoursmilesfeetmphvamfpmipctkmsmeters
Camaritas climb0.010.104810.001463.0480.09.090.1615.0
Paloma Climb0.020.14827.001250.0586.011.090.2325.0
Klamath Dr.0.020.12776.001173.0642.012.150.1923.0
Entrance Way Hill Repeats0.020.10765.001158.0760.014.390.1623.0
Davenport Kicker0.020.247412.001128.0308.05.840.3923.0
Valparaiso steep0.040.181454.501105.0806.015.260.2944.0
Invernes to Firecrest Climb0.040.281437.001090.0511.09.670.4544.0
Kings Mountain final sprint0.040.311357.751029.0435.08.250.5041.0
Limantour Spit0.090.473035.221026.0645.012.210.7692.0
Tunitas flattens0.050.421668.401012.0395.07.490.6851.0
Tunitas flattens0.050.421668.401012.0395.07.490.6851.0
Cemetery Sprint0.010.08338.001006.0412.07.810.1310.0
Skyline Bump at OLH0.020.216310.50960.0300.05.680.3419.0
Laning Bump0.030.24948.00955.0392.07.420.3929.0
Faught Turn0.020.226011.00914.0273.05.170.3518.0
Westridge 3min0.080.372404.62914.0649.012.290.6073.0
Valparaiso steep0.050.181453.60884.0806.015.260.2944.0
Sharon Park steep part0.030.21867.00874.0410.07.760.3426.0
Old La Honda Mile 10.130.993707.62868.0374.07.081.59113.0
Joaquin0.090.332543.67860.0770.014.580.5377.0
\n", + "
" + ], + "text/plain": [ + " title hours miles feet mph vam fpmi \\\n", + " Camaritas climb 0.01 0.10 48 10.00 1463.0 480.0 \n", + " Paloma Climb 0.02 0.14 82 7.00 1250.0 586.0 \n", + " Klamath Dr. 0.02 0.12 77 6.00 1173.0 642.0 \n", + " Entrance Way Hill Repeats 0.02 0.10 76 5.00 1158.0 760.0 \n", + " Davenport Kicker 0.02 0.24 74 12.00 1128.0 308.0 \n", + " Valparaiso steep 0.04 0.18 145 4.50 1105.0 806.0 \n", + " Invernes to Firecrest Climb 0.04 0.28 143 7.00 1090.0 511.0 \n", + " Kings Mountain final sprint 0.04 0.31 135 7.75 1029.0 435.0 \n", + " Limantour Spit 0.09 0.47 303 5.22 1026.0 645.0 \n", + " Tunitas flattens 0.05 0.42 166 8.40 1012.0 395.0 \n", + " Tunitas flattens 0.05 0.42 166 8.40 1012.0 395.0 \n", + " Cemetery Sprint 0.01 0.08 33 8.00 1006.0 412.0 \n", + " Skyline Bump at OLH 0.02 0.21 63 10.50 960.0 300.0 \n", + " Laning Bump 0.03 0.24 94 8.00 955.0 392.0 \n", + " Faught Turn 0.02 0.22 60 11.00 914.0 273.0 \n", + " Westridge 3min 0.08 0.37 240 4.62 914.0 649.0 \n", + " Valparaiso steep 0.05 0.18 145 3.60 884.0 806.0 \n", + " Sharon Park steep part 0.03 0.21 86 7.00 874.0 410.0 \n", + " Old La Honda Mile 1 0.13 0.99 370 7.62 868.0 374.0 \n", + " Joaquin 0.09 0.33 254 3.67 860.0 770.0 \n", + "\n", + " pct kms meters \n", + " 9.09 0.16 15.0 \n", + " 11.09 0.23 25.0 \n", + " 12.15 0.19 23.0 \n", + " 14.39 0.16 23.0 \n", + " 5.84 0.39 23.0 \n", + " 15.26 0.29 44.0 \n", + " 9.67 0.45 44.0 \n", + " 8.25 0.50 41.0 \n", + " 12.21 0.76 92.0 \n", + " 7.49 0.68 51.0 \n", + " 7.49 0.68 51.0 \n", + " 7.81 0.13 10.0 \n", + " 5.68 0.34 19.0 \n", + " 7.42 0.39 29.0 \n", + " 5.17 0.35 18.0 \n", + " 12.29 0.60 73.0 \n", + " 15.26 0.29 44.0 \n", + " 7.76 0.34 26.0 \n", + " 7.08 1.59 113.0 \n", + " 14.58 0.53 77.0 " + ] + }, + "execution_count": 96, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "top(segments, 'vam')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "On segments that are at least a kilometer long my VAM tops out at about 800 meters/hour:" + ] + }, + { + "cell_type": "code", + "execution_count": 97, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
titlehoursmilesfeetmphvamfpmipctkmsmeters
Old La Honda Mile 10.130.993707.62868.0374.07.081.59113.0
Westridge0.140.683854.86838.0566.010.721.09117.0
Old La Honda (Bridge to Stop)0.483.3312556.94797.0377.07.145.36383.0
Old La Honda (Bridge to Stop)0.513.3312556.53750.0377.07.145.36383.0
Westridge0.160.683854.25733.0566.010.721.09117.0
Tunitas steep0.251.205994.80730.0499.09.451.93183.0
Old La Honda Mile 10.160.993706.19705.0374.07.081.59113.0
Woodside Climb0.131.7129513.15692.0173.03.272.7590.0
Huddart0.170.923855.41690.0418.07.931.48117.0
Top of Groton Rd heading west0.130.922917.08682.0316.05.991.4889.0
Watts (Sonoma)0.141.203138.57681.0261.04.941.9395.0
Tunitas steep0.271.205994.44676.0499.09.451.93183.0
Canon to No Cycling0.090.751988.33671.0264.05.001.2160.0
Canon to No Cycling0.090.751988.33671.0264.05.001.2160.0
Coe Second Switchback to flat0.221.004834.55669.0483.09.151.61147.0
Lower Redwood Gulch0.221.034744.68657.0460.08.721.66144.0
Lobitas Creek0.200.964304.80655.0448.08.481.54131.0
West Alpine switchback0.150.783225.20654.0413.07.821.2698.0
Kaboom Portola Rd0.050.6710213.40622.0152.02.881.0831.0
Huddart0.190.923854.84618.0418.07.931.48117.0
Kings Greer to Skyline0.783.9215365.03600.0392.07.426.31468.0
Woodside Climb0.151.7129511.40599.0173.03.272.7590.0
Stage Rd0.191.013735.32598.0369.06.991.63114.0
Try not to fall back0.210.714103.38595.0577.010.941.14125.0
Sand Gill Sharon-top0.070.8513612.14592.0160.03.031.3741.0
Sand Gill Sharon-top0.070.8513612.14592.0160.03.031.3741.0
Mt Eden climb0.141.022727.29592.0267.05.051.6483.0
Tunitas lower climb0.221.304215.91583.0324.06.132.09128.0
Kings Greer to Skyline0.813.9215364.84578.0392.07.426.31468.0
Haskins0.301.515665.03575.0375.07.102.43173.0
\n", + "
" + ], + "text/plain": [ + " title hours miles feet mph vam fpmi \\\n", + " Old La Honda Mile 1 0.13 0.99 370 7.62 868.0 374.0 \n", + " Westridge 0.14 0.68 385 4.86 838.0 566.0 \n", + " Old La Honda (Bridge to Stop) 0.48 3.33 1255 6.94 797.0 377.0 \n", + " Old La Honda (Bridge to Stop) 0.51 3.33 1255 6.53 750.0 377.0 \n", + " Westridge 0.16 0.68 385 4.25 733.0 566.0 \n", + " Tunitas steep 0.25 1.20 599 4.80 730.0 499.0 \n", + " Old La Honda Mile 1 0.16 0.99 370 6.19 705.0 374.0 \n", + " Woodside Climb 0.13 1.71 295 13.15 692.0 173.0 \n", + " Huddart 0.17 0.92 385 5.41 690.0 418.0 \n", + " Top of Groton Rd heading west 0.13 0.92 291 7.08 682.0 316.0 \n", + " Watts (Sonoma) 0.14 1.20 313 8.57 681.0 261.0 \n", + " Tunitas steep 0.27 1.20 599 4.44 676.0 499.0 \n", + " Canon to No Cycling 0.09 0.75 198 8.33 671.0 264.0 \n", + " Canon to No Cycling 0.09 0.75 198 8.33 671.0 264.0 \n", + " Coe Second Switchback to flat 0.22 1.00 483 4.55 669.0 483.0 \n", + " Lower Redwood Gulch 0.22 1.03 474 4.68 657.0 460.0 \n", + " Lobitas Creek 0.20 0.96 430 4.80 655.0 448.0 \n", + " West Alpine switchback 0.15 0.78 322 5.20 654.0 413.0 \n", + " Kaboom Portola Rd 0.05 0.67 102 13.40 622.0 152.0 \n", + " Huddart 0.19 0.92 385 4.84 618.0 418.0 \n", + " Kings Greer to Skyline 0.78 3.92 1536 5.03 600.0 392.0 \n", + " Woodside Climb 0.15 1.71 295 11.40 599.0 173.0 \n", + " Stage Rd 0.19 1.01 373 5.32 598.0 369.0 \n", + " Try not to fall back 0.21 0.71 410 3.38 595.0 577.0 \n", + " Sand Gill Sharon-top 0.07 0.85 136 12.14 592.0 160.0 \n", + " Sand Gill Sharon-top 0.07 0.85 136 12.14 592.0 160.0 \n", + " Mt Eden climb 0.14 1.02 272 7.29 592.0 267.0 \n", + " Tunitas lower climb 0.22 1.30 421 5.91 583.0 324.0 \n", + " Kings Greer to Skyline 0.81 3.92 1536 4.84 578.0 392.0 \n", + " Haskins 0.30 1.51 566 5.03 575.0 375.0 \n", + "\n", + " pct kms meters \n", + " 7.08 1.59 113.0 \n", + " 10.72 1.09 117.0 \n", + " 7.14 5.36 383.0 \n", + " 7.14 5.36 383.0 \n", + " 10.72 1.09 117.0 \n", + " 9.45 1.93 183.0 \n", + " 7.08 1.59 113.0 \n", + " 3.27 2.75 90.0 \n", + " 7.93 1.48 117.0 \n", + " 5.99 1.48 89.0 \n", + " 4.94 1.93 95.0 \n", + " 9.45 1.93 183.0 \n", + " 5.00 1.21 60.0 \n", + " 5.00 1.21 60.0 \n", + " 9.15 1.61 147.0 \n", + " 8.72 1.66 144.0 \n", + " 8.48 1.54 131.0 \n", + " 7.82 1.26 98.0 \n", + " 2.88 1.08 31.0 \n", + " 7.93 1.48 117.0 \n", + " 7.42 6.31 468.0 \n", + " 3.27 2.75 90.0 \n", + " 6.99 1.63 114.0 \n", + " 10.94 1.14 125.0 \n", + " 3.03 1.37 41.0 \n", + " 3.03 1.37 41.0 \n", + " 5.05 1.64 83.0 \n", + " 6.13 2.09 128.0 \n", + " 7.42 6.31 468.0 \n", + " 7.10 2.43 173.0 " + ] + }, + "execution_count": 97, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "top(segments[segments.kms >= 1], 'vam', n=30)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "I can also look at VAM numbers for complete rides. I would expect the ride VAM to be half the segment VAM (or less) since most of my rides are circuits where I return to the start, and thus no more than half the ride is climbing. Sure enough, the best I can do is about 400 meters/hour:" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
dateyeartitlehoursmilesfeetmphvamfpmipctkmsmeters
Sun, 11/29/20152015Mt. Hamilton3.6837.00490210.05406.0132.02.5159.531494.0
Fri, 4/2/20212021Everesting 5: climb 2×(OLH + WOLH)3.2731.4843449.63405.0138.02.6150.651324.0
Tue, 3/30/20212021Everesting 2: Kings + WOLH + OLH3.3435.99437710.78399.0122.02.3057.911334.0
Mon, 3/29/20212021Everesting 1: Mt Diablo2.6022.2234068.55399.0153.02.9035.751038.0
Sat, 11/25/20172017Mt. Hamilton3.6936.6548069.93397.0131.02.4858.971465.0
Sun, 12/1/20132013Mt. Hamilton3.7837.5649219.94397.0131.02.4860.431500.0
Fri, 10/30/20152015OLH / West Alpine3.4839.51450511.35395.0114.02.1663.571373.0
Sat, 4/26/20142014OLH / Tunitas Creek5.2658.69674211.16391.0115.02.1894.432055.0
Sat, 4/18/20152015Tunitas + Lobitos Creeks5.2461.27661111.69385.0108.02.0498.582015.0
Wed, 10/14/20152015Half Moon Bay6.1372.97764411.90380.0105.01.98117.412330.0
Sun, 6/4/20172017Sequoia Challenge6.2966.52752010.58364.0113.02.14107.032292.0
Sat, 7/25/20152015Palo Alto, California4.0443.62481910.80364.0110.02.0970.181469.0
Sat, 10/11/20142014OLH / Tunitas5.0958.29604411.45362.0104.01.9693.791842.0
Sat, 8/13/20162016Petaluma / Point Reyes4.5054.75528612.17358.097.01.8388.091611.0
Fri, 8/28/20152015Pescadaro via OLH5.3166.01613712.43352.093.01.76106.211871.0
Sun, 4/4/20212021Everesting 7: Mill Creek / Morrison Canyon3.0829.3835179.54348.0120.02.2747.271072.0
Sat, 2/10/20242024Seacliff, etc.4.7263.41536513.43346.085.01.60102.031635.0
Wed, 6/18/20142014Sierra to the Sea Day 44.9657.64556111.62342.096.01.8392.741695.0
Sun, 6/3/20182018The Sequoia5.9764.92667710.87341.0103.01.95104.462035.0
Sat, 5/9/20152015OLH2.5032.33278812.93340.086.01.6352.02850.0
\n", + "
" + ], + "text/plain": [ + " date year title hours \\\n", + " Sun, 11/29/2015 2015 Mt. Hamilton 3.68 \n", + " Fri, 4/2/2021 2021 Everesting 5: climb 2×(OLH + WOLH) 3.27 \n", + " Tue, 3/30/2021 2021 Everesting 2: Kings + WOLH + OLH 3.34 \n", + " Mon, 3/29/2021 2021 Everesting 1: Mt Diablo 2.60 \n", + " Sat, 11/25/2017 2017 Mt. Hamilton 3.69 \n", + " Sun, 12/1/2013 2013 Mt. Hamilton 3.78 \n", + " Fri, 10/30/2015 2015 OLH / West Alpine 3.48 \n", + " Sat, 4/26/2014 2014 OLH / Tunitas Creek 5.26 \n", + " Sat, 4/18/2015 2015 Tunitas + Lobitos Creeks 5.24 \n", + " Wed, 10/14/2015 2015 Half Moon Bay 6.13 \n", + " Sun, 6/4/2017 2017 Sequoia Challenge 6.29 \n", + " Sat, 7/25/2015 2015 Palo Alto, California 4.04 \n", + " Sat, 10/11/2014 2014 OLH / Tunitas 5.09 \n", + " Sat, 8/13/2016 2016 Petaluma / Point Reyes 4.50 \n", + " Fri, 8/28/2015 2015 Pescadaro via OLH 5.31 \n", + " Sun, 4/4/2021 2021 Everesting 7: Mill Creek / Morrison Canyon 3.08 \n", + " Sat, 2/10/2024 2024 Seacliff, etc. 4.72 \n", + " Wed, 6/18/2014 2014 Sierra to the Sea Day 4 4.96 \n", + " Sun, 6/3/2018 2018 The Sequoia 5.97 \n", + " Sat, 5/9/2015 2015 OLH 2.50 \n", + "\n", + " miles feet mph vam fpmi pct kms meters \n", + " 37.00 4902 10.05 406.0 132.0 2.51 59.53 1494.0 \n", + " 31.48 4344 9.63 405.0 138.0 2.61 50.65 1324.0 \n", + " 35.99 4377 10.78 399.0 122.0 2.30 57.91 1334.0 \n", + " 22.22 3406 8.55 399.0 153.0 2.90 35.75 1038.0 \n", + " 36.65 4806 9.93 397.0 131.0 2.48 58.97 1465.0 \n", + " 37.56 4921 9.94 397.0 131.0 2.48 60.43 1500.0 \n", + " 39.51 4505 11.35 395.0 114.0 2.16 63.57 1373.0 \n", + " 58.69 6742 11.16 391.0 115.0 2.18 94.43 2055.0 \n", + " 61.27 6611 11.69 385.0 108.0 2.04 98.58 2015.0 \n", + " 72.97 7644 11.90 380.0 105.0 1.98 117.41 2330.0 \n", + " 66.52 7520 10.58 364.0 113.0 2.14 107.03 2292.0 \n", + " 43.62 4819 10.80 364.0 110.0 2.09 70.18 1469.0 \n", + " 58.29 6044 11.45 362.0 104.0 1.96 93.79 1842.0 \n", + " 54.75 5286 12.17 358.0 97.0 1.83 88.09 1611.0 \n", + " 66.01 6137 12.43 352.0 93.0 1.76 106.21 1871.0 \n", + " 29.38 3517 9.54 348.0 120.0 2.27 47.27 1072.0 \n", + " 63.41 5365 13.43 346.0 85.0 1.60 102.03 1635.0 \n", + " 57.64 5561 11.62 342.0 96.0 1.83 92.74 1695.0 \n", + " 64.92 6677 10.87 341.0 103.0 1.95 104.46 2035.0 \n", + " 32.33 2788 12.93 340.0 86.0 1.63 52.02 850.0 " + ] + }, + "execution_count": 98, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "top(rides, 'vam')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Exploring the Data\n", + "\n", + "\n", + "Some more ways to look at the data, both rides and segments." + ] + }, + { + "cell_type": "code", + "execution_count": 99, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
yearhoursmilesfeetmphvamfpmipctkmsmeters
count537.000000537.000000537.000000537.000000537.000000537.000000537.000000537.000000537.000000537.000000
mean2016.9329613.34558742.7248981816.79888312.977039157.87150841.5847300.78737468.744246553.765363
std2.4878761.44414317.2693251494.5422451.31359690.16680427.2955220.51683927.786324455.534057
min2012.0000001.54000020.96000068.0000008.55000010.0000003.0000000.05000033.72000021.000000
25%2015.0000002.21000029.030000731.00000012.16000081.00000020.0000000.37000046.710000223.000000
50%2017.0000002.86000036.7100001368.00000013.120000153.00000036.0000000.69000059.070000417.000000
75%2018.0000004.32000056.2600002323.00000013.770000219.00000056.0000001.07000090.520000708.000000
max2024.0000008.140000102.4100007644.00000016.750000406.000000153.0000002.900000164.7800002330.000000
\n", + "
" + ], + "text/plain": [ + " year hours miles feet mph \\\n", + "count 537.000000 537.000000 537.000000 537.000000 537.000000 \n", + "mean 2016.932961 3.345587 42.724898 1816.798883 12.977039 \n", + "std 2.487876 1.444143 17.269325 1494.542245 1.313596 \n", + "min 2012.000000 1.540000 20.960000 68.000000 8.550000 \n", + "25% 2015.000000 2.210000 29.030000 731.000000 12.160000 \n", + "50% 2017.000000 2.860000 36.710000 1368.000000 13.120000 \n", + "75% 2018.000000 4.320000 56.260000 2323.000000 13.770000 \n", + "max 2024.000000 8.140000 102.410000 7644.000000 16.750000 \n", + "\n", + " vam fpmi pct kms meters \n", + "count 537.000000 537.000000 537.000000 537.000000 537.000000 \n", + "mean 157.871508 41.584730 0.787374 68.744246 553.765363 \n", + "std 90.166804 27.295522 0.516839 27.786324 455.534057 \n", + "min 10.000000 3.000000 0.050000 33.720000 21.000000 \n", + "25% 81.000000 20.000000 0.370000 46.710000 223.000000 \n", + "50% 153.000000 36.000000 0.690000 59.070000 417.000000 \n", + "75% 219.000000 56.000000 1.070000 90.520000 708.000000 \n", + "max 406.000000 153.000000 2.900000 164.780000 2330.000000 " + ] + }, + "execution_count": 99, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "rides.describe() # Summary statistics for the rides" + ] + }, + { + "cell_type": "code", + "execution_count": 100, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
hoursmilesfeetmphvamfpmipctkmsmeters
count141.000000141.000000141.000000141.000000141.000000141.000000141.000000141.000000141.000000
mean0.1417020.932979268.9787237.444539641.893617353.5035466.6948941.50106481.978723
std0.1737110.989832295.0606593.557336211.923595186.8163203.5379741.59226789.957408
min0.0100000.08000021.0000002.120000111.00000018.0000000.3500000.1300006.000000
25%0.0500000.330000104.0000004.830000503.000000219.0000004.1400000.53000032.000000
50%0.0900000.600000166.0000006.190000630.000000333.0000006.3000000.97000051.000000
75%0.1500001.190000303.0000009.800000724.000000462.0000008.7600001.91000092.000000
max1.3900007.3800001887.00000019.7900001463.000000839.00000015.89000011.870000575.000000
\n", + "
" + ], + "text/plain": [ + " hours miles feet mph vam \\\n", + "count 141.000000 141.000000 141.000000 141.000000 141.000000 \n", + "mean 0.141702 0.932979 268.978723 7.444539 641.893617 \n", + "std 0.173711 0.989832 295.060659 3.557336 211.923595 \n", + "min 0.010000 0.080000 21.000000 2.120000 111.000000 \n", + "25% 0.050000 0.330000 104.000000 4.830000 503.000000 \n", + "50% 0.090000 0.600000 166.000000 6.190000 630.000000 \n", + "75% 0.150000 1.190000 303.000000 9.800000 724.000000 \n", + "max 1.390000 7.380000 1887.000000 19.790000 1463.000000 \n", + "\n", + " fpmi pct kms meters \n", + "count 141.000000 141.000000 141.000000 141.000000 \n", + "mean 353.503546 6.694894 1.501064 81.978723 \n", + "std 186.816320 3.537974 1.592267 89.957408 \n", + "min 18.000000 0.350000 0.130000 6.000000 \n", + "25% 219.000000 4.140000 0.530000 32.000000 \n", + "50% 333.000000 6.300000 0.970000 51.000000 \n", + "75% 462.000000 8.760000 1.910000 92.000000 \n", + "max 839.000000 15.890000 11.870000 575.000000 " + ] + }, + "execution_count": 100, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "segments.describe() # Summary statistics for the segments" + ] + }, + { + "cell_type": "code", + "execution_count": 101, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
dateyeartitlehoursmilesfeetmphvamfpmipctkmsmeters
Sun, 5/22/20162016Canada2.1936.68133216.75185.036.00.6959.02406.0
Wed, 9/13/20172017Healdburg / Jimtown2.1334.4591216.17131.026.00.5055.43278.0
Sat, 1/25/20142014Woodside1.5625.08124316.08243.050.00.9440.35379.0
Sat, 4/11/20152015Woodside1.5424.73103516.06205.042.00.7939.79315.0
Sun, 7/11/20212021San Jose4.1065.10108615.8881.017.00.32104.75331.0
Sun, 1/18/20152015Woodside1.6426.02125715.87234.048.00.9141.87383.0
Fri, 6/24/20162016Foothill Expway1.5925.1162315.79119.025.00.4740.40190.0
Sun, 1/26/20142014Canada Rd2.1033.12144615.77210.044.00.8353.29441.0
Fri, 1/6/20122012Omarama to Wanaka New Zealand4.4870.35326215.70222.046.00.88113.19994.0
Sun, 4/12/20152015Palo Alto Cycling2.0331.76121015.65182.038.00.7251.10369.0
Sun, 10/15/20172017Los Gatos2.8644.71143715.63153.032.00.6171.94438.0
Sun, 8/5/20182018Bike Ride Northwest Day 13.5855.77182415.58155.033.00.6289.73556.0
Sun, 2/28/20162016Woodside Loop1.7326.9384315.57149.031.00.5943.33257.0
Sun, 6/26/20162016Los Gatos3.2850.78118115.48110.023.00.4481.71360.0
Mon, 1/19/20152015Canada Rd, etc.2.9545.64183615.47190.040.00.7673.43560.0
Sun, 1/19/20142014Palo Alto, CA1.6225.01120115.44226.048.00.9140.24366.0
Sun, 12/6/20152015Canada Rd2.2534.67123715.41168.036.00.6855.78377.0
Tue, 6/18/20132013work etc (headwinds)2.0631.4880915.28120.026.00.4950.65247.0
Fri, 9/23/20162016Los Gatos2.8943.93133915.20141.030.00.5870.68408.0
Sat, 7/9/20162016Santa Cruz3.8458.23404215.16321.069.01.3193.691232.0
\n", + "
" + ], + "text/plain": [ + " date year title hours miles feet \\\n", + " Sun, 5/22/2016 2016 Canada 2.19 36.68 1332 \n", + " Wed, 9/13/2017 2017 Healdburg / Jimtown 2.13 34.45 912 \n", + " Sat, 1/25/2014 2014 Woodside 1.56 25.08 1243 \n", + " Sat, 4/11/2015 2015 Woodside 1.54 24.73 1035 \n", + " Sun, 7/11/2021 2021 San Jose 4.10 65.10 1086 \n", + " Sun, 1/18/2015 2015 Woodside 1.64 26.02 1257 \n", + " Fri, 6/24/2016 2016 Foothill Expway 1.59 25.11 623 \n", + " Sun, 1/26/2014 2014 Canada Rd 2.10 33.12 1446 \n", + " Fri, 1/6/2012 2012 Omarama to Wanaka New Zealand 4.48 70.35 3262 \n", + " Sun, 4/12/2015 2015 Palo Alto Cycling 2.03 31.76 1210 \n", + " Sun, 10/15/2017 2017 Los Gatos 2.86 44.71 1437 \n", + " Sun, 8/5/2018 2018 Bike Ride Northwest Day 1 3.58 55.77 1824 \n", + " Sun, 2/28/2016 2016 Woodside Loop 1.73 26.93 843 \n", + " Sun, 6/26/2016 2016 Los Gatos 3.28 50.78 1181 \n", + " Mon, 1/19/2015 2015 Canada Rd, etc. 2.95 45.64 1836 \n", + " Sun, 1/19/2014 2014 Palo Alto, CA 1.62 25.01 1201 \n", + " Sun, 12/6/2015 2015 Canada Rd 2.25 34.67 1237 \n", + " Tue, 6/18/2013 2013 work etc (headwinds) 2.06 31.48 809 \n", + " Fri, 9/23/2016 2016 Los Gatos 2.89 43.93 1339 \n", + " Sat, 7/9/2016 2016 Santa Cruz 3.84 58.23 4042 \n", + "\n", + " mph vam fpmi pct kms meters \n", + " 16.75 185.0 36.0 0.69 59.02 406.0 \n", + " 16.17 131.0 26.0 0.50 55.43 278.0 \n", + " 16.08 243.0 50.0 0.94 40.35 379.0 \n", + " 16.06 205.0 42.0 0.79 39.79 315.0 \n", + " 15.88 81.0 17.0 0.32 104.75 331.0 \n", + " 15.87 234.0 48.0 0.91 41.87 383.0 \n", + " 15.79 119.0 25.0 0.47 40.40 190.0 \n", + " 15.77 210.0 44.0 0.83 53.29 441.0 \n", + " 15.70 222.0 46.0 0.88 113.19 994.0 \n", + " 15.65 182.0 38.0 0.72 51.10 369.0 \n", + " 15.63 153.0 32.0 0.61 71.94 438.0 \n", + " 15.58 155.0 33.0 0.62 89.73 556.0 \n", + " 15.57 149.0 31.0 0.59 43.33 257.0 \n", + " 15.48 110.0 23.0 0.44 81.71 360.0 \n", + " 15.47 190.0 40.0 0.76 73.43 560.0 \n", + " 15.44 226.0 48.0 0.91 40.24 366.0 \n", + " 15.41 168.0 36.0 0.68 55.78 377.0 \n", + " 15.28 120.0 26.0 0.49 50.65 247.0 \n", + " 15.20 141.0 30.0 0.58 70.68 408.0 \n", + " 15.16 321.0 69.0 1.31 93.69 1232.0 " + ] + }, + "execution_count": 101, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "top(rides, 'mph') # Fastest rides (of more than 20 miles, that I sampled into database)" + ] + }, + { + "cell_type": "code", + "execution_count": 102, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
titlehoursmilesfeetmphvamfpmipctkmsmeters
PCH Pescadero to Bean Hollow0.142.775119.79111.018.00.354.4616.0
Highway 1 Cascanoa to Cascade0.091.618917.89301.055.01.052.5927.0
Vickrey Fruitvale0.060.996816.50345.069.01.301.5921.0
Highway 9 Mantalvo0.030.453515.00356.078.01.470.7211.0
Highway 9 Mantalvo0.030.453515.00356.078.01.470.7211.0
The Boneyard0.101.4813514.80411.091.01.732.3841.0
Vickrey Fruitvale0.070.996814.14296.069.01.301.5921.0
Sand Hill Alpine to 2800.121.6718013.92457.0108.02.042.6955.0
Canada to College0.101.3711913.70363.087.01.652.2036.0
Foothill Homestead0.091.2212613.56427.0103.01.961.9638.0
The Boneyard0.111.4813513.45374.091.01.732.3841.0
Kaboom Portola Rd0.050.6710213.40622.0152.02.881.0831.0
Woodside Climb0.131.7129513.15692.0173.03.272.7590.0
Sand Hill Alpine to 2800.131.6718012.85422.0108.02.042.6955.0
Alpine Westridge0.060.769912.67503.0130.02.471.2230.0
Alpine Westridge0.060.769912.67503.0130.02.471.2230.0
Stanford Ave0.050.638512.60518.0135.02.561.0126.0
Canada to College0.111.3711912.45330.087.01.652.2036.0
Sand Hill 280 to horse0.040.499512.25724.0194.03.670.7929.0
Stevens Country Park0.101.2211212.20341.092.01.741.9634.0
\n", + "
" + ], + "text/plain": [ + " title hours miles feet mph vam fpmi \\\n", + " PCH Pescadero to Bean Hollow 0.14 2.77 51 19.79 111.0 18.0 \n", + " Highway 1 Cascanoa to Cascade 0.09 1.61 89 17.89 301.0 55.0 \n", + " Vickrey Fruitvale 0.06 0.99 68 16.50 345.0 69.0 \n", + " Highway 9 Mantalvo 0.03 0.45 35 15.00 356.0 78.0 \n", + " Highway 9 Mantalvo 0.03 0.45 35 15.00 356.0 78.0 \n", + " The Boneyard 0.10 1.48 135 14.80 411.0 91.0 \n", + " Vickrey Fruitvale 0.07 0.99 68 14.14 296.0 69.0 \n", + " Sand Hill Alpine to 280 0.12 1.67 180 13.92 457.0 108.0 \n", + " Canada to College 0.10 1.37 119 13.70 363.0 87.0 \n", + " Foothill Homestead 0.09 1.22 126 13.56 427.0 103.0 \n", + " The Boneyard 0.11 1.48 135 13.45 374.0 91.0 \n", + " Kaboom Portola Rd 0.05 0.67 102 13.40 622.0 152.0 \n", + " Woodside Climb 0.13 1.71 295 13.15 692.0 173.0 \n", + " Sand Hill Alpine to 280 0.13 1.67 180 12.85 422.0 108.0 \n", + " Alpine Westridge 0.06 0.76 99 12.67 503.0 130.0 \n", + " Alpine Westridge 0.06 0.76 99 12.67 503.0 130.0 \n", + " Stanford Ave 0.05 0.63 85 12.60 518.0 135.0 \n", + " Canada to College 0.11 1.37 119 12.45 330.0 87.0 \n", + " Sand Hill 280 to horse 0.04 0.49 95 12.25 724.0 194.0 \n", + " Stevens Country Park 0.10 1.22 112 12.20 341.0 92.0 \n", + "\n", + " pct kms meters \n", + " 0.35 4.46 16.0 \n", + " 1.05 2.59 27.0 \n", + " 1.30 1.59 21.0 \n", + " 1.47 0.72 11.0 \n", + " 1.47 0.72 11.0 \n", + " 1.73 2.38 41.0 \n", + " 1.30 1.59 21.0 \n", + " 2.04 2.69 55.0 \n", + " 1.65 2.20 36.0 \n", + " 1.96 1.96 38.0 \n", + " 1.73 2.38 41.0 \n", + " 2.88 1.08 31.0 \n", + " 3.27 2.75 90.0 \n", + " 2.04 2.69 55.0 \n", + " 2.47 1.22 30.0 \n", + " 2.47 1.22 30.0 \n", + " 2.56 1.01 26.0 \n", + " 1.65 2.20 36.0 \n", + " 3.67 0.79 29.0 \n", + " 1.74 1.96 34.0 " + ] + }, + "execution_count": 102, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "top(segments, 'mph') # Fastest segments (there are no descent segments in the database)" ] }, { @@ -1383,321 +5266,321 @@ " feet\n", " mph\n", " vam\n", - " fpm\n", + " fpmi\n", " pct\n", " kms\n", - " km_up\n", + " meters\n", " \n", " \n", " \n", " \n", - " 14\n", - " Camaritas climb\n", - " 0.01\n", - " 0.10\n", - " 48\n", - " 10.00\n", - " 1463.0\n", - " 480.0\n", - " 9.09\n", - " 0.16\n", - " 0.0\n", + " \n", + " West Alpine full\n", + " 1.39\n", + " 7.38\n", + " 1887\n", + " 5.31\n", + " 414.0\n", + " 256.0\n", + " 4.84\n", + " 11.87\n", + " 575.0\n", " \n", " \n", - " 71\n", - " Paloma Climb\n", - " 0.02\n", - " 0.14\n", - " 82\n", - " 7.00\n", - " 1250.0\n", - " 586.0\n", - " 11.09\n", - " 0.23\n", - " 0.0\n", - " \n", - " \n", - " 55\n", - " Klamath Dr.\n", - " 0.02\n", - " 0.12\n", - " 77\n", - " 6.00\n", - " 1173.0\n", - " 642.0\n", - " 12.15\n", - " 0.19\n", - " 0.0\n", - " \n", - " \n", - " 28\n", - " Entrance Way Hill Repeats\n", - " 0.02\n", - " 0.10\n", - " 76\n", - " 5.00\n", - " 1158.0\n", - " 760.0\n", - " 14.39\n", - " 0.16\n", - " 0.0\n", - " \n", - " \n", - " 25\n", - " Davenport Kicker\n", - " 0.02\n", - " 0.24\n", - " 74\n", - " 12.00\n", - " 1128.0\n", - " 308.0\n", - " 5.84\n", - " 0.39\n", - " 0.0\n", - " \n", - " \n", - " 121\n", - " Valparaiso steep\n", - " 0.04\n", - " 0.18\n", - " 145\n", - " 4.50\n", - " 1105.0\n", - " 806.0\n", - " 15.26\n", - " 0.29\n", - " 0.0\n", - " \n", - " \n", - " 42\n", - " Invernes to Firecrest Climb\n", - " 0.04\n", - " 0.28\n", - " 143\n", - " 7.00\n", - " 1090.0\n", - " 511.0\n", - " 9.67\n", - " 0.45\n", - " 0.0\n", - " \n", - " \n", - " 54\n", - " Kings Mountain final sprint\n", - " 0.04\n", - " 0.31\n", - " 135\n", - " 7.75\n", - " 1029.0\n", - " 435.0\n", - " 8.25\n", - " 0.50\n", - " 0.0\n", - " \n", - " \n", - " 57\n", - " Limantour Spit\n", - " 0.09\n", - " 0.47\n", - " 303\n", - " 5.22\n", - " 1026.0\n", - " 645.0\n", - " 12.21\n", - " 0.76\n", - " 0.1\n", - " \n", - " \n", - " 115\n", - " Tunitas flattens\n", - " 0.05\n", - " 0.42\n", - " 166\n", - " 8.40\n", - " 1012.0\n", - " 395.0\n", - " 7.49\n", - " 0.68\n", - " 0.1\n", - " \n", - " \n", - " 116\n", - " Tunitas flattens\n", - " 0.05\n", - " 0.42\n", - " 166\n", - " 8.40\n", - " 1012.0\n", - " 395.0\n", - " 7.49\n", - " 0.68\n", - " 0.1\n", - " \n", - " \n", - " 21\n", - " Cemetery Sprint\n", - " 0.01\n", - " 0.08\n", - " 33\n", - " 8.00\n", - " 1006.0\n", - " 412.0\n", - " 7.81\n", - " 0.13\n", - " 0.0\n", - " \n", - " \n", - " 93\n", - " Skyline Bump at OLH\n", - " 0.02\n", - " 0.21\n", - " 63\n", - " 10.50\n", - " 960.0\n", - " 300.0\n", - " 5.68\n", - " 0.34\n", - " 0.0\n", - " \n", - " \n", - " 56\n", - " Laning Bump\n", - " 0.03\n", - " 0.24\n", - " 94\n", - " 8.00\n", - " 955.0\n", + " \n", + " Kings Greer to Skyline\n", + " 0.78\n", + " 3.92\n", + " 1536\n", + " 5.03\n", + " 600.0\n", " 392.0\n", " 7.42\n", - " 0.39\n", - " 0.0\n", + " 6.31\n", + " 468.0\n", " \n", " \n", - " 29\n", - " Faught Turn\n", - " 0.02\n", - " 0.22\n", - " 60\n", - " 11.00\n", - " 914.0\n", - " 273.0\n", - " 5.17\n", - " 0.35\n", - " 0.0\n", + " \n", + " Kings Greer to Skyline\n", + " 0.81\n", + " 3.92\n", + " 1536\n", + " 4.84\n", + " 578.0\n", + " 392.0\n", + " 7.42\n", + " 6.31\n", + " 468.0\n", " \n", " \n", - " 131\n", - " Westridge 3min\n", - " 0.08\n", - " 0.37\n", - " 240\n", - " 4.62\n", - " 914.0\n", - " 649.0\n", - " 12.29\n", - " 0.60\n", - " 0.1\n", + " \n", + " Old La Honda (Bridge to Stop)\n", + " 0.48\n", + " 3.33\n", + " 1255\n", + " 6.94\n", + " 797.0\n", + " 377.0\n", + " 7.14\n", + " 5.36\n", + " 383.0\n", " \n", " \n", - " 122\n", - " Valparaiso steep\n", - " 0.05\n", - " 0.18\n", - " 145\n", - " 3.60\n", - " 884.0\n", - " 806.0\n", - " 15.26\n", - " 0.29\n", - " 0.0\n", + " \n", + " Old La Honda (Bridge to Stop)\n", + " 0.51\n", + " 3.33\n", + " 1255\n", + " 6.53\n", + " 750.0\n", + " 377.0\n", + " 7.14\n", + " 5.36\n", + " 383.0\n", " \n", " \n", - " 92\n", - " Sharon Park steep part\n", - " 0.03\n", - " 0.21\n", - " 86\n", - " 7.00\n", - " 874.0\n", - " 410.0\n", - " 7.76\n", - " 0.34\n", - " 0.0\n", - " \n", - " \n", - " 69\n", - " Old La Honda Mile 1\n", - " 0.13\n", - " 0.99\n", - " 370\n", - " 7.62\n", - " 868.0\n", - " 374.0\n", - " 7.08\n", - " 1.59\n", - " 0.1\n", - " \n", - " \n", - " 43\n", - " Joaquin\n", - " 0.09\n", - " 0.33\n", - " 254\n", - " 3.67\n", - " 860.0\n", - " 770.0\n", - " 14.58\n", + " \n", + " Alma Mountain Charlie\n", " 0.53\n", - " 0.1\n", + " 3.12\n", + " 875\n", + " 5.89\n", + " 503.0\n", + " 280.0\n", + " 5.31\n", + " 5.02\n", + " 267.0\n", + " \n", + " \n", + " \n", + " Kings half way\n", + " 0.46\n", + " 2.89\n", + " 820\n", + " 6.28\n", + " 543.0\n", + " 284.0\n", + " 5.37\n", + " 4.65\n", + " 250.0\n", + " \n", + " \n", + " \n", + " Kings half way\n", + " 0.50\n", + " 2.89\n", + " 820\n", + " 5.78\n", + " 500.0\n", + " 284.0\n", + " 5.37\n", + " 4.65\n", + " 250.0\n", + " \n", + " \n", + " \n", + " Alpine Portola to top Joaquin\n", + " 0.57\n", + " 3.52\n", + " 801\n", + " 6.18\n", + " 428.0\n", + " 228.0\n", + " 4.31\n", + " 5.66\n", + " 244.0\n", + " \n", + " \n", + " \n", + " Alpine Portola to top Joaquin\n", + " 0.58\n", + " 3.52\n", + " 801\n", + " 6.07\n", + " 421.0\n", + " 228.0\n", + " 4.31\n", + " 5.66\n", + " 244.0\n", + " \n", + " \n", + " \n", + " Tunitas steep\n", + " 0.27\n", + " 1.20\n", + " 599\n", + " 4.44\n", + " 676.0\n", + " 499.0\n", + " 9.45\n", + " 1.93\n", + " 183.0\n", + " \n", + " \n", + " \n", + " Tunitas steep\n", + " 0.25\n", + " 1.20\n", + " 599\n", + " 4.80\n", + " 730.0\n", + " 499.0\n", + " 9.45\n", + " 1.93\n", + " 183.0\n", + " \n", + " \n", + " \n", + " Haskins\n", + " 0.30\n", + " 1.51\n", + " 566\n", + " 5.03\n", + " 575.0\n", + " 375.0\n", + " 7.10\n", + " 2.43\n", + " 173.0\n", + " \n", + " \n", + " \n", + " Haskins\n", + " 0.31\n", + " 1.51\n", + " 566\n", + " 4.87\n", + " 557.0\n", + " 375.0\n", + " 7.10\n", + " 2.43\n", + " 173.0\n", + " \n", + " \n", + " \n", + " Coe Second Switchback to flat\n", + " 0.22\n", + " 1.00\n", + " 483\n", + " 4.55\n", + " 669.0\n", + " 483.0\n", + " 9.15\n", + " 1.61\n", + " 147.0\n", + " \n", + " \n", + " \n", + " Lower Redwood Gulch\n", + " 0.22\n", + " 1.03\n", + " 474\n", + " 4.68\n", + " 657.0\n", + " 460.0\n", + " 8.72\n", + " 1.66\n", + " 144.0\n", + " \n", + " \n", + " \n", + " Alpine Willowbrook to Joaquin\n", + " 0.29\n", + " 2.27\n", + " 461\n", + " 7.83\n", + " 485.0\n", + " 203.0\n", + " 3.85\n", + " 3.65\n", + " 141.0\n", + " \n", + " \n", + " \n", + " Alpine Willowbrook to Joaquin\n", + " 0.28\n", + " 2.27\n", + " 461\n", + " 8.11\n", + " 502.0\n", + " 203.0\n", + " 3.85\n", + " 3.65\n", + " 141.0\n", + " \n", + " \n", + " \n", + " Lobitas Creek\n", + " 0.20\n", + " 0.96\n", + " 430\n", + " 4.80\n", + " 655.0\n", + " 448.0\n", + " 8.48\n", + " 1.54\n", + " 131.0\n", + " \n", + " \n", + " \n", + " Tunitas lower climb\n", + " 0.22\n", + " 1.30\n", + " 421\n", + " 5.91\n", + " 583.0\n", + " 324.0\n", + " 6.13\n", + " 2.09\n", + " 128.0\n", " \n", " \n", "\n", "" ], "text/plain": [ - " title hours miles feet mph vam fpm \\\n", - "14 Camaritas climb 0.01 0.10 48 10.00 1463.0 480.0 \n", - "71 Paloma Climb 0.02 0.14 82 7.00 1250.0 586.0 \n", - "55 Klamath Dr. 0.02 0.12 77 6.00 1173.0 642.0 \n", - "28 Entrance Way Hill Repeats 0.02 0.10 76 5.00 1158.0 760.0 \n", - "25 Davenport Kicker 0.02 0.24 74 12.00 1128.0 308.0 \n", - "121 Valparaiso steep 0.04 0.18 145 4.50 1105.0 806.0 \n", - "42 Invernes to Firecrest Climb 0.04 0.28 143 7.00 1090.0 511.0 \n", - "54 Kings Mountain final sprint 0.04 0.31 135 7.75 1029.0 435.0 \n", - "57 Limantour Spit 0.09 0.47 303 5.22 1026.0 645.0 \n", - "115 Tunitas flattens 0.05 0.42 166 8.40 1012.0 395.0 \n", - "116 Tunitas flattens 0.05 0.42 166 8.40 1012.0 395.0 \n", - "21 Cemetery Sprint 0.01 0.08 33 8.00 1006.0 412.0 \n", - "93 Skyline Bump at OLH 0.02 0.21 63 10.50 960.0 300.0 \n", - "56 Laning Bump 0.03 0.24 94 8.00 955.0 392.0 \n", - "29 Faught Turn 0.02 0.22 60 11.00 914.0 273.0 \n", - "131 Westridge 3min 0.08 0.37 240 4.62 914.0 649.0 \n", - "122 Valparaiso steep 0.05 0.18 145 3.60 884.0 806.0 \n", - "92 Sharon Park steep part 0.03 0.21 86 7.00 874.0 410.0 \n", - "69 Old La Honda Mile 1 0.13 0.99 370 7.62 868.0 374.0 \n", - "43 Joaquin 0.09 0.33 254 3.67 860.0 770.0 \n", + " title hours miles feet mph vam fpmi pct \\\n", + " West Alpine full 1.39 7.38 1887 5.31 414.0 256.0 4.84 \n", + " Kings Greer to Skyline 0.78 3.92 1536 5.03 600.0 392.0 7.42 \n", + " Kings Greer to Skyline 0.81 3.92 1536 4.84 578.0 392.0 7.42 \n", + " Old La Honda (Bridge to Stop) 0.48 3.33 1255 6.94 797.0 377.0 7.14 \n", + " Old La Honda (Bridge to Stop) 0.51 3.33 1255 6.53 750.0 377.0 7.14 \n", + " Alma Mountain Charlie 0.53 3.12 875 5.89 503.0 280.0 5.31 \n", + " Kings half way 0.46 2.89 820 6.28 543.0 284.0 5.37 \n", + " Kings half way 0.50 2.89 820 5.78 500.0 284.0 5.37 \n", + " Alpine Portola to top Joaquin 0.57 3.52 801 6.18 428.0 228.0 4.31 \n", + " Alpine Portola to top Joaquin 0.58 3.52 801 6.07 421.0 228.0 4.31 \n", + " Tunitas steep 0.27 1.20 599 4.44 676.0 499.0 9.45 \n", + " Tunitas steep 0.25 1.20 599 4.80 730.0 499.0 9.45 \n", + " Haskins 0.30 1.51 566 5.03 575.0 375.0 7.10 \n", + " Haskins 0.31 1.51 566 4.87 557.0 375.0 7.10 \n", + " Coe Second Switchback to flat 0.22 1.00 483 4.55 669.0 483.0 9.15 \n", + " Lower Redwood Gulch 0.22 1.03 474 4.68 657.0 460.0 8.72 \n", + " Alpine Willowbrook to Joaquin 0.29 2.27 461 7.83 485.0 203.0 3.85 \n", + " Alpine Willowbrook to Joaquin 0.28 2.27 461 8.11 502.0 203.0 3.85 \n", + " Lobitas Creek 0.20 0.96 430 4.80 655.0 448.0 8.48 \n", + " Tunitas lower climb 0.22 1.30 421 5.91 583.0 324.0 6.13 \n", "\n", - " pct kms km_up \n", - "14 9.09 0.16 0.0 \n", - "71 11.09 0.23 0.0 \n", - "55 12.15 0.19 0.0 \n", - "28 14.39 0.16 0.0 \n", - "25 5.84 0.39 0.0 \n", - "121 15.26 0.29 0.0 \n", - "42 9.67 0.45 0.0 \n", - "54 8.25 0.50 0.0 \n", - "57 12.21 0.76 0.1 \n", - "115 7.49 0.68 0.1 \n", - "116 7.49 0.68 0.1 \n", - "21 7.81 0.13 0.0 \n", - "93 5.68 0.34 0.0 \n", - "56 7.42 0.39 0.0 \n", - "29 5.17 0.35 0.0 \n", - "131 12.29 0.60 0.1 \n", - "122 15.26 0.29 0.0 \n", - "92 7.76 0.34 0.0 \n", - "69 7.08 1.59 0.1 \n", - "43 14.58 0.53 0.1 " + " kms meters \n", + " 11.87 575.0 \n", + " 6.31 468.0 \n", + " 6.31 468.0 \n", + " 5.36 383.0 \n", + " 5.36 383.0 \n", + " 5.02 267.0 \n", + " 4.65 250.0 \n", + " 4.65 250.0 \n", + " 5.66 244.0 \n", + " 5.66 244.0 \n", + " 1.93 183.0 \n", + " 1.93 183.0 \n", + " 2.43 173.0 \n", + " 2.43 173.0 \n", + " 1.61 147.0 \n", + " 1.66 144.0 \n", + " 3.65 141.0 \n", + " 3.65 141.0 \n", + " 1.54 131.0 \n", + " 2.09 128.0 " ] }, "execution_count": 103, @@ -1706,14 +5589,7 @@ } ], "source": [ - "top(segments, 'vam')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "On segments that are at least a kilometer long my VAM tops out at about 800 meters/hour:" + "top(segments, 'feet') # Biggest climbing segments" ] }, { @@ -1748,28 +5624,236 @@ " feet\n", " mph\n", " vam\n", - " fpm\n", + " fpmi\n", " pct\n", " kms\n", - " km_up\n", + " meters\n", " \n", " \n", " \n", " \n", - " 69\n", - " Old La Honda Mile 1\n", - " 0.13\n", - " 0.99\n", - " 370\n", - " 7.62\n", - " 868.0\n", - " 374.0\n", - " 7.08\n", - " 1.59\n", - " 0.1\n", + " \n", + " Redwood Gulch hits\n", + " 0.06\n", + " 0.18\n", + " 151\n", + " 3.00\n", + " 767.0\n", + " 839.0\n", + " 15.89\n", + " 0.29\n", + " 46.0\n", " \n", " \n", - " 129\n", + " \n", + " Valparaiso steep\n", + " 0.04\n", + " 0.18\n", + " 145\n", + " 4.50\n", + " 1105.0\n", + " 806.0\n", + " 15.26\n", + " 0.29\n", + " 44.0\n", + " \n", + " \n", + " \n", + " Valparaiso steep\n", + " 0.05\n", + " 0.18\n", + " 145\n", + " 3.60\n", + " 884.0\n", + " 806.0\n", + " 15.26\n", + " 0.29\n", + " 44.0\n", + " \n", + " \n", + " \n", + " Limantour steepest\n", + " 0.09\n", + " 0.20\n", + " 159\n", + " 2.22\n", + " 538.0\n", + " 795.0\n", + " 15.06\n", + " 0.32\n", + " 48.0\n", + " \n", + " \n", + " \n", + " Joaquin\n", + " 0.10\n", + " 0.33\n", + " 254\n", + " 3.30\n", + " 774.0\n", + " 770.0\n", + " 14.58\n", + " 0.53\n", + " 77.0\n", + " \n", + " \n", + " \n", + " Joaquin\n", + " 0.09\n", + " 0.33\n", + " 254\n", + " 3.67\n", + " 860.0\n", + " 770.0\n", + " 14.58\n", + " 0.53\n", + " 77.0\n", + " \n", + " \n", + " \n", + " Entrance Way Hill Repeats\n", + " 0.02\n", + " 0.10\n", + " 76\n", + " 5.00\n", + " 1158.0\n", + " 760.0\n", + " 14.39\n", + " 0.16\n", + " 23.0\n", + " \n", + " \n", + " \n", + " Stirrup Wall\n", + " 0.06\n", + " 0.17\n", + " 125\n", + " 2.83\n", + " 635.0\n", + " 735.0\n", + " 13.93\n", + " 0.27\n", + " 38.0\n", + " \n", + " \n", + " \n", + " Stirrup Wall\n", + " 0.08\n", + " 0.17\n", + " 125\n", + " 2.12\n", + " 476.0\n", + " 735.0\n", + " 13.93\n", + " 0.27\n", + " 38.0\n", + " \n", + " \n", + " \n", + " Westridge 3min\n", + " 0.08\n", + " 0.37\n", + " 240\n", + " 4.62\n", + " 914.0\n", + " 649.0\n", + " 12.29\n", + " 0.60\n", + " 73.0\n", + " \n", + " \n", + " \n", + " Westridge 3min\n", + " 0.09\n", + " 0.37\n", + " 240\n", + " 4.11\n", + " 813.0\n", + " 649.0\n", + " 12.29\n", + " 0.60\n", + " 73.0\n", + " \n", + " \n", + " \n", + " Limantour Spit\n", + " 0.09\n", + " 0.47\n", + " 303\n", + " 5.22\n", + " 1026.0\n", + " 645.0\n", + " 12.21\n", + " 0.76\n", + " 92.0\n", + " \n", + " \n", + " \n", + " Klamath Dr.\n", + " 0.02\n", + " 0.12\n", + " 77\n", + " 6.00\n", + " 1173.0\n", + " 642.0\n", + " 12.15\n", + " 0.19\n", + " 23.0\n", + " \n", + " \n", + " \n", + " green valley kicker\n", + " 0.08\n", + " 0.29\n", + " 178\n", + " 3.62\n", + " 678.0\n", + " 614.0\n", + " 11.62\n", + " 0.47\n", + " 54.0\n", + " \n", + " \n", + " \n", + " Redwood Gulch wall\n", + " 0.11\n", + " 0.43\n", + " 258\n", + " 3.91\n", + " 715.0\n", + " 600.0\n", + " 11.36\n", + " 0.69\n", + " 79.0\n", + " \n", + " \n", + " \n", + " Paloma Climb\n", + " 0.02\n", + " 0.14\n", + " 82\n", + " 7.00\n", + " 1250.0\n", + " 586.0\n", + " 11.09\n", + " 0.23\n", + " 25.0\n", + " \n", + " \n", + " \n", + " Try not to fall back\n", + " 0.21\n", + " 0.71\n", + " 410\n", + " 3.38\n", + " 595.0\n", + " 577.0\n", + " 10.94\n", + " 1.14\n", + " 125.0\n", + " \n", + " \n", + " \n", " Westridge\n", " 0.14\n", " 0.68\n", @@ -1779,36 +5863,10 @@ " 566.0\n", " 10.72\n", " 1.09\n", - " 0.1\n", + " 117.0\n", " \n", " \n", - " 67\n", - " Old La Honda (Bridge to Stop)\n", - " 0.48\n", - " 3.33\n", - " 1255\n", - " 6.94\n", - " 797.0\n", - " 377.0\n", - " 7.14\n", - " 5.36\n", - " 0.4\n", - " \n", - " \n", - " 68\n", - " Old La Honda (Bridge to Stop)\n", - " 0.51\n", - " 3.33\n", - " 1255\n", - " 6.53\n", - " 750.0\n", - " 377.0\n", - " 7.14\n", - " 5.36\n", - " 0.4\n", - " \n", - " \n", - " 130\n", + " \n", " Westridge\n", " 0.16\n", " 0.68\n", @@ -1818,251 +5876,69 @@ " 566.0\n", " 10.72\n", " 1.09\n", - " 0.1\n", + " 117.0\n", " \n", " \n", - " 119\n", - " Tunitas steep\n", - " 0.25\n", - " 1.20\n", - " 599\n", - " 4.80\n", - " 730.0\n", - " 499.0\n", - " 9.45\n", - " 1.93\n", - " 0.2\n", - " \n", - " \n", - " 70\n", - " Old La Honda Mile 1\n", - " 0.16\n", - " 0.99\n", - " 370\n", - " 6.19\n", - " 705.0\n", - " 374.0\n", - " 7.08\n", - " 1.59\n", - " 0.1\n", - " \n", - " \n", - " 139\n", - " Woodside Climb\n", - " 0.13\n", - " 1.71\n", - " 295\n", - " 13.15\n", - " 692.0\n", - " 173.0\n", - " 3.27\n", - " 2.75\n", - " 0.1\n", - " \n", - " \n", - " 40\n", - " Huddart\n", - " 0.17\n", - " 0.92\n", - " 385\n", - " 5.41\n", - " 690.0\n", - " 418.0\n", - " 7.93\n", - " 1.48\n", - " 0.1\n", - " \n", - " \n", - " 111\n", - " Top of Groton Rd heading west\n", - " 0.13\n", - " 0.92\n", - " 291\n", - " 7.08\n", - " 682.0\n", - " 316.0\n", - " 5.99\n", - " 1.48\n", - " 0.1\n", - " \n", - " \n", - " 125\n", - " Watts (Sonoma)\n", - " 0.14\n", - " 1.20\n", - " 313\n", - " 8.57\n", - " 681.0\n", - " 261.0\n", - " 4.94\n", - " 1.93\n", - " 0.1\n", - " \n", - " \n", - " 120\n", - " Tunitas steep\n", - " 0.27\n", - " 1.20\n", - " 599\n", - " 4.44\n", - " 676.0\n", - " 499.0\n", - " 9.45\n", - " 1.93\n", - " 0.2\n", - " \n", - " \n", - " 19\n", - " Canon to No Cycling\n", + " \n", + " Stair Step\n", " 0.09\n", - " 0.75\n", - " 198\n", - " 8.33\n", - " 671.0\n", - " 264.0\n", - " 5.00\n", - " 1.21\n", - " 0.1\n", - " \n", - " \n", - " 20\n", - " Canon to No Cycling\n", - " 0.09\n", - " 0.75\n", - " 198\n", - " 8.33\n", - " 671.0\n", - " 264.0\n", - " 5.00\n", - " 1.21\n", - " 0.1\n", - " \n", - " \n", - " 23\n", - " Coe Second Switchback to flat\n", - " 0.22\n", - " 1.00\n", - " 483\n", - " 4.55\n", - " 669.0\n", - " 483.0\n", - " 9.15\n", - " 1.61\n", - " 0.1\n", - " \n", - " \n", - " 60\n", - " Lower Redwood Gulch\n", - " 0.22\n", - " 1.03\n", - " 474\n", - " 4.68\n", - " 657.0\n", - " 460.0\n", - " 8.72\n", - " 1.66\n", - " 0.1\n", - " \n", - " \n", - " 59\n", - " Lobitas Creek\n", - " 0.20\n", - " 0.96\n", - " 430\n", - " 4.80\n", - " 655.0\n", - " 448.0\n", - " 8.48\n", - " 1.54\n", - " 0.1\n", - " \n", - " \n", - " 126\n", - " West Alpine switchback\n", - " 0.15\n", - " 0.78\n", - " 322\n", - " 5.20\n", - " 654.0\n", - " 413.0\n", - " 7.82\n", - " 1.26\n", - " 0.1\n", - " \n", - " \n", - " 45\n", - " Kaboom Portola Rd\n", - " 0.05\n", - " 0.67\n", - " 102\n", - " 13.40\n", - " 622.0\n", - " 152.0\n", - " 2.88\n", - " 1.08\n", - " 0.0\n", - " \n", - " \n", - " 41\n", - " Huddart\n", - " 0.19\n", - " 0.92\n", - " 385\n", - " 4.84\n", - " 618.0\n", - " 418.0\n", - " 7.93\n", - " 1.48\n", - " 0.1\n", + " 0.32\n", + " 175\n", + " 3.56\n", + " 593.0\n", + " 547.0\n", + " 10.36\n", + " 0.51\n", + " 53.0\n", " \n", " \n", "\n", "" ], "text/plain": [ - " title hours miles feet mph vam fpm \\\n", - "69 Old La Honda Mile 1 0.13 0.99 370 7.62 868.0 374.0 \n", - "129 Westridge 0.14 0.68 385 4.86 838.0 566.0 \n", - "67 Old La Honda (Bridge to Stop) 0.48 3.33 1255 6.94 797.0 377.0 \n", - "68 Old La Honda (Bridge to Stop) 0.51 3.33 1255 6.53 750.0 377.0 \n", - "130 Westridge 0.16 0.68 385 4.25 733.0 566.0 \n", - "119 Tunitas steep 0.25 1.20 599 4.80 730.0 499.0 \n", - "70 Old La Honda Mile 1 0.16 0.99 370 6.19 705.0 374.0 \n", - "139 Woodside Climb 0.13 1.71 295 13.15 692.0 173.0 \n", - "40 Huddart 0.17 0.92 385 5.41 690.0 418.0 \n", - "111 Top of Groton Rd heading west 0.13 0.92 291 7.08 682.0 316.0 \n", - "125 Watts (Sonoma) 0.14 1.20 313 8.57 681.0 261.0 \n", - "120 Tunitas steep 0.27 1.20 599 4.44 676.0 499.0 \n", - "19 Canon to No Cycling 0.09 0.75 198 8.33 671.0 264.0 \n", - "20 Canon to No Cycling 0.09 0.75 198 8.33 671.0 264.0 \n", - "23 Coe Second Switchback to flat 0.22 1.00 483 4.55 669.0 483.0 \n", - "60 Lower Redwood Gulch 0.22 1.03 474 4.68 657.0 460.0 \n", - "59 Lobitas Creek 0.20 0.96 430 4.80 655.0 448.0 \n", - "126 West Alpine switchback 0.15 0.78 322 5.20 654.0 413.0 \n", - "45 Kaboom Portola Rd 0.05 0.67 102 13.40 622.0 152.0 \n", - "41 Huddart 0.19 0.92 385 4.84 618.0 418.0 \n", + " title hours miles feet mph vam fpmi pct \\\n", + " Redwood Gulch hits 0.06 0.18 151 3.00 767.0 839.0 15.89 \n", + " Valparaiso steep 0.04 0.18 145 4.50 1105.0 806.0 15.26 \n", + " Valparaiso steep 0.05 0.18 145 3.60 884.0 806.0 15.26 \n", + " Limantour steepest 0.09 0.20 159 2.22 538.0 795.0 15.06 \n", + " Joaquin 0.10 0.33 254 3.30 774.0 770.0 14.58 \n", + " Joaquin 0.09 0.33 254 3.67 860.0 770.0 14.58 \n", + " Entrance Way Hill Repeats 0.02 0.10 76 5.00 1158.0 760.0 14.39 \n", + " Stirrup Wall 0.06 0.17 125 2.83 635.0 735.0 13.93 \n", + " Stirrup Wall 0.08 0.17 125 2.12 476.0 735.0 13.93 \n", + " Westridge 3min 0.08 0.37 240 4.62 914.0 649.0 12.29 \n", + " Westridge 3min 0.09 0.37 240 4.11 813.0 649.0 12.29 \n", + " Limantour Spit 0.09 0.47 303 5.22 1026.0 645.0 12.21 \n", + " Klamath Dr. 0.02 0.12 77 6.00 1173.0 642.0 12.15 \n", + " green valley kicker 0.08 0.29 178 3.62 678.0 614.0 11.62 \n", + " Redwood Gulch wall 0.11 0.43 258 3.91 715.0 600.0 11.36 \n", + " Paloma Climb 0.02 0.14 82 7.00 1250.0 586.0 11.09 \n", + " Try not to fall back 0.21 0.71 410 3.38 595.0 577.0 10.94 \n", + " Westridge 0.14 0.68 385 4.86 838.0 566.0 10.72 \n", + " Westridge 0.16 0.68 385 4.25 733.0 566.0 10.72 \n", + " Stair Step 0.09 0.32 175 3.56 593.0 547.0 10.36 \n", "\n", - " pct kms km_up \n", - "69 7.08 1.59 0.1 \n", - "129 10.72 1.09 0.1 \n", - "67 7.14 5.36 0.4 \n", - "68 7.14 5.36 0.4 \n", - "130 10.72 1.09 0.1 \n", - "119 9.45 1.93 0.2 \n", - "70 7.08 1.59 0.1 \n", - "139 3.27 2.75 0.1 \n", - "40 7.93 1.48 0.1 \n", - "111 5.99 1.48 0.1 \n", - "125 4.94 1.93 0.1 \n", - "120 9.45 1.93 0.2 \n", - "19 5.00 1.21 0.1 \n", - "20 5.00 1.21 0.1 \n", - "23 9.15 1.61 0.1 \n", - "60 8.72 1.66 0.1 \n", - "59 8.48 1.54 0.1 \n", - "126 7.82 1.26 0.1 \n", - "45 2.88 1.08 0.0 \n", - "41 7.93 1.48 0.1 " + " kms meters \n", + " 0.29 46.0 \n", + " 0.29 44.0 \n", + " 0.29 44.0 \n", + " 0.32 48.0 \n", + " 0.53 77.0 \n", + " 0.53 77.0 \n", + " 0.16 23.0 \n", + " 0.27 38.0 \n", + " 0.27 38.0 \n", + " 0.60 73.0 \n", + " 0.60 73.0 \n", + " 0.76 92.0 \n", + " 0.19 23.0 \n", + " 0.47 54.0 \n", + " 0.69 79.0 \n", + " 0.23 25.0 \n", + " 1.14 125.0 \n", + " 1.09 117.0 \n", + " 1.09 117.0 \n", + " 0.51 53.0 " ] }, "execution_count": 104, @@ -2071,14 +5947,7 @@ } ], "source": [ - "top(segments[segments.kms >= 1], 'vam')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "I can also look at VAM numbers for complete rides. I would expect the ride VAM to be half the segment VAM (or less) since most of my rides are circuits where I return to the start, and thus no more than half the ride is climbing. Sure enough, the best I can do is about 400 meters/hour:" + "top(segments, 'pct') # Steepest climbs" ] }, { @@ -2115,2247 +5984,16 @@ " feet\n", " mph\n", " vam\n", - " fpm\n", + " fpmi\n", " pct\n", " kms\n", - " km_up\n", + " meters\n", " \n", " \n", " \n", " \n", " \n", - " Sun, 11/29\n", - " 2015\n", - " Mt. Hamilton\n", - " 3.68\n", - " 37.00\n", - " 4902\n", - " 10.05\n", - " 406.0\n", - " 132.0\n", - " 2.51\n", - " 59.53\n", - " 1.5\n", - " \n", - " \n", - " \n", - " Fri, 4/2\n", - " 2021\n", - " Everesting 5: climb 2×(OLH + WOLH)\n", - " 3.27\n", - " 31.48\n", - " 4344\n", - " 9.63\n", - " 405.0\n", - " 138.0\n", - " 2.61\n", - " 50.65\n", - " 1.3\n", - " \n", - " \n", - " \n", - " Tue, 3/30\n", - " 2021\n", - " Everesting 2: Kings + WOLH + OLH\n", - " 3.34\n", - " 35.99\n", - " 4377\n", - " 10.78\n", - " 399.0\n", - " 122.0\n", - " 2.30\n", - " 57.91\n", - " 1.3\n", - " \n", - " \n", - " \n", - " Mon, 3/29\n", - " 2021\n", - " Everesting 1: Mt Diablo\n", - " 2.60\n", - " 22.22\n", - " 3406\n", - " 8.55\n", - " 399.0\n", - " 153.0\n", - " 2.90\n", - " 35.75\n", - " 1.0\n", - " \n", - " \n", - " \n", - " Sat, 11/25\n", - " 2017\n", - " Mt. Hamilton\n", - " 3.69\n", - " 36.65\n", - " 4806\n", - " 9.93\n", - " 397.0\n", - " 131.0\n", - " 2.48\n", - " 58.97\n", - " 1.5\n", - " \n", - " \n", - " \n", - " Sun, 12/1\n", - " 2013\n", - " Mt. Hamilton\n", - " 3.78\n", - " 37.56\n", - " 4921\n", - " 9.94\n", - " 397.0\n", - " 131.0\n", - " 2.48\n", - " 60.43\n", - " 1.5\n", - " \n", - " \n", - " \n", - " Fri, 10/30\n", - " 2015\n", - " OLH / West Alpine\n", - " 3.48\n", - " 39.51\n", - " 4505\n", - " 11.35\n", - " 395.0\n", - " 114.0\n", - " 2.16\n", - " 63.57\n", - " 1.4\n", - " \n", - " \n", - " \n", - " Sat, 4/26\n", - " 2014\n", - " OLH / Tunitas Creek\n", - " 5.26\n", - " 58.69\n", - " 6742\n", - " 11.16\n", - " 391.0\n", - " 115.0\n", - " 2.18\n", - " 94.43\n", - " 2.1\n", - " \n", - " \n", - " \n", - " Sat, 4/18\n", - " 2015\n", - " Tunitas + Lobitos Creeks\n", - " 5.24\n", - " 61.27\n", - " 6611\n", - " 11.69\n", - " 385.0\n", - " 108.0\n", - " 2.04\n", - " 98.58\n", - " 2.0\n", - " \n", - " \n", - " \n", - " Wed, 10/14\n", - " 2015\n", - " Half Moon Bay\n", - " 6.13\n", - " 72.97\n", - " 7644\n", - " 11.90\n", - " 380.0\n", - " 105.0\n", - " 1.98\n", - " 117.41\n", - " 2.3\n", - " \n", - " \n", - " \n", - " Sat, 7/25\n", - " 2015\n", - " Palo Alto, California\n", - " 4.04\n", - " 43.62\n", - " 4819\n", - " 10.80\n", - " 364.0\n", - " 110.0\n", - " 2.09\n", - " 70.18\n", - " 1.5\n", - " \n", - " \n", - " \n", - " Sun, 6/4\n", - " 2017\n", - " Sequoia Challenge\n", - " 6.29\n", - " 66.52\n", - " 7520\n", - " 10.58\n", - " 364.0\n", - " 113.0\n", - " 2.14\n", - " 107.03\n", - " 2.3\n", - " \n", - " \n", - " \n", - " Sat, 10/11\n", - " 2014\n", - " OLH / Tunitas\n", - " 5.09\n", - " 58.29\n", - " 6044\n", - " 11.45\n", - " 362.0\n", - " 104.0\n", - " 1.96\n", - " 93.79\n", - " 1.8\n", - " \n", - " \n", - " \n", - " Sat, 8/13\n", - " 2016\n", - " Petaluma / Point Reyes\n", - " 4.50\n", - " 54.75\n", - " 5286\n", - " 12.17\n", - " 358.0\n", - " 97.0\n", - " 1.83\n", - " 88.09\n", - " 1.6\n", - " \n", - " \n", - " \n", - " Fri, 8/28\n", - " 2015\n", - " Pescadaro via OLH\n", - " 5.31\n", - " 66.01\n", - " 6137\n", - " 12.43\n", - " 352.0\n", - " 93.0\n", - " 1.76\n", - " 106.21\n", - " 1.9\n", - " \n", - " \n", - " \n", - " Sun, 4/4\n", - " 2021\n", - " Everesting 7: Mill Creek / Morrison Canyon\n", - " 3.08\n", - " 29.38\n", - " 3517\n", - " 9.54\n", - " 348.0\n", - " 120.0\n", - " 2.27\n", - " 47.27\n", - " 1.1\n", - " \n", - " \n", - " \n", - " Wed, 6/18\n", - " 2014\n", - " Sierra to the Sea Day 4\n", - " 4.96\n", - " 57.64\n", - " 5561\n", - " 11.62\n", - " 342.0\n", - " 96.0\n", - " 1.83\n", - " 92.74\n", - " 1.7\n", - " \n", - " \n", - " \n", - " Sun, 6/3\n", - " 2018\n", - " The Sequoia\n", - " 5.97\n", - " 64.92\n", - " 6677\n", - " 10.87\n", - " 341.0\n", - " 103.0\n", - " 1.95\n", - " 104.46\n", - " 2.0\n", - " \n", - " \n", - " \n", - " Sat, 5/9\n", - " 2015\n", - " OLH\n", - " 2.50\n", - " 32.33\n", - " 2788\n", - " 12.93\n", - " 340.0\n", - " 86.0\n", - " 1.63\n", - " 52.02\n", - " 0.8\n", - " \n", - " \n", - " \n", - " Sat, 9/12\n", - " 2015\n", - " Morning Ride\n", - " 3.10\n", - " 38.20\n", - " 3342\n", - " 12.32\n", - " 329.0\n", - " 87.0\n", - " 1.66\n", - " 61.46\n", - " 1.0\n", - " \n", - " \n", - "\n", - "" - ], - "text/plain": [ - " date year title hours miles \\\n", - " Sun, 11/29 2015 Mt. Hamilton 3.68 37.00 \n", - " Fri, 4/2 2021 Everesting 5: climb 2×(OLH + WOLH) 3.27 31.48 \n", - " Tue, 3/30 2021 Everesting 2: Kings + WOLH + OLH 3.34 35.99 \n", - " Mon, 3/29 2021 Everesting 1: Mt Diablo 2.60 22.22 \n", - " Sat, 11/25 2017 Mt. Hamilton 3.69 36.65 \n", - " Sun, 12/1 2013 Mt. Hamilton 3.78 37.56 \n", - " Fri, 10/30 2015 OLH / West Alpine 3.48 39.51 \n", - " Sat, 4/26 2014 OLH / Tunitas Creek 5.26 58.69 \n", - " Sat, 4/18 2015 Tunitas + Lobitos Creeks 5.24 61.27 \n", - " Wed, 10/14 2015 Half Moon Bay 6.13 72.97 \n", - " Sat, 7/25 2015 Palo Alto, California 4.04 43.62 \n", - " Sun, 6/4 2017 Sequoia Challenge 6.29 66.52 \n", - " Sat, 10/11 2014 OLH / Tunitas 5.09 58.29 \n", - " Sat, 8/13 2016 Petaluma / Point Reyes 4.50 54.75 \n", - " Fri, 8/28 2015 Pescadaro via OLH 5.31 66.01 \n", - " Sun, 4/4 2021 Everesting 7: Mill Creek / Morrison Canyon 3.08 29.38 \n", - " Wed, 6/18 2014 Sierra to the Sea Day 4 4.96 57.64 \n", - " Sun, 6/3 2018 The Sequoia 5.97 64.92 \n", - " Sat, 5/9 2015 OLH 2.50 32.33 \n", - " Sat, 9/12 2015 Morning Ride 3.10 38.20 \n", - "\n", - " feet mph vam fpm pct kms km_up \n", - " 4902 10.05 406.0 132.0 2.51 59.53 1.5 \n", - " 4344 9.63 405.0 138.0 2.61 50.65 1.3 \n", - " 4377 10.78 399.0 122.0 2.30 57.91 1.3 \n", - " 3406 8.55 399.0 153.0 2.90 35.75 1.0 \n", - " 4806 9.93 397.0 131.0 2.48 58.97 1.5 \n", - " 4921 9.94 397.0 131.0 2.48 60.43 1.5 \n", - " 4505 11.35 395.0 114.0 2.16 63.57 1.4 \n", - " 6742 11.16 391.0 115.0 2.18 94.43 2.1 \n", - " 6611 11.69 385.0 108.0 2.04 98.58 2.0 \n", - " 7644 11.90 380.0 105.0 1.98 117.41 2.3 \n", - " 4819 10.80 364.0 110.0 2.09 70.18 1.5 \n", - " 7520 10.58 364.0 113.0 2.14 107.03 2.3 \n", - " 6044 11.45 362.0 104.0 1.96 93.79 1.8 \n", - " 5286 12.17 358.0 97.0 1.83 88.09 1.6 \n", - " 6137 12.43 352.0 93.0 1.76 106.21 1.9 \n", - " 3517 9.54 348.0 120.0 2.27 47.27 1.1 \n", - " 5561 11.62 342.0 96.0 1.83 92.74 1.7 \n", - " 6677 10.87 341.0 103.0 1.95 104.46 2.0 \n", - " 2788 12.93 340.0 86.0 1.63 52.02 0.8 \n", - " 3342 12.32 329.0 87.0 1.66 61.46 1.0 " - ] - }, - "execution_count": 105, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "top(rides, 'vam')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Exploring the Data\n", - "\n", - "\n", - "Some more ways to look at the data, both rides and segments." - ] - }, - { - "cell_type": "code", - "execution_count": 106, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
yearhoursmilesfeetmphvamfpmpctkmskm_up
count533.000000533.000000533.000000533.000000533.000000533.000000533.000000533.000000533.000000533.000000
mean2016.8818013.33195142.5451221798.01688612.976660157.17260841.4090060.78409068.4549720.549719
std2.4254111.44002517.2050431476.6081081.31421589.83043927.2339090.51573127.6828740.451922
min2012.0000001.54000020.96000068.0000008.55000010.0000003.0000000.05000033.7200000.000000
25%2015.0000002.21000028.870000725.00000012.16000080.00000019.0000000.37000046.4500000.200000
50%2017.0000002.85000036.6500001355.00000013.120000152.00000036.0000000.69000058.9700000.400000
75%2018.0000004.22000054.6500002300.00000013.770000218.00000056.0000001.07000087.9300000.700000
max2023.0000008.140000102.4100007644.00000016.750000406.000000153.0000002.900000164.7800002.300000
\n", - "
" - ], - "text/plain": [ - " year hours miles feet mph \\\n", - "count 533.000000 533.000000 533.000000 533.000000 533.000000 \n", - "mean 2016.881801 3.331951 42.545122 1798.016886 12.976660 \n", - "std 2.425411 1.440025 17.205043 1476.608108 1.314215 \n", - "min 2012.000000 1.540000 20.960000 68.000000 8.550000 \n", - "25% 2015.000000 2.210000 28.870000 725.000000 12.160000 \n", - "50% 2017.000000 2.850000 36.650000 1355.000000 13.120000 \n", - "75% 2018.000000 4.220000 54.650000 2300.000000 13.770000 \n", - "max 2023.000000 8.140000 102.410000 7644.000000 16.750000 \n", - "\n", - " vam fpm pct kms km_up \n", - "count 533.000000 533.000000 533.000000 533.000000 533.000000 \n", - "mean 157.172608 41.409006 0.784090 68.454972 0.549719 \n", - "std 89.830439 27.233909 0.515731 27.682874 0.451922 \n", - "min 10.000000 3.000000 0.050000 33.720000 0.000000 \n", - "25% 80.000000 19.000000 0.370000 46.450000 0.200000 \n", - "50% 152.000000 36.000000 0.690000 58.970000 0.400000 \n", - "75% 218.000000 56.000000 1.070000 87.930000 0.700000 \n", - "max 406.000000 153.000000 2.900000 164.780000 2.300000 " - ] - }, - "execution_count": 106, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "rides.describe() # Summary statistics for the rides" - ] - }, - { - "cell_type": "code", - "execution_count": 107, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
hoursmilesfeetmphvamfpmpctkmskm_up
count141.000000141.000000141.000000141.000000141.000000141.000000141.000000141.000000141.000000
mean0.1417020.932979268.9787237.444539641.893617353.5035466.6948941.5010640.073050
std0.1737110.989832295.0606593.557336211.923595186.8163203.5379741.5922670.101334
min0.0100000.08000021.0000002.120000111.00000018.0000000.3500000.1300000.000000
25%0.0500000.330000104.0000004.830000503.000000219.0000004.1400000.5300000.000000
50%0.0900000.600000166.0000006.190000630.000000333.0000006.3000000.9700000.100000
75%0.1500001.190000303.0000009.800000724.000000462.0000008.7600001.9100000.100000
max1.3900007.3800001887.00000019.7900001463.000000839.00000015.89000011.8700000.600000
\n", - "
" - ], - "text/plain": [ - " hours miles feet mph vam \\\n", - "count 141.000000 141.000000 141.000000 141.000000 141.000000 \n", - "mean 0.141702 0.932979 268.978723 7.444539 641.893617 \n", - "std 0.173711 0.989832 295.060659 3.557336 211.923595 \n", - "min 0.010000 0.080000 21.000000 2.120000 111.000000 \n", - "25% 0.050000 0.330000 104.000000 4.830000 503.000000 \n", - "50% 0.090000 0.600000 166.000000 6.190000 630.000000 \n", - "75% 0.150000 1.190000 303.000000 9.800000 724.000000 \n", - "max 1.390000 7.380000 1887.000000 19.790000 1463.000000 \n", - "\n", - " fpm pct kms km_up \n", - "count 141.000000 141.000000 141.000000 141.000000 \n", - "mean 353.503546 6.694894 1.501064 0.073050 \n", - "std 186.816320 3.537974 1.592267 0.101334 \n", - "min 18.000000 0.350000 0.130000 0.000000 \n", - "25% 219.000000 4.140000 0.530000 0.000000 \n", - "50% 333.000000 6.300000 0.970000 0.100000 \n", - "75% 462.000000 8.760000 1.910000 0.100000 \n", - "max 839.000000 15.890000 11.870000 0.600000 " - ] - }, - "execution_count": 107, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "segments.describe() # Summary statistics for the segments" - ] - }, - { - "cell_type": "code", - "execution_count": 108, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
dateyeartitlehoursmilesfeetmphvamfpmpctkmskm_up
Sun, 5/222016Canada2.1936.68133216.75185.036.00.6959.020.4
Wed, 9/132017Healdburg / Jimtown2.1334.4591216.17131.026.00.5055.430.3
Sat, 1/252014Woodside1.5625.08124316.08243.050.00.9440.350.4
Sat, 4/112015Woodside1.5424.73103516.06205.042.00.7939.790.3
Sun, 7/112021San Jose4.1065.10108615.8881.017.00.32104.750.3
Sun, 1/182015Woodside1.6426.02125715.87234.048.00.9141.870.4
Fri, 6/242016Foothill Expway1.5925.1162315.79119.025.00.4740.400.2
Sun, 1/262014Canada Rd2.1033.12144615.77210.044.00.8353.290.4
Fri, 1/62012Omarama to Wanaka New Zealand4.4870.35326215.70222.046.00.88113.191.0
Sun, 4/122015Palo Alto Cycling2.0331.76121015.65182.038.00.7251.100.4
Sun, 10/152017Los Gatos2.8644.71143715.63153.032.00.6171.940.4
Sun, 8/52018Bike Ride Northwest Day 13.5855.77182415.58155.033.00.6289.730.6
Sun, 2/282016Woodside Loop1.7326.9384315.57149.031.00.5943.330.3
Sun, 6/262016Los Gatos3.2850.78118115.48110.023.00.4481.710.4
Mon, 1/192015Canada Rd, etc.2.9545.64183615.47190.040.00.7673.430.6
Sun, 1/192014Palo Alto, CA1.6225.01120115.44226.048.00.9140.240.4
Sun, 12/62015Canada Rd2.2534.67123715.41168.036.00.6855.780.4
Tue, 6/182013work etc (headwinds)2.0631.4880915.28120.026.00.4950.650.2
Fri, 9/232016Los Gatos2.8943.93133915.20141.030.00.5870.680.4
Sat, 7/92016Santa Cruz3.8458.23404215.16321.069.01.3193.691.2
\n", - "
" - ], - "text/plain": [ - " date year title hours miles feet mph \\\n", - " Sun, 5/22 2016 Canada 2.19 36.68 1332 16.75 \n", - " Wed, 9/13 2017 Healdburg / Jimtown 2.13 34.45 912 16.17 \n", - " Sat, 1/25 2014 Woodside 1.56 25.08 1243 16.08 \n", - " Sat, 4/11 2015 Woodside 1.54 24.73 1035 16.06 \n", - " Sun, 7/11 2021 San Jose 4.10 65.10 1086 15.88 \n", - " Sun, 1/18 2015 Woodside 1.64 26.02 1257 15.87 \n", - " Fri, 6/24 2016 Foothill Expway 1.59 25.11 623 15.79 \n", - " Sun, 1/26 2014 Canada Rd 2.10 33.12 1446 15.77 \n", - " Fri, 1/6 2012 Omarama to Wanaka New Zealand 4.48 70.35 3262 15.70 \n", - " Sun, 4/12 2015 Palo Alto Cycling 2.03 31.76 1210 15.65 \n", - " Sun, 10/15 2017 Los Gatos 2.86 44.71 1437 15.63 \n", - " Sun, 8/5 2018 Bike Ride Northwest Day 1 3.58 55.77 1824 15.58 \n", - " Sun, 2/28 2016 Woodside Loop 1.73 26.93 843 15.57 \n", - " Sun, 6/26 2016 Los Gatos 3.28 50.78 1181 15.48 \n", - " Mon, 1/19 2015 Canada Rd, etc. 2.95 45.64 1836 15.47 \n", - " Sun, 1/19 2014 Palo Alto, CA 1.62 25.01 1201 15.44 \n", - " Sun, 12/6 2015 Canada Rd 2.25 34.67 1237 15.41 \n", - " Tue, 6/18 2013 work etc (headwinds) 2.06 31.48 809 15.28 \n", - " Fri, 9/23 2016 Los Gatos 2.89 43.93 1339 15.20 \n", - " Sat, 7/9 2016 Santa Cruz 3.84 58.23 4042 15.16 \n", - "\n", - " vam fpm pct kms km_up \n", - " 185.0 36.0 0.69 59.02 0.4 \n", - " 131.0 26.0 0.50 55.43 0.3 \n", - " 243.0 50.0 0.94 40.35 0.4 \n", - " 205.0 42.0 0.79 39.79 0.3 \n", - " 81.0 17.0 0.32 104.75 0.3 \n", - " 234.0 48.0 0.91 41.87 0.4 \n", - " 119.0 25.0 0.47 40.40 0.2 \n", - " 210.0 44.0 0.83 53.29 0.4 \n", - " 222.0 46.0 0.88 113.19 1.0 \n", - " 182.0 38.0 0.72 51.10 0.4 \n", - " 153.0 32.0 0.61 71.94 0.4 \n", - " 155.0 33.0 0.62 89.73 0.6 \n", - " 149.0 31.0 0.59 43.33 0.3 \n", - " 110.0 23.0 0.44 81.71 0.4 \n", - " 190.0 40.0 0.76 73.43 0.6 \n", - " 226.0 48.0 0.91 40.24 0.4 \n", - " 168.0 36.0 0.68 55.78 0.4 \n", - " 120.0 26.0 0.49 50.65 0.2 \n", - " 141.0 30.0 0.58 70.68 0.4 \n", - " 321.0 69.0 1.31 93.69 1.2 " - ] - }, - "execution_count": 108, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "top(rides, 'mph') # Fastest rides (of more than 20 miles)" - ] - }, - { - "cell_type": "code", - "execution_count": 109, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
titlehoursmilesfeetmphvamfpmpctkmskm_up
76PCH Pescadero to Bean Hollow0.142.775119.79111.018.00.354.460.0
36Highway 1 Cascanoa to Cascade0.091.618917.89301.055.01.052.590.0
123Vickrey Fruitvale0.060.996816.50345.069.01.301.590.0
39Highway 9 Mantalvo0.030.453515.00356.078.01.470.720.0
38Highway 9 Mantalvo0.030.453515.00356.078.01.470.720.0
109The Boneyard0.101.4813514.80411.091.01.732.380.0
124Vickrey Fruitvale0.070.996814.14296.069.01.301.590.0
90Sand Hill Alpine to 2800.121.6718013.92457.0108.02.042.690.1
15Canada to College0.101.3711913.70363.087.01.652.200.0
30Foothill Homestead0.091.2212613.56427.0103.01.961.960.0
110The Boneyard0.111.4813513.45374.091.01.732.380.0
45Kaboom Portola Rd0.050.6710213.40622.0152.02.881.080.0
139Woodside Climb0.131.7129513.15692.0173.03.272.750.1
91Sand Hill Alpine to 2800.131.6718012.85422.0108.02.042.690.1
1Alpine Westridge0.060.769912.67503.0130.02.471.220.0
2Alpine Westridge0.060.769912.67503.0130.02.471.220.0
98Stanford Ave0.050.638512.60518.0135.02.561.010.0
16Canada to College0.111.3711912.45330.087.01.652.200.0
88Sand Hill 280 to horse0.040.499512.25724.0194.03.670.790.0
100Stevens Country Park0.101.2211212.20341.092.01.741.960.0
\n", - "
" - ], - "text/plain": [ - " title hours miles feet mph vam fpm \\\n", - "76 PCH Pescadero to Bean Hollow 0.14 2.77 51 19.79 111.0 18.0 \n", - "36 Highway 1 Cascanoa to Cascade 0.09 1.61 89 17.89 301.0 55.0 \n", - "123 Vickrey Fruitvale 0.06 0.99 68 16.50 345.0 69.0 \n", - "39 Highway 9 Mantalvo 0.03 0.45 35 15.00 356.0 78.0 \n", - "38 Highway 9 Mantalvo 0.03 0.45 35 15.00 356.0 78.0 \n", - "109 The Boneyard 0.10 1.48 135 14.80 411.0 91.0 \n", - "124 Vickrey Fruitvale 0.07 0.99 68 14.14 296.0 69.0 \n", - "90 Sand Hill Alpine to 280 0.12 1.67 180 13.92 457.0 108.0 \n", - "15 Canada to College 0.10 1.37 119 13.70 363.0 87.0 \n", - "30 Foothill Homestead 0.09 1.22 126 13.56 427.0 103.0 \n", - "110 The Boneyard 0.11 1.48 135 13.45 374.0 91.0 \n", - "45 Kaboom Portola Rd 0.05 0.67 102 13.40 622.0 152.0 \n", - "139 Woodside Climb 0.13 1.71 295 13.15 692.0 173.0 \n", - "91 Sand Hill Alpine to 280 0.13 1.67 180 12.85 422.0 108.0 \n", - "1 Alpine Westridge 0.06 0.76 99 12.67 503.0 130.0 \n", - "2 Alpine Westridge 0.06 0.76 99 12.67 503.0 130.0 \n", - "98 Stanford Ave 0.05 0.63 85 12.60 518.0 135.0 \n", - "16 Canada to College 0.11 1.37 119 12.45 330.0 87.0 \n", - "88 Sand Hill 280 to horse 0.04 0.49 95 12.25 724.0 194.0 \n", - "100 Stevens Country Park 0.10 1.22 112 12.20 341.0 92.0 \n", - "\n", - " pct kms km_up \n", - "76 0.35 4.46 0.0 \n", - "36 1.05 2.59 0.0 \n", - "123 1.30 1.59 0.0 \n", - "39 1.47 0.72 0.0 \n", - "38 1.47 0.72 0.0 \n", - "109 1.73 2.38 0.0 \n", - "124 1.30 1.59 0.0 \n", - "90 2.04 2.69 0.1 \n", - "15 1.65 2.20 0.0 \n", - "30 1.96 1.96 0.0 \n", - "110 1.73 2.38 0.0 \n", - "45 2.88 1.08 0.0 \n", - "139 3.27 2.75 0.1 \n", - "91 2.04 2.69 0.1 \n", - "1 2.47 1.22 0.0 \n", - "2 2.47 1.22 0.0 \n", - "98 2.56 1.01 0.0 \n", - "16 1.65 2.20 0.0 \n", - "88 3.67 0.79 0.0 \n", - "100 1.74 1.96 0.0 " - ] - }, - "execution_count": 109, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "top(segments, 'mph') # Fastest segments (there are no descent segments in the database)" - ] - }, - { - "cell_type": "code", - "execution_count": 110, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
titlehoursmilesfeetmphvamfpmpctkmskm_up
128West Alpine full1.397.3818875.31414.0256.04.8411.870.6
51Kings Greer to Skyline0.783.9215365.03600.0392.07.426.310.5
52Kings Greer to Skyline0.813.9215364.84578.0392.07.426.310.5
67Old La Honda (Bridge to Stop)0.483.3312556.94797.0377.07.145.360.4
68Old La Honda (Bridge to Stop)0.513.3312556.53750.0377.07.145.360.4
0Alma Mountain Charlie0.533.128755.89503.0280.05.315.020.3
47Kings half way0.462.898206.28543.0284.05.374.650.2
48Kings half way0.502.898205.78500.0284.05.374.650.2
5Alpine Portola to top Joaquin0.573.528016.18428.0228.04.315.660.2
6Alpine Portola to top Joaquin0.583.528016.07421.0228.04.315.660.2
120Tunitas steep0.271.205994.44676.0499.09.451.930.2
119Tunitas steep0.251.205994.80730.0499.09.451.930.2
34Haskins0.301.515665.03575.0375.07.102.430.2
35Haskins0.311.515664.87557.0375.07.102.430.2
23Coe Second Switchback to flat0.221.004834.55669.0483.09.151.610.1
60Lower Redwood Gulch0.221.034744.68657.0460.08.721.660.1
8Alpine Willowbrook to Joaquin0.292.274617.83485.0203.03.853.650.1
7Alpine Willowbrook to Joaquin0.282.274618.11502.0203.03.853.650.1
59Lobitas Creek0.200.964304.80655.0448.08.481.540.1
117Tunitas lower climb0.221.304215.91583.0324.06.132.090.1
\n", - "
" - ], - "text/plain": [ - " title hours miles feet mph vam fpm \\\n", - "128 West Alpine full 1.39 7.38 1887 5.31 414.0 256.0 \n", - "51 Kings Greer to Skyline 0.78 3.92 1536 5.03 600.0 392.0 \n", - "52 Kings Greer to Skyline 0.81 3.92 1536 4.84 578.0 392.0 \n", - "67 Old La Honda (Bridge to Stop) 0.48 3.33 1255 6.94 797.0 377.0 \n", - "68 Old La Honda (Bridge to Stop) 0.51 3.33 1255 6.53 750.0 377.0 \n", - "0 Alma Mountain Charlie 0.53 3.12 875 5.89 503.0 280.0 \n", - "47 Kings half way 0.46 2.89 820 6.28 543.0 284.0 \n", - "48 Kings half way 0.50 2.89 820 5.78 500.0 284.0 \n", - "5 Alpine Portola to top Joaquin 0.57 3.52 801 6.18 428.0 228.0 \n", - "6 Alpine Portola to top Joaquin 0.58 3.52 801 6.07 421.0 228.0 \n", - "120 Tunitas steep 0.27 1.20 599 4.44 676.0 499.0 \n", - "119 Tunitas steep 0.25 1.20 599 4.80 730.0 499.0 \n", - "34 Haskins 0.30 1.51 566 5.03 575.0 375.0 \n", - "35 Haskins 0.31 1.51 566 4.87 557.0 375.0 \n", - "23 Coe Second Switchback to flat 0.22 1.00 483 4.55 669.0 483.0 \n", - "60 Lower Redwood Gulch 0.22 1.03 474 4.68 657.0 460.0 \n", - "8 Alpine Willowbrook to Joaquin 0.29 2.27 461 7.83 485.0 203.0 \n", - "7 Alpine Willowbrook to Joaquin 0.28 2.27 461 8.11 502.0 203.0 \n", - "59 Lobitas Creek 0.20 0.96 430 4.80 655.0 448.0 \n", - "117 Tunitas lower climb 0.22 1.30 421 5.91 583.0 324.0 \n", - "\n", - " pct kms km_up \n", - "128 4.84 11.87 0.6 \n", - "51 7.42 6.31 0.5 \n", - "52 7.42 6.31 0.5 \n", - "67 7.14 5.36 0.4 \n", - "68 7.14 5.36 0.4 \n", - "0 5.31 5.02 0.3 \n", - "47 5.37 4.65 0.2 \n", - "48 5.37 4.65 0.2 \n", - "5 4.31 5.66 0.2 \n", - "6 4.31 5.66 0.2 \n", - "120 9.45 1.93 0.2 \n", - "119 9.45 1.93 0.2 \n", - "34 7.10 2.43 0.2 \n", - "35 7.10 2.43 0.2 \n", - "23 9.15 1.61 0.1 \n", - "60 8.72 1.66 0.1 \n", - "8 3.85 3.65 0.1 \n", - "7 3.85 3.65 0.1 \n", - "59 8.48 1.54 0.1 \n", - "117 6.13 2.09 0.1 " - ] - }, - "execution_count": 110, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "top(segments, 'feet') # Biggest climbing segments" - ] - }, - { - "cell_type": "code", - "execution_count": 111, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
titlehoursmilesfeetmphvamfpmpctkmskm_up
84Redwood Gulch hits0.060.181513.00767.0839.015.890.290.0
121Valparaiso steep0.040.181454.501105.0806.015.260.290.0
122Valparaiso steep0.050.181453.60884.0806.015.260.290.0
58Limantour steepest0.090.201592.22538.0795.015.060.320.0
44Joaquin0.100.332543.30774.0770.014.580.530.1
43Joaquin0.090.332543.67860.0770.014.580.530.1
28Entrance Way Hill Repeats0.020.10765.001158.0760.014.390.160.0
102Stirrup Wall0.060.171252.83635.0735.013.930.270.0
103Stirrup Wall0.080.171252.12476.0735.013.930.270.0
131Westridge 3min0.080.372404.62914.0649.012.290.600.1
132Westridge 3min0.090.372404.11813.0649.012.290.600.1
57Limantour Spit0.090.473035.221026.0645.012.210.760.1
55Klamath Dr.0.020.12776.001173.0642.012.150.190.0
33green valley kicker0.080.291783.62678.0614.011.620.470.1
85Redwood Gulch wall0.110.432583.91715.0600.011.360.690.1
71Paloma Climb0.020.14827.001250.0586.011.090.230.0
112Try not to fall back0.210.714103.38595.0577.010.941.140.1
129Westridge0.140.683854.86838.0566.010.721.090.1
130Westridge0.160.683854.25733.0566.010.721.090.1
95Stair Step0.090.321753.56593.0547.010.360.510.1
\n", - "
" - ], - "text/plain": [ - " title hours miles feet mph vam fpm \\\n", - "84 Redwood Gulch hits 0.06 0.18 151 3.00 767.0 839.0 \n", - "121 Valparaiso steep 0.04 0.18 145 4.50 1105.0 806.0 \n", - "122 Valparaiso steep 0.05 0.18 145 3.60 884.0 806.0 \n", - "58 Limantour steepest 0.09 0.20 159 2.22 538.0 795.0 \n", - "44 Joaquin 0.10 0.33 254 3.30 774.0 770.0 \n", - "43 Joaquin 0.09 0.33 254 3.67 860.0 770.0 \n", - "28 Entrance Way Hill Repeats 0.02 0.10 76 5.00 1158.0 760.0 \n", - "102 Stirrup Wall 0.06 0.17 125 2.83 635.0 735.0 \n", - "103 Stirrup Wall 0.08 0.17 125 2.12 476.0 735.0 \n", - "131 Westridge 3min 0.08 0.37 240 4.62 914.0 649.0 \n", - "132 Westridge 3min 0.09 0.37 240 4.11 813.0 649.0 \n", - "57 Limantour Spit 0.09 0.47 303 5.22 1026.0 645.0 \n", - "55 Klamath Dr. 0.02 0.12 77 6.00 1173.0 642.0 \n", - "33 green valley kicker 0.08 0.29 178 3.62 678.0 614.0 \n", - "85 Redwood Gulch wall 0.11 0.43 258 3.91 715.0 600.0 \n", - "71 Paloma Climb 0.02 0.14 82 7.00 1250.0 586.0 \n", - "112 Try not to fall back 0.21 0.71 410 3.38 595.0 577.0 \n", - "129 Westridge 0.14 0.68 385 4.86 838.0 566.0 \n", - "130 Westridge 0.16 0.68 385 4.25 733.0 566.0 \n", - "95 Stair Step 0.09 0.32 175 3.56 593.0 547.0 \n", - "\n", - " pct kms km_up \n", - "84 15.89 0.29 0.0 \n", - "121 15.26 0.29 0.0 \n", - "122 15.26 0.29 0.0 \n", - "58 15.06 0.32 0.0 \n", - "44 14.58 0.53 0.1 \n", - "43 14.58 0.53 0.1 \n", - "28 14.39 0.16 0.0 \n", - "102 13.93 0.27 0.0 \n", - "103 13.93 0.27 0.0 \n", - "131 12.29 0.60 0.1 \n", - "132 12.29 0.60 0.1 \n", - "57 12.21 0.76 0.1 \n", - "55 12.15 0.19 0.0 \n", - "33 11.62 0.47 0.1 \n", - "85 11.36 0.69 0.1 \n", - "71 11.09 0.23 0.0 \n", - "112 10.94 1.14 0.1 \n", - "129 10.72 1.09 0.1 \n", - "130 10.72 1.09 0.1 \n", - "95 10.36 0.51 0.1 " - ] - }, - "execution_count": 111, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "top(segments, 'pct') # Steepest climbs" - ] - }, - { - "cell_type": "code", - "execution_count": 112, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -4366,11 +6004,11 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -4381,11 +6019,11 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -4396,11 +6034,11 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -4411,11 +6049,11 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -4426,11 +6064,11 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -4441,11 +6079,11 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -4456,11 +6094,11 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -4471,11 +6109,11 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -4486,11 +6124,11 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -4501,11 +6139,11 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -4516,11 +6154,11 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -4531,11 +6169,11 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -4546,11 +6184,11 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -4561,11 +6199,11 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -4576,11 +6214,11 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -4591,11 +6229,11 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -4606,11 +6244,11 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -4621,11 +6259,11 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -4636,11 +6274,11 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", " \n", @@ -4651,59 +6289,59 @@ " \n", " \n", " \n", - " \n", + " \n", " \n", " \n", "
dateyeartitlehoursmilesfeetmphvamfpmpctkmskm_up
Fri, 1/9Fri, 1/9/20122012Otago Rail Trail Century7.8722.00.42164.780.7697.0
Sat, 5/7Sat, 5/7/20222022Wine Country Century6.6552.00.99161.321.61601.0
Thu, 6/14Thu, 6/14/20122012Coyote Creek Century with Juliet8.1415.00.29161.010.5461.0
Sat, 5/13Sat, 5/13/20172017Morgan Hill iCare Classic7.4646.00.87160.981.41401.0
Sat, 5/12Sat, 5/12/20182018ICare Classic, Morgan Hill6.8046.00.86146.891.31268.0
Sat, 5/6Sat, 5/6/20172017Wine Country Century7.2659.01.11143.991.61599.0
Fri, 8/10Fri, 8/10/20182018Bike Ride Northwest Day 66.2452.00.98136.281.31335.0
Fri, 2/28Fri, 2/28/20202020Sawyer Camp Trail6.4141.00.77135.851.11051.0
Wed, 6/7Wed, 6/7/20232023Los Altos7.0526.00.49131.200.6643.0
Sun, 8/30Sun, 8/30/20202020Los Gatos6.3626.00.49130.200.6640.0
Sat, 9/17Sat, 9/17/20222022San Gregorio / Tunitas6.5675.01.41129.571.81833.0
Sat, 10/1Sat, 10/1/20162016Half Moon Bay overnight campout7.5175.01.43128.831.81841.0
Mon, 10/5Mon, 10/5/20202020Half way around the bay on bay trail6.447.00.13128.800.2165.0
Sun, 6/21Sun, 6/21/20202020Sawyer Camp Trail6.5922.00.41128.370.5530.0
Thu, 1/5Thu, 1/5/20122012Tekapo Lake to Omarama New Zealand5.4627.00.51127.790.7654.0
Tue, 8/7Tue, 8/7/20182018Bike Ride Northwest Day 36.1864.01.22127.051.61552.0
Sun, 6/15Sun, 6/15/20142014Sierra to the Sea Day 15.5761.01.15126.351.51456.0
Sun, 2/7Sun, 2/7/20212021Saratoga / Campbell5.8929.00.55126.110.7692.0
Sat, 7/2Sat, 7/2/20222022Bear Gulch, West Side6.4990.01.70125.072.12131.0
Sun, 6/2Sun, 6/2/20192019The Sequoia6.6883.01.58124.712.01971.0
\n", "
" ], "text/plain": [ - " date year title hours miles feet \\\n", - " Fri, 1/9 2012 Otago Rail Trail Century 7.87 102.41 2286 \n", - " Sat, 5/7 2022 Wine Country Century 6.65 100.26 5253 \n", - " Thu, 6/14 2012 Coyote Creek Century with Juliet 8.14 100.07 1513 \n", - " Sat, 5/13 2017 Morgan Hill iCare Classic 7.46 100.05 4596 \n", - " Sat, 5/12 2018 ICare Classic, Morgan Hill 6.80 91.29 4160 \n", - " Sat, 5/6 2017 Wine Country Century 7.26 89.49 5246 \n", - " Fri, 8/10 2018 Bike Ride Northwest Day 6 6.24 84.70 4380 \n", - " Fri, 2/28 2020 Sawyer Camp Trail 6.41 84.43 3448 \n", - " Wed, 6/7 2023 Los Altos 7.05 81.54 2110 \n", - " Sun, 8/30 2020 Los Gatos 6.36 80.92 2100 \n", - " Sat, 9/17 2022 San Gregorio / Tunitas 6.56 80.53 6015 \n", - " Sat, 10/1 2016 Half Moon Bay overnight campout 7.51 80.07 6039 \n", - " Mon, 10/5 2020 Half way around the bay on bay trail 6.44 80.05 541 \n", - " Sun, 6/21 2020 Sawyer Camp Trail 6.59 79.78 1738 \n", - " Thu, 1/5 2012 Tekapo Lake to Omarama New Zealand 5.46 79.42 2145 \n", - " Tue, 8/7 2018 Bike Ride Northwest Day 3 6.18 78.96 5092 \n", - " Sun, 6/15 2014 Sierra to the Sea Day 1 5.57 78.53 4777 \n", - " Sun, 2/7 2021 Saratoga / Campbell 5.89 78.38 2270 \n", - " Sat, 7/2 2022 Bear Gulch, West Side 6.49 77.73 6991 \n", - " Sun, 6/2 2019 The Sequoia 6.68 77.51 6467 \n", + " date year title hours miles \\\n", + " Fri, 1/9/2012 2012 Otago Rail Trail Century 7.87 102.41 \n", + " Sat, 5/7/2022 2022 Wine Country Century 6.65 100.26 \n", + " Thu, 6/14/2012 2012 Coyote Creek Century with Juliet 8.14 100.07 \n", + " Sat, 5/13/2017 2017 Morgan Hill iCare Classic 7.46 100.05 \n", + " Sat, 5/12/2018 2018 ICare Classic, Morgan Hill 6.80 91.29 \n", + " Sat, 5/6/2017 2017 Wine Country Century 7.26 89.49 \n", + " Fri, 8/10/2018 2018 Bike Ride Northwest Day 6 6.24 84.70 \n", + " Fri, 2/28/2020 2020 Sawyer Camp Trail 6.41 84.43 \n", + " Wed, 6/7/2023 2023 Los Altos 7.05 81.54 \n", + " Sun, 8/30/2020 2020 Los Gatos 6.36 80.92 \n", + " Sat, 9/17/2022 2022 San Gregorio / Tunitas 6.56 80.53 \n", + " Sat, 10/1/2016 2016 Half Moon Bay overnight campout 7.51 80.07 \n", + " Mon, 10/5/2020 2020 Half way around the bay on bay trail 6.44 80.05 \n", + " Sun, 6/21/2020 2020 Sawyer Camp Trail 6.59 79.78 \n", + " Thu, 1/5/2012 2012 Tekapo Lake to Omarama New Zealand 5.46 79.42 \n", + " Tue, 8/7/2018 2018 Bike Ride Northwest Day 3 6.18 78.96 \n", + " Sun, 6/15/2014 2014 Sierra to the Sea Day 1 5.57 78.53 \n", + " Sun, 2/7/2021 2021 Saratoga / Campbell 5.89 78.38 \n", + " Sat, 7/2/2022 2022 Bear Gulch, West Side 6.49 77.73 \n", + " Sun, 6/2/2019 2019 The Sequoia 6.68 77.51 \n", "\n", - " mph vam fpm pct kms km_up \n", - " 13.01 89.0 22.0 0.42 164.78 0.7 \n", - " 15.08 241.0 52.0 0.99 161.32 1.6 \n", - " 12.29 57.0 15.0 0.29 161.01 0.5 \n", - " 13.41 188.0 46.0 0.87 160.98 1.4 \n", - " 13.42 186.0 46.0 0.86 146.89 1.3 \n", - " 12.33 220.0 59.0 1.11 143.99 1.6 \n", - " 13.57 214.0 52.0 0.98 136.28 1.3 \n", - " 13.17 164.0 41.0 0.77 135.85 1.1 \n", - " 11.57 91.0 26.0 0.49 131.20 0.6 \n", - " 12.72 101.0 26.0 0.49 130.20 0.6 \n", - " 12.28 279.0 75.0 1.41 129.57 1.8 \n", - " 10.66 245.0 75.0 1.43 128.83 1.8 \n", - " 12.43 26.0 7.0 0.13 128.80 0.2 \n", - " 12.11 80.0 22.0 0.41 128.37 0.5 \n", - " 14.55 120.0 27.0 0.51 127.79 0.7 \n", - " 12.78 251.0 64.0 1.22 127.05 1.6 \n", - " 14.10 261.0 61.0 1.15 126.35 1.5 \n", - " 13.31 117.0 29.0 0.55 126.11 0.7 \n", - " 11.98 328.0 90.0 1.70 125.07 2.1 \n", - " 11.60 295.0 83.0 1.58 124.71 2.0 " + " feet mph vam fpmi pct kms meters \n", + " 2286 13.01 89.0 22.0 0.42 164.78 697.0 \n", + " 5253 15.08 241.0 52.0 0.99 161.32 1601.0 \n", + " 1513 12.29 57.0 15.0 0.29 161.01 461.0 \n", + " 4596 13.41 188.0 46.0 0.87 160.98 1401.0 \n", + " 4160 13.42 186.0 46.0 0.86 146.89 1268.0 \n", + " 5246 12.33 220.0 59.0 1.11 143.99 1599.0 \n", + " 4380 13.57 214.0 52.0 0.98 136.28 1335.0 \n", + " 3448 13.17 164.0 41.0 0.77 135.85 1051.0 \n", + " 2110 11.57 91.0 26.0 0.49 131.20 643.0 \n", + " 2100 12.72 101.0 26.0 0.49 130.20 640.0 \n", + " 6015 12.28 279.0 75.0 1.41 129.57 1833.0 \n", + " 6039 10.66 245.0 75.0 1.43 128.83 1841.0 \n", + " 541 12.43 26.0 7.0 0.13 128.80 165.0 \n", + " 1738 12.11 80.0 22.0 0.41 128.37 530.0 \n", + " 2145 14.55 120.0 27.0 0.51 127.79 654.0 \n", + " 5092 12.78 251.0 64.0 1.22 127.05 1552.0 \n", + " 4777 14.10 261.0 61.0 1.15 126.35 1456.0 \n", + " 2270 13.31 117.0 29.0 0.55 126.11 692.0 \n", + " 6991 11.98 328.0 90.0 1.70 125.07 2131.0 \n", + " 6467 11.60 295.0 83.0 1.58 124.71 1971.0 " ] }, - "execution_count": 112, + "execution_count": 105, "metadata": {}, "output_type": "execute_result" } diff --git a/ipynb/BikeCode.ipynb b/ipynb/BikeCode.ipynb index 768ccf7..b6f92eb 100644 --- a/ipynb/BikeCode.ipynb +++ b/ipynb/BikeCode.ipynb @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ @@ -31,24 +31,24 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Reading Data: `rides` and `yearly`\n", + "# Reading Data: `rides`, `yearly`, and `daily`\n", "\n", - "I saved a bunch of my recorded [Strava](https://www.strava.com/athletes/575579) rides, most of them longer than 25 miles, as [`bikerides.tsv`](bikerides.tsv). The columns are: the date; the year; a title; the elapsed time of the ride; the length of the ride in miles; and the total climbing in feet, e.g.: \n", + "I saved a bunch of my recorded [Strava](https://www.strava.com/athletes/575579) rides, most of them longer than 25 miles, as [`bikerides.tsv`](bikerides.tsv). The tab-separated columns are: the date; the year; a title; the elapsed time of the ride; the length of the ride in miles; and the total climbing in feet, e.g.: \n", "\n", - " Mon, 10/5\t2020\tHalf way around the bay on bay trail\t6:26:35\t80.05\t541\n", + " Mon, 10/5/2020\tHalf way around the bay on bay trail\t6:26:35\t80.05\t541\n", " \n", "I parse the file into the pandas dataframe `rides`, adding derived columns for miles per hour, vertical meters climbed per hour (VAM), grade in feet per mile, grade in percent, and kilometers ridden:" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 119, "metadata": {}, "outputs": [], "source": [ "def parse_rides(lines):\n", " \"\"\"Parse a bikerides.tsv file.\"\"\"\n", - " return drop_index(add_columns(pd.read_table(lines, comment='#',\n", + " return drop_index(add_ride_columns(pd.read_table(lines, comment='#',\n", " converters=dict(hours=parse_hours, feet=parse_int))))\n", "\n", "def parse_hours(time: str) -> float: \n", @@ -57,18 +57,20 @@ " for i, x in enumerate(reversed(time.split(':'))))\n", " return round(hrs, 2)\n", "\n", - "def parse_int(field: str) -> int: return int(field.replace(',', ''))\n", + "def parse_int(field: str) -> int: return int(field.replace(',', '').replace('ft', '').replace('mi', ''))\n", "\n", - "def add_columns(rides) -> pd.DataFrame:\n", + "def add_ride_columns(rides) -> pd.DataFrame:\n", " \"\"\"Compute new columns from existing ones.\"\"\"\n", " mi, hr, ft = rides['miles'], rides['hours'], rides['feet']\n", + " if 'date' in rides and 'year' not in rides:\n", + " rides.insert(1, \"year\", [int(str(d).split('/')[-1]) for d in rides['date'].tolist()])\n", " return rides.assign(\n", " mph=round(mi / hr, 2),\n", " vam=round(ft / hr / 3.28084),\n", - " fpm=round(ft / mi),\n", + " fpmi=round(ft / mi),\n", " pct=round(ft / mi * 100 / 5280, 2),\n", " kms=round(mi * 1.609, 2),\n", - " km_up=round(ft * 0.0003048, 1))\n", + " meters=round(ft * 0.3048))\n", "\n", "def drop_index(frame) -> pd.DataFrame:\n", " \"\"\"Drop the index column.\"\"\"\n", @@ -78,15 +80,17 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 125, "metadata": {}, "outputs": [], "source": [ "rides = parse_rides(open('bikerides.tsv'))\n", - "yearly = parse_rides(open('bikeyears.tsv')).drop(columns=['date', 'title'])\n", + "\n", + "yearly = parse_rides(open('bikeyears.tsv')).drop(columns='date')\n", + "\n", "daily = yearly.copy()\n", - "for name in 'hours miles feet kms km_up'.split():\n", - " daily[name] = round(daily[name].map(lambda x: x / 350), 3 if name == 'km_up' else 1)" + "for name in 'hours miles feet kms meters'.split():\n", + " daily[name] = round(daily[name].map(lambda x: x / (6 * 52)), 1)" ] }, { @@ -106,7 +110,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 36, "metadata": {}, "outputs": [], "source": [ @@ -118,30 +122,12 @@ " title, mi, ft, *times = segment.split(',')[:5]\n", " for time in times:\n", " records.append((title, parse_hours(time), float(mi), parse_int(ft)))\n", - " return add_columns(pd.DataFrame(records, columns=('title', 'hours', 'miles', 'feet')))" + " return add_ride_columns(pd.DataFrame(records, columns=('title', 'hours', 'miles', 'feet')))" ] }, { "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [], - "source": [ - "segments = parse_segments(open('bikesegments.csv'))" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [], - "source": [ - "places = pd.read_table(open('bikeplaceshort.csv'), sep=',', comment='#')" - ] - }, - { - "cell_type": "code", - "execution_count": 50, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ @@ -160,18 +146,25 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 59, "metadata": {}, "outputs": [], "source": [ + "segments = parse_segments(open('bikesegments.csv'))\n", + "\n", + "places = drop_index(pd.read_table(open('bikeplaceshort.csv'), sep=',', comment='#'))\n", + "\n", "tiles = drop_index(pd.DataFrame(columns='date square cluster total comment'.split(), data=[\n", - " ('06/30/2023', 13, 689, 2640, 'Rides in east Bay!9298603815'),\n", - " ('04/14/2023', 13, 630, 2595, 'Black Sands Beach connects Marin to max cluster!8891171008'),\n", - " ('03/04/2023', 13, 583, 2574, 'Almaden rides connects Gilroy to max cluster!8654437264'),\n", - " ('10/22/2022', 13, 396, 2495, 'Alviso levees to get to 13x13 max square!8003921626'),\n", - " ('10/16/2022', 12, 393, 2492, 'Milpitas ride connects East Bay to max cluster!7974994605'),\n", - " ('09/08/2022', 11, 300, 2487, 'First started tracking tiles')])\n", - " ).style.format({'comment': make_clickable, 'date': link_date})" + " ('01/01/2024', 14, 1056, 3105, 'Start of this year'),\n", + " ('12/08/2023', 14, 1042, 3084, 'Benicia ride connects East Bay and Napa clusters!10350071201'),\n", + " ('11/05/2023', 14, 932, 2914, 'Alum Rock ride gets 14x14 max square!8850905872'),\n", + " ('06/30/2023', 13, 689, 2640, 'Rides in east Bay fill in holes!9298603815'),\n", + " ('04/14/2023', 13, 630, 2595, 'Black Sands Beach low-tide hike connects Marin to max cluster!8891171008'),\n", + " ('03/04/2023', 13, 583, 2574, 'Almaden rides connects Gilroy to max cluster!8654437264'),\n", + " ('10/22/2022', 13, 396, 2495, 'Alviso levees to get to 13x13 max square!8003921626'),\n", + " ('10/16/2022', 12, 393, 2492, 'Milpitas ride connects East Bay to max cluster!7974994605'),\n", + " ('09/08/2022', 11, 300, 2487, 'First started tracking tiles')])\n", + " ).style.format({'comment': make_clickable, 'date': link_date})" ] }, { @@ -183,7 +176,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 41, "metadata": {}, "outputs": [], "source": [ @@ -216,7 +209,7 @@ " \"\"\"Given a ride distance in miles and total climb in feet, estimate time in minutes.\"\"\"\n", " return round(60 * miles / estimator(feet / miles))\n", "\n", - "def top(frame, field, n=20): return frame.sort_values(field, ascending=False).head(n)" + "def top(frame, field, n=20): return drop_index(frame.sort_values(field, ascending=False).head(n))" ] }, { @@ -228,23 +221,33 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 101, "metadata": {}, "outputs": [], "source": [ + "def mapl(f, *values): return list(map(f, *values))\n", + "\n", "def wandering(places=places, by=['pct']):\n", " \"All those who wander are not lost.\" # Also try by=['cat', 'pct']\n", - " frame = places.sort_values(by=by, ascending=('pct' not in by))\n", " M = 1_000_000\n", - " for i, (name, miles, county, pct) in frame.iterrows():\n", - " # Some fiddling to get the format right\n", - " p = f'{pct:.1f}' if (pct > 0.1) else f'{pct:.3f}'\n", - " mymiles = pct / 100 * miles\n", - " done = f'{rounded(mymiles)}/{rounded(miles)} mi'\n", - " togo = next((f'{rounded(target / 100 * miles - mymiles):>5} mi for {target}%' \n", - " for target in (0.02, 0.1, 0.2, 1, 2, 25, 50, 90, 99)\n", - " if mymiles < target / 100 * miles), '')\n", - " print(f'{county} {p:>5}% {name:25} {done:>15} {togo}') \n", + " F = drop_index(places.sort_values(by=by, ascending=('pct' not in by)))\n", + " pd.set_option('display.max_rows', None)\n", + " return pd.DataFrame(\n", + " {'pct': [f'{p:.1f}%' if (p > 1) else f'{p:.3f}%' for p in F['pct']],\n", + " 'county': F['county'],\n", + " 'name': F['name'],\n", + " 'total': F['miles'],\n", + " 'done': mapl(rounded, F['miles'] * F['pct'] / 100),\n", + " 'to next badge': mapl(to_go, F['pct'], F['miles'])})\n", + "\n", + "\n", + "def to_go(pct, miles, targets=(0.02, 0.1, 0.2, 1, 2, 25, 50, 90, 99)):\n", + " \"\"\"Describe next target to hit to get a badge.\"\"\"\n", + " done = pct * miles / 100\n", + " return next((f'{rounded(target / 100 * miles - done):>5} mi to {target}%' \n", + " for target in targets\n", + " if done < target / 100 * miles), \n", + " '')\n", " \n", "def rounded(x: float) -> str: \n", " \"\"\"Round x to 3 spaces wide (if possible).\"\"\"\n", @@ -263,32 +266,36 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 44, "metadata": {}, "outputs": [], "source": [ "def make_leaders(data):\n", " \"\"\"Make a dataframe of leaders in two counties.\"\"\"\n", - " leaders = pd.DataFrame(data, columns=['Name', 'Initials', 'SMC %', 'SCC %', 'Front?'])\n", + " leaders = pd.DataFrame(data, columns=['Name', 'Initials', 'SMC %', 'SCC %'])\n", " leaders['SMC miles'] = [round(2814 * d[2] / 100) for d in data]\n", " leaders['SCC miles'] = [round(7569 * d[3] / 100) for d in data]\n", " leaders['Total miles'] = leaders['SMC miles'] + leaders['SCC miles']\n", " leaders['Avg %'] = (leaders['SMC %'] + leaders['SCC %']) / 2\n", " return drop_index(leaders.sort_values('Avg %', ascending=False))\n", "\n", - "leaders = make_leaders([ # Data as of Sept 20, 2023 (Name, Initials, SMC, SCC, Frontier?)\n", - " ('Barry Mann', 'BM', 76.97, 30.21, 1), ('Jason Molenda', 'JM', 7.13, 55.39, 1), \n", - " ('Peter Norvig', 'PN', 61.56, 32.8, 1), ('Brian Feinberg', 'BF', 32.5, 43.68, 1),\n", - " ('Jim Brooks', 'JB', 4.23, 44.36, 0), ('Megan Gardner', 'MG', 97.62, 8.69, 1),\n", - " ('Matthew Ring', 'MR', 78.85, 1.48, 0), ('Elliot Hoff', 'EF', 52.88, 8.14, 0)])\n", + "leaders = make_leaders([ # Data as of Jan 3, 2024 (Name, Initials, SMC, SCC)\n", + " ('Megan Gardner', 'MG', 99.01, 13.6),\n", + " ('Barry Mann', 'BM', 77.41, 30.38), \n", + " ('Peter Norvig', 'PN', 63.5, 33.0),\n", + " ('Brian Feinberg', 'BF', 32.5, 43.9),\n", + " ('Jason Molenda', 'JM', 7.56, 56.25) \n", + " ])\n", " \n", "def pareto_front(leaders):\n", - " ax = leaders.plot('SMC %', 'SCC %', grid=True, kind='scatter')\n", - " front = sorted((x, y) for i, (_, _, x, y, f, *_) in leaders.iterrows() if f)\n", + " ax = leaders.plot('SMC %', 'SCC %', kind='scatter')\n", + " front = sorted((x, y) for i, (_, _, x, y, *_) in leaders.iterrows())\n", " ax.plot(*zip(*front), ':'); ax.axis('square'); grid()\n", + " ax.set_xlabel('San Mateo County %')\n", + " ax.set_ylabel('Santa Clara County %')\n", " for i, (name, initials, x, y, *_) in leaders.iterrows():\n", " ax.text(x - 2, y + 2, initials)\n", - " return leaders.drop(columns=['Front?'])" + " return leaders" ] }, { @@ -300,7 +307,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 45, "metadata": {}, "outputs": [], "source": [ @@ -314,16 +321,14 @@ " \"\"\"The number of rides needed to reach an Eddington number target.\"\"\"\n", " return target - sum(distances >= target)\n", "\n", - "def Ed_gaps(rides, E_km=100, E_mi=67, N=11) -> dict:\n", + "def Ed_gaps(rides, E_km=103, E_mi=69, N=9) -> dict:\n", " \"\"\"A table of gaps to Eddington numbers by year.\"\"\"\n", - " data = [(E_km + d, sum(rides.kms >= E_km + d), Ed_gap(rides.kms, E_km + d), \n", - " E_mi + d, sum(rides.miles >= E_mi + d), Ed_gap(rides.miles, E_mi + d))\n", + " data = [(E_km + d, Ed_gap(rides.kms, E_km + d), E_mi + d, Ed_gap(rides.miles, E_mi + d))\n", " for d in range(N)]\n", - " df = pd.DataFrame(data, columns=['kms', 'km rides', 'kms gap', \n", - " 'miles', 'miles rides', 'miles gap'])\n", + " df = pd.DataFrame(data, columns=['kms', 'kms gap', 'miles', 'miles gap'])\n", " return drop_index(df)\n", "\n", - "def Ed_progress(rides, years=range(2023, 2013, -1)) -> pd.DataFrame:\n", + "def Ed_progress(rides, years=range(2024, 2013, -1)) -> pd.DataFrame:\n", " \"\"\"A table of Eddington numbers by year, and a plot.\"\"\"\n", " def Ed(year, unit): return Ed_number(rides[rides['year'] <= year], unit)\n", " data = [(y, Ed(y, 'kms'), Ed(y, 'miles')) for y in years]\n", diff --git a/ipynb/Goldberg.ipynb b/ipynb/Goldberg.ipynb index b1c4a29..e691947 100644 --- a/ipynb/Goldberg.ipynb +++ b/ipynb/Goldberg.ipynb @@ -4,112 +4,27 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# The Unreasonable Effectiveness of Character-level Language Models\n", - "# (and why RNNs are still cool)\n", + "
Peter Norvig
2019, revised Jan 2024
Based on Yoav Goldberg's 2015 notebook
\n", "\n", - "## By [Yoav Goldberg](http://www.cs.biu.ac.il/~yogo) (2015)\n", + "# Generative Character-Level Language Models\n", "\n", - "#### (with minor changes by Peter Norvig (2022) for modern Python 3)\n", + "This is a variant of [**Yoav Goldberg's 2015 notebook**](https://nbviewer.org/gist/yoavg/d76121dfde2618422139) on character-level language models, which in turn was a response to [**Andrej Karpathy's 2015 blog post**](http://karpathy.github.io/2015/05/21/rnn-effectiveness/) on recursive neural network (RNN) language models. The term [generative AI](https://en.wikipedia.org/wiki/Generative_artificial_intelligencehttps://en.wikipedia.org/wiki/Generative_artificial_intelligence) is all the rage these days; it refers to computer programs that can *generate* something new (such as an image or a piece of text) based on a model learned from training data. Back in 2015 generative AI was just starting to take off, and Karpathy's point was that the RNNs were unreasonably effective at generating good text, even though they are at heart quite simple. Goldberg's point was that, yes, that's true, but actually most of the magic is not in the RNNs, it is in the training data itself, and an even simpler model (with no neural nets) does just as well at generating English text. Goldberg did agree with Karpathy that the RNN captures some aspects of C++ code that the character-level model does not.\n", "\n", - "
\n", + "My implementation is similar to Goldberg's, but I updated his code to use Python 3 instead of Python 2, and made some additional changes for simplicity and clarity. (This makes the code less efficient than it could be, but plenty fast enough.) \n", "\n", - "[RNNs](https://en.wikipedia.org/wiki/Recurrent_neural_network), [LSTMs](https://en.wikipedia.org/wiki/Long_short-term_memory) and [Deep Learning](https://en.wikipedia.org/wiki/Deep_learning) are all the rage, and a recent [blog post](http://karpathy.github.io/2015/05/21/rnn-effectiveness/) by Andrej Karpathy is doing a great job explaining what these models are and how to train them.\n", - "It also provides some very impressive results of what they are capable of. This is a great post, and if you are interested in natural language, machine learning or neural networks you should definitely read it. \n", + "## Definition\n", "\n", - "Go [**read it now**](http://karpathy.github.io/2015/05/21/rnn-effectiveness/), then come back here. \n", + "What do we mean by a **generative character-level language model**? It means a model that, when given a sequence of characters, can predict what character comes next; it can generate a continuation of a partial text. (And when the partial text is empty, it can generate the whole text.) In terms of probabilities, the model represents *P*(*c* | *h*), the probability distribution that the next character will be *c*, given a history of previous characters *h*. For example, given the previous characters `'chai'`, a character-level model should learn to predict that the next character is probably `'r'` or `'n'` (to form the word `'chair'` or `'chain'`). Goldberg calls this a model of order 4 (because it considers histories of length 4) while other authors call it an *n*-gram model with *n* = 5 (because it represents the probabilities of sequences of 5 characters).\n", "\n", - "You're back? Good. Impressive stuff, huh? How could the network learn to imitate the input like that?\n", - "Indeed. I was quite impressed as well.\n", + "## Training Data\n", "\n", - "However, it feels to me that most readers of the post are impressed by the wrong reasons.\n", - "This is because they are not familiar with **unsmoothed maximum-liklihood character level language models** and their unreasonable effectiveness at generating rather convincing natural language outputs.\n", - "\n", - "In what follows I will briefly describe these character-level maximum-likelihood langauge models, which are much less magical than RNNs and LSTMs, and show that they too can produce a rather convincing Shakespearean prose. I will also show about 30 lines of python code that take care of both training the model and generating the output. Compared to this baseline, the RNNs may seem somehwat less impressive. So why was I impressed? I will explain this too, below." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Unsmoothed Maximum Likelihood Character Level Language Model \n", - "\n", - "The name is quite long, but the idea is very simple. We want a model whose job is to guess the next character based on the previous *n* characters. For example, having seen `ello`, the next characer is likely to be either a commma or space (if we assume is is the end of the word \"hello\"), or the letter `w` if we believe we are in the middle of the word \"mellow\" or \"yellow\". Humans are quite good at this, but of course seeing a larger history makes things easier (if we were to see 5 letters instead of 4, the choice between space and `w` would have been much easier).\n", - "\n", - "We will call *n*, the number of letters we need to guess based on, the _order_ of the language model.\n", - "\n", - "RNNs and LSTMs can potentially learn infinite-order language model (they guess the next character based on a \"state\" which supposedly encodes all the previous history). We here will restrict ourselves to a fixed-order language model.\n", - "\n", - "So, we are seeing *n* letters, and need to guess the *n+1*th one. We are also given a large-ish amount of text (say, all of Shakespeare's works) that we can use. How would we go about solving this task?\n", - "\n", - "Mathematically, we would like to learn a function *P(c* | *h)*. Here, *c* is a character, *h* is a *n*-character history, and *P(c* | *h)* stands for how likely is it to see *c* after we've seen *h*.\n", - "\n", - "Perhaps the simplest approach would be to just count and divide (a.k.a **maximum likelihood estimates**). We will count the number of times each letter *c* appeared after *h*, and divide by the total numbers of letters appearing after *h*. The **unsmoothed** part means that if we did not see a given letter following *h*, we will just give it a probability of zero.\n", - "\n", - "And that's all there is to it." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Training Code\n", - "\n", - "Here is the code for training a language model, which implements *P(c* | *h)* with a counter of the number of times we have seen each character, for each history. The function `train_char_lm` takes a filename to read the characters from. `order` is the history size to consult. Note that we pad the data with `order` leading characters so that we also learn how to start.\n" + "How does the language model learn these probabilities? By observing a sequence of characters that we call the **training data**. Both Karpathy and Goldberg use the complete works of Shakespeare as their initial training data:" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, - "outputs": [], - "source": [ - "import random\n", - "import collections" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "class LanguageModel(collections.defaultdict):\n", - " \"\"\"A mapping from `order` history characters to possible next characters and their \n", - " frequency, e.g. {'spea': Counter({'k': 9, 'r': 1})} lets us generate 'speak' or 'spear'.\"\"\"\n", - " def __init__(self, order): \n", - " self.order = order\n", - " self.default_factory = collections.Counter \n", - "\n", - "def train_char_lm(fname, order=4) -> LanguageModel:\n", - " \"\"\"Train an character-level language model of given order on all the text in `fname`.\"\"\"\n", - " lm = LanguageModel(order)\n", - " data = (order * PAD) + open(fname).read()\n", - " for i in range(order, len(data)):\n", - " history, char = data[i - order:i], data[i]\n", - " lm[history][char] += 1\n", - " for counter in lm.values():\n", - " counter.total = sum(counter.values()) # Cache total counts (for sample_character)\n", - " return lm\n", - "\n", - "PAD = '`' # Character to pad the beginning of a text" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's train a model on Andrej's Shakespeare text:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, "outputs": [ { "name": "stdout", @@ -121,585 +36,12 @@ ], "source": [ "! [ -f shakespeare_input.txt ] || curl -O https://norvig.com/ngrams/shakespeare_input.txt\n", - "! wc shakespeare_input.txt" + "! wc shakespeare_input.txt # Print the number of lines, words, and characters" ] }, { "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "lm = train_char_lm(\"shakespeare_input.txt\", order=4)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Ok. Now let's do some queries on the language model:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "Counter({'r': 35,\n", - " 'w': 480,\n", - " 'u': 22,\n", - " ',': 16,\n", - " ' ': 8,\n", - " '.': 4,\n", - " '?': 4,\n", - " ':': 3,\n", - " 'n': 1,\n", - " \"'\": 10,\n", - " '!': 4})" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "lm['ello']" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "Counter({'t': 864})" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "lm['Firs']" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "Counter({'C': 119,\n", - " 'f': 14,\n", - " 'i': 21,\n", - " 't': 67,\n", - " 'u': 2,\n", - " 'S': 203,\n", - " 'h': 24,\n", - " 's': 41,\n", - " 'R': 1,\n", - " 'b': 31,\n", - " 'c': 16,\n", - " 'O': 23,\n", - " 'w': 30,\n", - " 'a': 28,\n", - " 'm': 28,\n", - " 'n': 25,\n", - " 'I': 12,\n", - " 'L': 133,\n", - " 'M': 74,\n", - " 'l': 13,\n", - " 'o': 38,\n", - " 'H': 5,\n", - " 'd': 19,\n", - " 'W': 42,\n", - " 'K': 10,\n", - " 'q': 2,\n", - " 'G': 112,\n", - " 'g': 14,\n", - " 'k': 5,\n", - " 'e': 4,\n", - " 'y': 3,\n", - " 'r': 9,\n", - " 'p': 11,\n", - " 'A': 7,\n", - " 'P': 18,\n", - " 'F': 15,\n", - " 'v': 3,\n", - " 'T': 4,\n", - " 'D': 4,\n", - " 'B': 12,\n", - " 'N': 1,\n", - " \"'\": 1,\n", - " 'E': 2})" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "lm['rst ']" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "So `\"ello\"` is followed by either space, punctuation or `w` (or `r`, `u`, `n`), `\"Firs\"` is pretty much deterministic, and the word following `\"rst \"` can start with pretty much every letter. \n", - "\n", - "## Character Probabilities\n", - "\n", - "We can extract probabilities *P(c* | *h)* from the model as follows:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "def P(c, h, lm) -> float: \n", - " \"\"\"The probability P(c | h) of next character c given history h, according to the language model.\"\"\"\n", - " return lm[h][c] / lm[h].total" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.817717206132879" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "P('w', 'ello', lm)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1.0" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "P('t', 'Firs', lm)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.0" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "P('x', 'Firs', lm)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.16292134831460675" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "P('S', 'rst ', lm)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Generating sample text from a model\n", - "\n", - "To randomly generate a sample text from a model, we maintain a history of *order* characters, starting with all pad characters. We then enter a loop that looks up the history in the language model, randomly samples a character from the history's counter, then updates the history by dropping its first character and adding the randomly-sampled character to the end. " - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "def generate_text(lm, length=1000) -> str:\n", - " \"\"\"Sample a random `length`-long passage from `lm`.\"\"\"\n", - " history = lm.order * PAD\n", - " text = []\n", - " for _ in range(length):\n", - " c = sample_character(lm[history])\n", - " history = history[1:] + c\n", - " text.append(c)\n", - " return ''.join(text)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To sample a single character *c* from a counter, randomly choose an integer *n* from 1 to the total count of characters in the counter, then iterate through the counter until the cumulative total of the counts meets or exceeds *n*." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "def sample_character(counter) -> str:\n", - " \"\"\"Randomly sample the nth character from the counter.\"\"\"\n", - " n = random.randint(1, counter.total)\n", - " cumulative = 0\n", - " for c in counter:\n", - " cumulative += counter[c]\n", - " if cumulative >= n: \n", - " return c" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Generating Shakespeare text from different order models\n", - "\n", - "Let's try to generate text based on different language-model orders. Let's start with something silly:\n", - "\n", - "## Order 2:" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Fir,\n", - "ACBENE:\n", - "But st thaverforn'd his then my ther's one,\n", - "Whe Lonce de kinger, ap the sa? Whee mus ne.\n", - "\n", - "But of ithice soaciven loven one my I ch hight sithe or ble-ern the my wherephy graidia,\n", - "a belf;\n", - "Ay, quare hins an an:\n", - "Whem'd cur beek, Hecomat on my he arry Riciuse, giver nothery: nal.\n", - "\n", - "DUKE:\n", - "Hold but,\n", - "And to moul ford\n", - "ant ace\n", - "wore hime re shathis onsol th I dis spried!\n", - "\n", - "SIR Lorthat mor ent: sur suchast the spoo, wit, the in hief-wis hand lor reand way be wo set this solorsee Dand, loot the makin am hisfourab:\n", - "Bar his the frortithe uponowee:\n", - "Is han hour leyessin on to and take my pas and to alset hatterialow\n", - "Suchaso you\n", - "be creest to tak alks.\n", - "\n", - "PATIANTIS Fraff,\n", - "ime proier theatholk yould a the raingrain anith, th hardso I halmorge th wall wast liver,\n", - "in theall-dayst this craire swot Pere whow And nobeece\n", - "Tithall, ar,\n", - "Thar und of ch mandrefor the\n", - "Andes, Dest allighe an:\n", - "Warrow thavererstund no lad your som of why nothat my whows be pur hat chou!\n", - "\n", - "SLYCUS:\n", - "It th withith it pon spe myse \n" - ] - } - ], - "source": [ - "lm = train_char_lm(\"shakespeare_input.txt\", order=2)\n", - "print(generate_text(lm))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Order 4 \n", - "\n", - "Order 2 was not so great... but what if we increase the order to 4?\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "First Murderer:\n", - "Unquet thee one; and chant:\n", - "Birons--\n", - "Poor cheeks they much my bosom sorrow! how stops as him your honest,\n", - "And it better? Rugby, if you pleasures, all me writter of men.\n", - "\n", - "EGLAMOUR:\n", - "Ay, but him a tready the work madam?\n", - "\n", - "MENENIUS:\n", - "Lamentony conquer'd in purpose; above.\n", - "\n", - "SIR ANDRONICUS:\n", - "And be my pardon my father for ink, he's greaten him bound thus?\n", - "Let thee,\n", - "And lady, willius' tongue deed world.\n", - "\n", - "PRINCE EDWARD:\n", - "As love hold, to be constantiately captains o' thee, purs and Petructified: but his judgment.\n", - "\n", - "THUR:\n", - "O Lord of thou the bold not as well take thus sends--takings thready friends, but that he sleep o' the neverended.\n", - "\n", - "MACDUFF:\n", - "Venice that blasp and him was faults struth the more of men so well;\n", - "Ford, I should betray in this in the bear you,\n", - "To sand I hadst travery,\n", - "Yet, herrily;\n", - "And bed to king: God sense\n", - "Caius with and still behind.\n", - "\n", - "SUFFOLK:\n", - "Right a tear\n", - "shout of suborned so prince him. Your titless'd the better is be the she, swell!\n", - "This a little come, and sons;\n", - "\n" - ] - } - ], - "source": [ - "lm = train_char_lm(\"shakespeare_input.txt\", order=4)\n", - "print(generate_text(lm))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "## Order 7\n", - "\n", - "Order 4 is already quite reasonable, and reads like English. Just 4 letters history! What if we increase it to 7?" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "First Citizen:\n", - "Before I keep the\n", - "sounder a rhyme nor pinch the engenders you:\n", - "And he was the valour that blows, they did;\n", - "So I grow; I prospero my lords,\n", - "Will creep into strong,\n", - "And I'll rake up, pipers.\n", - "\n", - "FALSTAFF:\n", - "Fie! you played the midwife present a large and grave,\n", - "Being simply; the honour,\n", - "Purchase the good hope\n", - "The period to make us medicine of kindness: I dare not making?\n", - "\n", - "GONZALO:\n", - "All's hush'd with the vast sea: at land:\n", - "Come, come, come, though, haply, are you colours turn'd youth: who best king of though\n", - "The enmity.\n", - "\n", - "ORLEANS:\n", - "He's alive, I was not angry indeed to him.\n", - "\n", - "MENENIUS:\n", - "I loved withal: except a sword, despite of brooded waters, and\n", - "that all tire.\n", - "\n", - "WARWICK:\n", - "Ay, sir, an she to be doubtful for those that\n", - "it will have my country's wreck, to transportance;\n", - "Sometimes, like a rebel,\n", - "And dreadful object him, till the fashion? do I not known\n", - "No less home, you wrestler's\n", - "heels a huge to be impart to hide thee much to live created one of you; which marriage move\n", - "And she is fast\n" - ] - } - ], - "source": [ - "lm = train_char_lm(\"shakespeare_input.txt\", order=7)\n", - "print(generate_text(lm))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## How about order 10?" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "First Citizen:\n", - "Read the will.\n", - "\n", - "ANTONY:\n", - "He will seek\n", - "Some way to leave as keep; whose top to climb\n", - "Is certain and shameless callet know herself.\n", - "Helen of Greece! what should I curse the ducats.'\n", - "\n", - "SALARINO:\n", - "That's not my meaning:\n", - "go to thy cost.\n", - "\n", - "VERNON:\n", - "There is no force in the first, how get hence:\n", - "Why should the gods to witness from their colour fly,\n", - "And to become the function takes,\n", - "The ear more quick of apprehensions, motions,\n", - "as promising\n", - "Than a wild dedication; facere, as it grows.\n", - "\n", - "Poet:\n", - "Ay, that I can do it: I\n", - "commend you well, my lord,\n", - "Grievous complaint; hasty and tinder-like\n", - "upon too trivial motion; one that, in King Edward's good success hath done to-day\n", - "Mad and fantastical banquet, just so much they love his lady was but devised at first, to try her skill,\n", - "Reignier, whose frank heart gave all,--\n", - "O, that way and you to your majesty had call'd you up, have held him dear.\n", - "\n", - "BEVIS:\n", - "Come, and believe thee,\n", - "Were they not by you?\n", - "\n", - "LENNOX:\n", - "Ay, my good lord;\n", - "'Tis but the gods to inte\n" - ] - } - ], - "source": [ - "lm = train_char_lm(\"shakespeare_input.txt\", order=10)\n", - "print(generate_text(lm))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## This works pretty well\n", - "\n", - "With an order of 4, we already get quite reasonable results. Increasing the order to 7 (about a word and a half of history) or 10 (about two short words of history) already gets us quite passable Shakepearan text. I'd say it is on par with the examples in Andrej's post. And how simple and un-mystical the model is!\n", - "\n", - "## Aside: First words\n", - "\n", - "One thing you may have noticed: all the generated passages start with \"Fi\". Why is that? Because the training data starts with the word \"First\" (preceeded by padding), and so when we go to randomly `generate_text`, the only thing that follows the initial padding is the word \"First\". We could get more variety in the generated text by breaking the training text up into sections and inserting padding at the start of each section. But that would require some knowledge of the structure of the training text; right know the only assumption is that it is a sequence of characters." - ] - }, - { - "cell_type": "code", - "execution_count": 19, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -720,29 +62,557 @@ } ], "source": [ - "! head shakespeare_input.txt" + "! head shakespeare_input.txt # First 10 lines" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## So why am I impressed with the RNNs after all?\n", + "## Python Code\n", "\n", - "Generating English a character at a time -- not so impressive in my view. The RNN needs to learn the previous *n* letters, for a rather small *n*, and that's it. \n", + "There are four main parts to the code:\n", "\n", - "However, Karpathy's C++ code generation example is very impressive. Why? because of the context awareness. Note that in all of Karpathy's posted examples, the code is well indented, the braces and brackets are correctly nested, and even the comments start and end correctly. This is not something that can be achieved by simply looking at the previous *n* characters. \n", - "\n", - "If Karpathy's examples are not cherry-picked, and the output is generally that nice, then the LSTM did learn something not trivial at all.\n", - "\n", - "# Linux Kernel C++ Code\n", - "\n", - "Just for the fun of it, let's see what our simple language model does with the Linux-kernel code:" + "- `LanguageModel` is a `defaultdict` that maps a history *h* to a `Counter` of the number of times each character *c* appears immediately following *h* in the training data. \n", + "- `train_LM` takes a string of training `data` and an `order`, and builds a language model, formed by counting the times each character *c* occurs and storing that under the entry for the history *h* of characters that precede *c*. \n", + "- `generate_text` generates a random text, given a language model, a desired length, and an optional start of the text. At each step it looks at the previous `order` characters and chooses a new character at random from the language model's counter for those previous characters.\n", + "- `random_sample` randomly chooses a single character from a counter, with each possibility chosen in proportion to the character's count." ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "from collections import defaultdict, Counter\n", + "\n", + "class LanguageModel(defaultdict): \"\"\"A mapping of {history: Counter(characters)}.\"\"\"\n", + "\n", + "def train_LM(data: str, order: int) -> LanguageModel:\n", + " \"\"\"Train a character-level language model of given `order` on the training `data`.\"\"\"\n", + " LM = LanguageModel(Counter)\n", + " LM.order = order\n", + " history = ''\n", + " for c in data:\n", + " LM[history][c] += 1\n", + " history = (history + c)[-order:] # add c to history; truncate history to length `order`\n", + " return LM\n", + "\n", + "def generate_text(LM: LanguageModel, length=1000, text='') -> str:\n", + " \"\"\"Generate a random text of `length` characters, with an optional start, from `LM`.\"\"\"\n", + " while len(text) < length:\n", + " history = text[-LM.order:]\n", + " text = text + random_sample(LM[history])\n", + " return text\n", + "\n", + "def random_sample(counter: Counter) -> str:\n", + " \"\"\"Randomly sample from the counter, proportional to each entry's count.\"\"\"\n", + " i = random.randint(1, sum(counter.values()))\n", + " cumulative = 0\n", + " for c in counter:\n", + " cumulative += counter[c]\n", + " if cumulative >= i: \n", + " return c" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's train a model of order 4 on the Shakespeare data. We'll call the model `LM`, and we'll do some queries of it:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "data = open(\"shakespeare_input.txt\").read()\n", + "\n", + "LM = train_LM(data, order=4)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Counter({'n': 78, 'r': 35})" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "LM[\"chai\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Counter({'p': 1360,\n", + " 's': 2058,\n", + " 'l': 1006,\n", + " 'o': 530,\n", + " 'g': 1037,\n", + " 'c': 1561,\n", + " 'a': 554,\n", + " 'C': 81,\n", + " 'r': 804,\n", + " 'h': 1029,\n", + " 'R': 45,\n", + " 'd': 1170,\n", + " 'w': 1759,\n", + " 'b': 1217,\n", + " 'm': 1392,\n", + " 'v': 388,\n", + " 't': 1109,\n", + " 'f': 1258,\n", + " 'i': 298,\n", + " 'n': 616,\n", + " 'V': 18,\n", + " 'e': 704,\n", + " 'u': 105,\n", + " 'L': 105,\n", + " 'y': 120,\n", + " 'A': 29,\n", + " 'H': 20,\n", + " 'k': 713,\n", + " 'M': 54,\n", + " 'T': 102,\n", + " 'j': 99,\n", + " 'q': 171,\n", + " 'K': 22,\n", + " 'D': 146,\n", + " 'P': 54,\n", + " 'S': 40,\n", + " 'G': 75,\n", + " 'I': 14,\n", + " 'B': 31,\n", + " 'W': 14,\n", + " 'E': 77,\n", + " 'F': 103,\n", + " 'O': 3,\n", + " \"'\": 10,\n", + " 'z': 6,\n", + " 'J': 30,\n", + " 'N': 18,\n", + " 'Q': 7})" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "LM[\"the \"]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "So `\"chai\"` is followed by either `'n'` or `'r'`, and almost any letter can follow `\"the \"`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Generating Shakespeare\n", + "\n", + "Let's try to generate random text based on character language models of various orders, starting with order 4." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "First\n", + "five men crown tribunes an aunt, holden a suddenly daught.\n", + "\n", + "HECTOR CAIUS:\n", + "And drawn by your confess, the jangle such a conferritoriest an make a cost, as you were the world,--\n", + "\n", + "BENVOLIO:\n", + "Where shorter:\n", + "A stom old been;\n", + "Get you may parts food;\n", + "I serve memory her. He is come fire to the\n", + "skirted great knowledges,\n", + "monster Ajax, thou do thy heart to spend theat--unhappy in and so! There shall spectar? Goodman! we are mine an heart in then\n", + "The stomach times bear too: the emperformane\n", + "And least,\n", + "And then you are my grate and as\n", + "A woman, I cannon down!' 'Course of my\n", + "love,\n", + "The tillo, away heard\n", + "You soul issue us comes hand,\n", + "To Julius, that pattering teach thither\n", + "that for come in\n", + "a fathere growned from far\n", + "Crying, from yoursed into this.\n", + "\n", + "SILVIA:\n", + "Wilt before.\n", + "\n", + "PAULINA:\n", + "Might him: but Marshall be my fail age in fat, remember than arms? calls and the compulsion liar came him. If thy is bled this ever; or your tempts,\n", + "Open an of think it from him our changentleman, more ther titless them th\n" + ] + } + ], + "source": [ + "print(generate_text(LM))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Order 4 captures the structure of plays, mentions some characters, and generates mostly English words, although they don't always go together to form grammatical sentences, and there is certainly no coherence or plot. \n", + "\n", + "## Generating Order 7 Shakespeare\n", + "\n", + "What if we increase it to order 7? Or more? We find that it gets a bit better, roughly as good as the RNN models that Karpathy shows, and all from a much simpler model." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "First Clown:\n", + "Like an ass of France to kill horns;\n", + "And Brutus, and in such a one as he weariness does any strange fish! Were I adore.' When we arrives him not the time, whither argument?\n", + "\n", + "MARIA:\n", + "Get thee on.\n", + "\n", + "SIR TOBY BELCH:\n", + "Why, 'tis well.\n", + "\n", + "CLOTEN:\n", + "Sayest trusts to your royal graces,\n", + "I will draw his heinous and holiness\n", + "Than are to breath.\n", + "\n", + "ISABELLA:\n", + "Madam, pardon me: teach you, sirs, be it lying so, yet but the 'ever' last?\n", + "\n", + "EDWARD:\n", + "An oath in it to bid you. You a lover dearly to our roses;\n", + "For intercepted pardon him,\n", + "And even now\n", + "In any branches, wherefore let us go seek him:\n", + "There's a good master; thyself a wise men,\n", + "Let him when you are\n", + "going to his entering\n", + "into so quickly.\n", + "Which all bosom as a bell,\n", + "Remember thee who I am. Good Paulina more.' And in an hour?\n", + "\n", + "ORLANDO:\n", + "As I wear\n", + "In the eastern gate, horse!\n", + "Do but he hath astonish thee apt;\n", + "And this pardon me, I conjure them:\n", + "To show more offering in saying them, whose beauty starves the night\n", + "Did Jessica:\n", + "Besides, Antony. But art \n" + ] + } + ], + "source": [ + "print(generate_text(train_LM(data, order=7)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Generating Order 10 Shakespeare" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "First Citizen:\n", + "That cannot go but thirty miles to ride yet ere day.\n", + "\n", + "PUCK:\n", + "Now the pleasure of the realm in farm.\n", + "\n", + "LORD WILLOUGHBY:\n", + "And daily graced by an inkhorn mate,\n", + "We and our power,\n", + "Let us see:\n", + "Write, 'Lord have mercies\n", + "More than all his creature in her, you may\n", + "say they be not take my plight shall lie\n", + "His old betrothed lord.\n", + "\n", + "URSULA:\n", + "She's limed, I warrant;\n", + "speciously on him;\n", + "Lose not so near:\n", + "I had rather be at a breakfast to the abject rear,\n", + "O'er-run and trampled on: then what is this law?\n", + "\n", + "First Murderer:\n", + "What speech, my lord\n", + "For certain, and is gone aboard a\n", + "new ship to purge him of the affected.\n", + "\n", + "PRINCE:\n", + "Give me a copy of the forlorn French!\n", + "Him I forgive thee,\n", + "Unnatural though the very life\n", + "Of my dear friend Leonato hath\n", + "invited you all. I tell him we shall have 'em\n", + "Talk us to silence.\n", + "\n", + "ANNE:\n", + "You can do better yet\n", + "And show the increasing in love?\n", + "\n", + "LUCETTA:\n", + "That they travail for, if it were not virtue, not\n", + "For such proceeding by the way\n", + "Should have both the parties of suspic\n" + ] + } + ], + "source": [ + "print(generate_text(train_LM(data, order=10)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aside: Probabilities\n", + "\n", + "Sometimes we'd rather see probabilities, not raw counts. Given a language model `LM`, the probability *P(c* | *h)* can be computed as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "def P(c, h, LM=LM): \n", + " \"The probability that character c follows history h.\"\"\"\n", + " return LM[h][c] / sum(LM[h].values())" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.09286165508528112" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "P('s', 'the ')" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.6902654867256637" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "P('n', 'chai')" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.30973451327433627" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "P('r', 'chai')" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.0" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "P('s', 'chai')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Shakespeare never wrote about \"chaise longues,\" so the probability of an `'s'` following `'chai'` is zero, according to our language model. But do we really want to say it is absolutely impossible for the sequence of letters `'chais'` to appear, just because we didn't happen to see it in our training data? More sophisticated language models use [**smoothing**](https://en.wikipedia.org/wiki/Kneser%E2%80%93Ney_smoothing) to assign non-zero (but small) probabilities to previously-unseen sequences. But in this notebook we stick to the simple unsmoothed model." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aside: Starting Text\n", + "\n", + "One thing you may have noticed: all the generated passages start with \"F\". Why is that? Because the training data happens to start with the line \"First Citizen:\", and so when we call `generate_text`, we start with an empty history, and the only thing that follows the empty history is the letter \"F\". We could get more variety in the generated text by breaking the training text up into separate sections, so that each section would contribute a different possible starting point. But that would require some knowledge of the structure of the training text; right now the only assumption is that it is a sequence of characters.\n", + "\n", + "We can give a starting text to `generate_text` and it will continue from there. But since the models only look at a few characters of history (just 4 for `LM`), this won't make much difference." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ROMEO:\n", + "The kill not my come.\n", + "\n", + "FALSTAFF:\n", + "No, good, sister, whereforeson, and strive merry Blance; and by the like a heart of mind;\n", + "And him sough they bodied;\n", + "The could thy made know are corona's and proved,\n", + "To fresh are did call'd Messenge you into\n", + "termity.\n", + "On they found\n", + "they.\n", + "\n", + "Firstling our such a score you a\n", + "touch'd,\n", + "I make you them compossessed in dead,\n", + "And when the hadst be, thy lament:\n", + "Your to doth due that ring, and quiet not be fetch hear: but stop. All\n", + "the map o'erween attery seat most wonder of beat imprison want me hear to the general we wick outward's he gentre, doth receive doom; and forth\n", + "Do you know'd cond,\n", + "And root us madam, yours of with her of it is.\n", + "\n", + "SHALLOW:\n", + "'Swound and cry a bravel your land, crystally carry with than and present they display\n", + "Is no remembrass, each and monkey, thrive does wife countain,\n", + "We will we marry, I\n", + "shall have I never ask, the reason thes:\n", + "He's good for me: thou and me good to bedded:\n", + "Again, thee, death made best.\n", + "\n", + "MARK ANTONIO:\n", + "Amen, stays to\n" + ] + } + ], + "source": [ + "print(generate_text(LM, text='ROMEO:'))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Linux Kernel C++ Code\n", + "\n", + "Goldberg's point is that the simple character-level model performs about as well as the much more complex RNN model on Shakespearean text. But Karpathy also trained an RNN on 6 megabytes of Linux-kernel C++ code. Let's see what we can do with that training data." + ] + }, + { + "cell_type": "code", + "execution_count": 16, "metadata": { "collapsed": false, "jupyter": { @@ -760,12 +630,22 @@ ], "source": [ "! [ -f linux_input.txt ] || curl -O https://norvig.com/ngrams/linux_input.txt\n", + "linux = open(\"linux_input.txt\").read()\n", "! wc linux_input.txt" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Generating Order 10 C++\n", + "\n", + "We'll start with an order-10 model, and compare that to an order-20 model. WEe'll generate a longer text, because sometimes a 1000-character text ends up being just one long comment." + ] + }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 17, "metadata": { "collapsed": false, "jupyter": { @@ -779,119 +659,145 @@ "text": [ "/*\n", " * linux/kernel.h>\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", "#include \n", - "#include \n", - "#include \n", - "#include \"rcu.h\"\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", "\n", - "MODULE_AUTHOR(\"Paul E. McKenney GRAPH_MAX_FUNC_TEST) {\n", - "\t\ttrace_array_put(this_tr);\n", - "\tmutex_unlock_pi;\n", + "#include \n", + "#include \t\t/* try_to_freeze();\n", "\n", - "\t/*\n", - "\t * One idle CPUs. */\n", - "\tshuffle_intervals\n", - "\t * (think \"ticks\") worth of data\n", - " */\n", - "static int func_id)\n", + "\t\tif (hlock->references) {\n", + "\t\thlock_curr;\n", + "\tint cpu;\n", + "\n", + "\t/* initiate RCU priority unchanged. Otherwise just see\n", + "\t * if we get it wrong the load-balancer moves */\n", + "\tupdate_sched_clock_stable()) {\n", + "\t\t*(char **)kp->arg));\t\t\t\\\n", + "\t}\t\t\t\t\t\t\t\t\\\n", + "\tfor ((cmd) = kdb_base_commands, list) {\n", + "\t\tfor (i = 0; i < length; i++)\n", + "\t\tseq_printf(m, \"Per CPU device: %d\\n\", ret);\n", + "\t\treturn error;\n", + "\t}\n", + "\n", + "\tif (skip_equal && f->op != Audit_equal)\n", + "\t\t\treturn 0;\n", + "}\n", + "\n", + "static bool migrated to a second choice node will lead to deadlock detection for find_existing_css_set(struct gcov_iterator *iter)\n", "{\n", - "\tswitch (ret) {\n", - "\t\t\tif (group_rq && (rt_rq_throttled;\n", + "\tif (iter->idx > i)\n", + "\t\treturn;\n", + "\n", + "\tif (graph)\n", + "\t\tret = rb_head_page_activate(struct tick_device *tick_get_tick_dev(struct delayed_work(&req->work);\n", + "\n", + "\treturn 0;\n", + "}\n", + "\n", + "static bool rcu_preempt_qs(void)\n", + "{\n", + "\tstruct rt_bandwidth;\n", + "\n", + "\tif (entry->class == data;\n", "}\n", "\n", "/**\n", - " * worker_clr_flags(worker, WORKER_PREP);\n", - "sleep:\n", - "\t/*\n", - "\t * In the semi idle case by checking on the CPU, it can't propagate the requeue\n", - " * @mode:\texpiry mode: absolute time */\n", - "\tif (ftrace_enabled) {\n", - "\t\t/* Keep track of cpu being initialization of architecture-specific fields. */\n", - "\tadd_taint(TAINT_LIVEPATCH\\n\");\n", - "\tadd_taint(TAINT_FORCED_MODULE))\n", - "\t\tbuf[l++] = 'E';\n", - "\n", - "\trwbs[i] = '\\0';\n", - "\tif (count > 0) {\n", - "\t\terror = -EINVAL;\n", - "\t}\n", - "\n", - "\tif (prctl_map->__m1 __op\t\t\t\t\\\n", - "\t (unsigned long ticks)\n", + " * pm_qos_update_request_timeout(\n", + "\tvoid *word, int bit)\n", "{\n", - "\tjiffie\n" - ] - } - ], - "source": [ - "lm = train_char_lm(\"linux_input.txt\", order=10)\n", - "print(generate_text(lm))" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/*\n", - " * linux/kernel/timer.c.\n", - " * Please see that file for copyright and history logs.\n", + "\t__wake_up(wait_queue(¤t->signal->thread_head = (cmd_head == cmd_tail)\n", + "\t\treturn -ENOMEM;\n", + "\n", + "\tmutex_init(&session->stat_root, node) {\n", + "\n", + "\t\t\t/* exclude other factors [XXX].\n", " *\n", - " */\n", + " * The init function_set_filter_inodes(tsk, context, for example, \"1\" if the CPU is\n", + " * not large enough to allow coalescing,\n", + " * allocators\n", + "\t * in the owner.\n", + "\t\t * refcount of the sample period a kick. */\n", + "\t\t\traw_spin_unlock_commit(struct nosave_region *region;\n", "\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD kernel builds.\n", - " */\n", - "void destroy_rcu_head_on_stack(&rh1);\n", - "\tinit_rcu_head_on_stack(&rcu.head);\n", - "\tinit_completion(&self.exited);\n", - "\tinit_completion(&rcu.completion);\n", + "\tif (kobj) {\n", + "\t\tmk = to_module_attribute *klp_patch_attrs[] = {\n", + "\t&dev_attr_pid)\n", + "\t\t\tq->blk_trace;\n", + "\tstruct file *filp, const char *devname,\n", + "\t\t\t int cpu, struct rcu_head *nocb_follower_head);\n", "\n", - "\t/* Other rcu_barrier(). */\n", - "\t/* End of fields guarded by barrier_mutex. */\n", + "\taux_head = local_read(&cpu_buffer->pages = reqd_free_pages;\n", + "\twhile ((*nl) != NULL) {\n", + "\t\tmemcpy(&entry->caller[6], __entry->caller[7]),\n", "\n", - "\tatomic_long_t refs;\n", - "\tstruct rcu_node *rnp;\n", + "\tFILTER_OTHER\n", + ");\n", "\n", - "\tif (t->rcu_read_lock_nesting, shared state will be updated only when filter_hash updating */\n", - "\t\tret = atomic_add_return(1, &rttest_event);\n", - "\t\ttd->mutexes[td->opdata] = 1;\n", - "\t\ttd->event = atomic_add_return(0, &rdp->dynticks->dynticks_nesting, rdtp->dyn\n" + "FTRACE_ENTRIES];\n", + "\tsector_t cur_swap;\n", + "\treturn retval;\n", + "}\n", + "\n", + "/*\n", + " * Load the comments */\n", + "\tif (se->on_rq)\n", + "\t\taccount_steal_ticks(unsigned long cpu0_err;\n", + "\n", + "static inline int desc_node(desc), NULL);\n", + "\tif (ret) {\n", + "\t\tif (mode & HRTIMER_STATS\n", + "\tif (likely(uprobe_is_active(uprobe))) {\n", + "\t\tret = unregister_ftrace_function_stack_trace_init_module);\n", + "\n", + "static int __sprint_symbols_seq(struct sched_param param = { .sched_priority == 0 when\n", + "\t * part of the group hasn't been updated, and\n", + "\t *\tthe other group-sibling):\n", + "\t\t */\n", + "\t\titer->cpu = cpumask_first(sched_domain *sd)\n", + "{\n", + "\treturn single_open(file, tracing_cpumask_notifier - unregister(struct task_struct *p)\n", + "{\n", + "\tstruct cpuset *parent_css = cgroup_parent(cgrp);\n", + "\tunsigned long active_timers--;\n", + "\t(void)catchup_timer_jiffies;\n", + "\tzalloc_cpumask_var(&mask, GFP_KERNEL);\n", + "\tif (!buf)\n", + "\t\tgoto out;\n", + "\t\tif ((unsigned long) (t)->tv_usec)) ? -EFAULT : 0;\n", + "\n", + "\treturn 0;\n", + "}\n", + "\n", + "DEFINE_PER_CPU(struct swap_map_handle *handle, void *buf,\n", + "\t\t\t sizeof(files_stat,\n", + "\t\t.maxlen\t\t= sizeof(int)))\n", + "\t\t\treturn\n" ] } ], "source": [ - "lm = train_char_lm(\"linux_input.txt\", order=15)\n", - "print(generate_text(lm))" + "print(generate_text(train_LM(linux, order=10), length=3000))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Order 20 C++" ] }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 18, "metadata": { "collapsed": false, "jupyter": { @@ -906,361 +812,126 @@ "/*\n", " * linux/kernel/irq/manage.c\n", " *\n", - " * Copyright (C) 2006 Timesys Corp., Thomas Gleixner\n", + " * Copyright (C) 1992, 1998-2006 Linus Torvalds, Ingo Molnar\n", + " * Copyright(C) 2007, Red Hat, Inc., Ingo Molnar \n", + " * Guillaume Chazarain \n", " *\n", - " * This code is licenced under the GPL version 2. For details see\n", - " * kernel-base/COPYING\n", - " */\n", - "\n", - "#include \n", - "#include \n", - "#include \n", - "#include /* for spin_unlock_irq() using preempt_count() m68k */\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "\n", - "#include \"power.h\"\n", - "\n", - "static DEFINE_MUTEX(stop_cpus_mutex);\n", - "static DEFINE_SPINLOCK(rcu_torture_lock);\n", - "static DEFINE_PER_CPU(struct llist_head, call_single_queue);\n", - "\n", - "static void flush_module_icache(const struct module *mod,\n", - "\t\t\t const unsigned long seccomp_mode = SECCOMP_MODE_STRICT:\n", - "\t\top = SECCOMP_SET_MODE_FILTER:\n", - "\t\treturn __seccomp_phase1_filter(int this_syscall, struct seccomp_data *sd)\n", - "{\n", - "\tstruct seccomp_filter *walker;\n", - "\n", - "\tassert_spin_locked(¤t->sighand->siglock);\n", - "\tset_proces\n" - ] - } - ], - "source": [ - "lm = train_char_lm(\"linux_input.txt\", order=20)\n", - "print(generate_text(lm))" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/*\n", - " * linux/kernel/irq/autoprobe.c\n", " *\n", - " * Copyright (C) 2003-2004 Amit S. Kale \n", - " * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.\n", + " * What:\n", " *\n", - " * This program is free software; you can redistribute it and/or modify\n", - " * it under the terms of the GNU General Public License version 2 as\n", - " * published by the Free Software Foundation.\n", - " */\n", - "\n", - "#include \n", - "\n", - "#include \"trace.h\"\n", - "#include \"trace_output.h\"\n", - "\n", - "struct header_iter {\n", - "\tstruct pci_dev *dev;\n", - "};\n", - "\n", - "static struct rb_root swsusp_extents = RB_ROOT;\n", - "\n", - "static int swsusp_page_is_forbidden(page) && swsusp_page_is_free(virt_to_page(lp))) {\n", - "\t\t\t/* The page is \"safe\", add it to the list */\n", - "\t\t\tlp->next = safe_pages_list;\n", - "\t\tsafe_pages_list = safe_pages_list->next;\n", - "\tpbe->next = restore_pblist;\n", - "\trestore_pblist = NULL;\n", - "\tbuffer = NULL;\n", - "\talloc_normal = 0;\n", - "\talloc_highmem = 0;\n", - "\n", - "\t/* Count the number of saveable data pages. */\n", - "\tsave_highmem = count_highmem_image_pages - compute the total number of overruns from\n", - " */\n", - "unsigned long\n", - "ring\n" - ] - } - ], - "source": [ - "print(generate_text(lm))" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/*\n", - " * linux/kernel/irq/resend.c\n", + " * cpu_clock(i) -- can be used from any context, including NMI.\n", + " * local_clock() -- is cpu_clock() on the current cpu.\n", " *\n", - " * Copyright (C) 1997 Andrew Main \n", + " * sched_clock_cpu(i)\n", " *\n", - " * Integrated into 2.1.97+, Andrew G. Morgan \n", - " * 30 May 2002:\tCleanup, Robert M. Love \n", - " */\n", - "\n", - "#define pr_fmt(fmt) KBUILD_MODNAME \": \" fmt\n", - "\n", - "#include \n", - "#include \n", - "#include \"cpudeadline.h\"\n", - "#include \"cpuacct.h\"\n", - "\n", - "struct rq;\n", - "struct cpuidle_state *idle_state)\n", - "{\n", - "\trq->idle_state = idle_state;\n", + " * How:\n", + " *\n", + " * The implementation either uses sched_clock() when\n", + " * !CONFIG_HAVE_UNSTABLE_SCHED_CLOCK\n", + "static struct static_key __sched_clock_stable);\n", "}\n", "\n", - "static inline struct cpuacct *css_ca(struct cgroup_subsys *ss;\n", - "\tint i;\n", + "static void __maybe_unused rcu_try_advance_all_cbs())\n", + "\t\tinvoke_rcu_core(); /* force nohz to see update. */\n", + "\t\trdtp->tick_nohz_enabled_snap = tne;\n", + "\t\treturn;\n", + "\t}\n", + "\tif (!needreport)\n", + "\t\treturn;\n", + "\tif (*firstreport) {\n", + "\t\tpr_err(\"INFO: rcu_tasks detected stalls on CPUs/tasks:\",\n", + "\t rsp->name);\n", + "\tprint_cpu_stall_info(struct rcu_state *rsp, struct rcu_node *rnp_leaf)\n", + "{\n", + "\tlong mask;\n", + "\tstruct rcu_node *rnp);\n", + "#ifdef CONFIG_HOTPLUG_CPU\n", + "\tbuffer->cpu_notify.notifier_call = rb_cpu_notify;\n", + "\tbuffer->cpu_notify.priority = 0;\n", + "\t__register_cpu_notifier(struct notifier_block *nb)\n", + "{\n", + "\treturn raw_notifier_chain_unregister(\n", + "\t\t\t\t&munmap_notifier, n);\n", + "\t\tbreak;\n", + "\t}\n", "\n", - "\tinit_cgroup_root(&cgrp_dfl_root, &opts);\n", - "\tcgrp_dfl_root.cgrp.self.flags |= CSS_NO_REF;\n", + "\treturn cpu;\n", + "}\n", "\n", - "\tif (early) {\n", - "\t\t/* allocation can't be done safely during early init */\n", - "\t\tcss->id = 1;\n", - "\t} else {\n", - "\t\tcss->id = cgroup_idr_alloc(&ss->css_idr, css, 1, 2,\n", - "\t\t\t\t\t\t GFP_KERNEL);\n", - "\tif (!data)\n", + "#define RT_PUSH_IPI_RESTART;\n", + "\t\trt_rq->push_cpu = src_rq->cpu;\n", + "\t}\n", + "\n", + "\tcpu = find_next_push_cpu(struct rq *rq)\n", + "{\n", + "\tif (rq->rt.overloaded)\n", + "\t\treturn 0;\n", + "\n", + "\tnext_task = pick_next_pushable_task(rq);\n", + "\t\tif (task_cpu(next_task) == rq->cpu && task == next_task) {\n", + "\t\t\t/*\n", + "\t\t\t * The first thread which returns from do_signal_stop()\n", + "\t\t\t * will take ->siglock, notice SIGNAL_CLD_MASK, and\n", + "\t\t\t * notify its parent. See get_signal_to_deliver().\n", + "\t\t\t */\n", + "\t\t\tsignal->flags = why | SIGNAL_STOP_CONTINUED because\n", + "\t\t * an intervening stop signal is required to cause two\n", + "\t\t * continued events regardless of ptrace.\n", + "\t\t */\n", + "\t\tif (!(sig->flags & SIGNAL_STOP_STOPPED))\n", + "\t\t\tsig->group_exit_code;\n", + "\telse if (!thread_group_empty(tsk) || signal_group_exit(tsk->signal)) {\n", + "\t\ttsk->flags |= PF_EXITING;\n", + "\n", + "\tthreadgroup_change_end(current);\n", + "\tdelayacct_tsk_free(p);\n", + "bad_fork_cleanup_signal;\n", + "\tretval = copy_namespaces(clone_flags, p);\n", + "\tif (retval)\n", "\t\tgoto out;\n", "\n", - "\tcd->fp = fp;\n", - "\tcd->clk = get_posix_clock(fp);\n", - "\tint err = -ENODEV;\n", - "\n", - "\tif (cnt >= PAGE_SIZE)\n", - "\t\treturn -EINVAL;\n", - "\trcu_read_lock();\n", - "\tsd = rcu_dereference(per_cpu(sd_asym, cpu), sd);\n", - "}\n", - "\n", - "/*\n", - " * Attach the domain 'sd' to 'cpu' as its base domain. Callers must\n", - " * hold the hotplug lock.\n", - " * For now this just excludes isolated cpus, but could be used to\n", - " * exclude other special cases in the future.\n", - " */\n", - "void nohz_balance_enter_idle(int cpu)\n", - "{\n", - "\t/*\n", - "\t * Make sure legacy kernel users don't send in bad values\n", - "\t * (normal paths check this in check_kill_permission(int sig, struct k_sigaction *ka;\n", - "\n", - "\t\tif (unlikely(current->lockdep_recursion = 1;\n", - "\t__trace_hardirqs_on_caller(CALLER_ADDR0);\n", - "}\n", - "EXPORT_SYMBOL(default_wake_function(wait, mode, sync, key);\n", - "}\n", - "\n", - "void __wake_up_parent(struct task_struct *\n", - "pick_next_task_stop(struct rq *rq)\n", - "{\n", - "}\n", - "\n", - "static inline void dl_clear_overload(struct rq *rq)\n", - "{\n", - "\treturn atomic_read(&mod->refcnt) - MODULE_REF_BASE;\n", - "}\n", - "EXPORT_SYMBOL(mod_timer_pinned);\n", - "\n", - "/**\n", - " * add_timer - start a timer\n", - " * @timer: the timer to be initialized\n", - " *\n", - " * This function can be called from any context.\n", - " */\n", - "void audit_log_untrustedstring(ab, get_task_comm(comm, current));\n", - "\taudit_log_untrustedstring(ab, get_task_comm(comm, tsk));\n", - "\n", - "\taudit_log_d_path_exe(struct audit_buffer *ab, __u32 portid)\n", - "{\n", - "\tif (ab) {\n", - "\t\tstruct nlmsghdr *nlh;\n", - "\t/*\n", - "\t * len MUST be signed for nlmsg_next to be able to dec it below 0\n", - "\t * if the nlmsg_len was not aligned\n", - "\t */\n", - "\tint len;\n", - "\tint err;\n", - "\n", - "\tnlh = nlmsg_put(ab->skb, 0, 0, type, 0, 0);\n", - "\tif (!nlh)\n", - "\t\tgoto out_kfree_skb;\n", - "\n", - "\treturn ab;\n", - "\n", - "out_kfree_skb:\n", - "\tkfree_skb(skb);\n", - "\treturn NULL;\n", - "}\n", - "\n", - "static struct ftrace_ops global_ops = {\n", - "\t.func\t\t\t= event_enable_count_trigger_ops = {\n", - "\t.func\t\t\t= ftrace_traceon_print,\n", - "};\n", - "\n", - "static struct kobj_attribute *attr,\n", - "\t\t\t char *buf)\n", - "{\n", - "\treturn sprintf(buffer, \"%i\\n\", module_refcount(mod));\n", - "\n", - "\t/*\n", - "\t * Always include a trailing , so userspace can differentiate\n", - "\t * between this and the old multi-field proc format.\n", - "\t */\n", - "\tlist_for_each_entry(tr, &ftrace_trace_arrays, list) {\n", - "\t\tlist_for_each_entry(event, &parent_ctx->pinned_groups, group_entry) {\n", - "\t\tret = inherit_task_group(event, parent, parent_ctx,\n", - "\t\t\t child, child_ctx);\n", - "\n", - "\tif (ret)\n", - "\t\t*inherited_all = 0;\n", - "\t\treturn 0;\n", - "\t}\n", - "\t\n", - "\ti = (int *) tbl_data;\n", - "\tvleft = table->maxlen / sizeof(*i);\n", - "\tleft = *lenp;\n", - "\n", - "\tif (!conv)\n", - "\t\tconv = do_proc_dointvec_conv;\n", - "\n", - "\tif (write) {\n", - "\t\tif (sysctl_writes_strict = SYSCTL_WRITES_WARN)\n", - "\t\twarn_sysctl_write(table);\n", - "\t\t\t\tbreak;\n", - "\t\t\tdefault:\n", - "\t\t\t\tWARN_ON_ONCE(1);\n", - "\t\treturn PTR_ERR(old);\n", - "\t}\n", - "\n", - "\tif (!tp_funcs) {\n", - "\t\t/* Removed last function */\n", - "\t\tif (tp->unregfunc && static_key_enabled(key);\n", - "\n", - "\tif ((!true_branch && state) || (true_branch && !state))\n", - "\t\treturn JUMP_LABEL_ENABLE;\n", - "\n", - "\treturn JUMP_LABEL_DISABLE;\n", - "}\n", - "\n", - "void __init jump_label_init(void)\n", - "{\n", - "\tstruct dentry *d = kern_path_locked(watch->path, parent);\n", - "\tif (IS_ERR(tg))\n", - "\t\treturn ERR_PTR(-ENOTDIR);\n", - "\tctl_name = *name;\n", - "\tname++;\n", - "\tnlen--;\n", - "\tfor ( ; table->convert; table++) {\n", - "\t\tint len = 0;\n", - "\n", - "\t\t/*\n", - "\t\t * For a wild card entry map from ifindex to network\n", - "\t\t * device name.\n", - "\t\t */\n", - "\t\tif (!table->ctl_name) {\n", - "#ifdef CONFIG_NET\n", - "\t.net_ns\t\t\t= &init_net,\n", - "#endif\n", - "};\n", - "\n", - "static struct miscdevice snapshot_device = {\n", - "\t.minor = SNAPSHOT_MINOR,\n", - "\t.name = \"snapshot\",\n", - "\t.fops = &snapshot_fops,\n", - "};\n", - "\n", - "static int __init sched_clock_postinit(void)\n", - "{\n", - "\t/*\n", - "\t * If there is an existing filter, make it the prev and don't drop its\n", - "\t * task reference.\n", - "\t */\n", - "\tfilter->prev = current->seccomp.mode && current->seccomp.mode != seccomp_mode)\n", - "\t\treturn false;\n", - "\n", - "\tif ((sgs->group_capacity * 100) >\n", - "\t\t\t(sgs->group_usage * env->sd->imbalance_pct))\n", - "\t\treturn true;\n", - "\n", - "\treturn false;\n", - "}\n", - "\n", - "static bool klp_initialized(void)\n", - "{\n", - "\treturn klp_root_kobj;\n", - "}\n", - "\n", - "struct klp_find_arg *args = data;\n", - "\n", - "\tif (!mod &&\n", - "\t !strcmp(args->name, name) &&\n", - "\t args->addr == addr)\n", - "\t\treturn 1;\n", - "\n", + "\tretval = sched_setaffinity(pid, new_mask);\n", + "\tfree_cpumask_var(cs->cpus_allowed);\n", "\treturn 0;\n", "}\n", "\n", - "static struct ftrace_ops *ops)\n", + "/*\n", + " * kdb_md - This function implements the 'defcmd'\n", + " *\tcommand which defines one command as a set of other commands,\n", + " *\tterminated by endefcmd. kdb_defcmd processes the initial\n", + " *\t'defcmd' command, kdb_defcmd2 is invoked from kdb_parse for\n", + " *\tthe following commands until 'endefcmd'.\n", + " * Inputs:\n", + " *\targc\targument count\n", + " *\targv\tArgument vector\n", + " * Outputs:\n", + " *\tNone.\n", + " * Returns:\n", + " *\tNone.\n", + " * Locking:\n", + " *\tNone.\n", + " * Remarks:\n", + " *\n", + " *\tbp\tSet breakpoint on all cpus. Only use hardware assist if need.\n", + " *\tbph\tSet breakpoint on all cpus. Force hardware register\n", + " */\n", + "\n", + "static int kdb_flags_stack[4], kdb_flags_index;\n", + "\n", + "void kdb_save_flags(void)\n", "{\n", - "\tint __percpu *disabled;\n", + "\tBUG_ON(kdb_flags_index >= ARRAY_SIZE(kdb_flags_stack));\n", + "\tkdb_flags_stack[kdb_flags_index++] = kdb_flags;\n", + "}\n", "\n", - "\tdisabled = alloc_percpu(int);\n", - "\tif (!disabled)\n", - "\t\treturn -EINVAL;\n", - "\n", - "\tif (vma_size != PAGE_SIZE * (1 + nr_pages))\n", - "\t\treturn -EINVAL;\n", - "\t}\n", - "\tif (insn->off != 0) {\n", - "\t\t\t\tverbose(\"BPF_END uses reserved fields\\n\");\n", - "\t\t\t\treturn -EINVAL;\n", - "\t\t}\n", - "\t\tp[AUDIT_WORD(n)] |= AUDIT_BIT(n);\n", - "\t}\n", - "\tif (class >= AUDIT_SYSCALL_CLASSES) {\n", - "\t\t\tkfree(p);\n", - "\t\t\treturn -EINVAL;\n", - "\t/* FALL THROUGH */\n", - "\tcase AUDIT\n" + "void kdb_restore_flags(void)\n", + "{\n", + "\tBUG_ON(kdb_flags_index <= \n" ] } ], "source": [ - "print(generate_text(lm, length=5000))" + "print(generate_text(train_LM(linux, order=20), length=3000))" ] }, { @@ -1269,18 +940,514 @@ "source": [ "## Analysis\n", "\n", - "Order 10 is pretty much junk. In order 15 things sort-of make sense, but we jump abruptly between the `[sic]`\n", - "and by order 20 we are doing quite nicely — but are far from keeping good indentation and brackets. \n", + "As Goldberg says, \"Order 10 is pretty much junk.\" But order 20 is much better. Most of the comments have a start and an end; most of the open parentheses are balanced with a close parentheses; but the braces are not as well balanced. That shouldn't be surprising. If the span of an open/close parenthesis pair is less than 20 characters then it is represented within the model, but if the span of an open/close brace is more than 20 characters, then it cannot be represented by the model. Goldberg notes that Karpathy's RRN seems to have learned to devote some of its long short-term memory (LSTM) to representing nesting level, as well as things like whether we are currently within a string or a comment. It is indeed impressive, as Karpathy says, that the model learned to do this on its own, without any input from the human engineer.\n", "\n", - "How could we? we do not have the memory, and these things are not modeled at all. While we could quite easily enrich our model to support also keeping track of brackets and indentation (by adding information such as \"have I seen ( but not )\" to the conditioning history), this requires extra work, non-trivial human reasoning, and will make the model significantly more complex. \n", + "## Token Models versus Character Models\n", "\n", - "Karpathy's LSTM, on the other hand, seemed to have just learn it on its own. And that's impressive." + "Karpathy and Goldberg both used character models, because the exact formatting of characters (especially indentation and line breaks) is important in the format of plays and C++ programs. But if you are just interested in running paragraphs of text, it is more common to use a **word** model, which represents the probability of the next word given the previous words, or a **token** model, where a token is something similar to a word. Sometimes a word is broken into several tokens; the word \"dogcatcher\" might become two tokens, \"dog\" and \"catcher.\" One or more characters of punctuation can also form a token. In the implementation below, `train_token_LM` and `generate_token_text` are almost the same as their charac ter-model counterparts, but they deal with a list of tokens rather than a string of characters (however, in the Counters that make up the model, the keys are formed by concatenating the tokens together, in part because lists can't be keys of dicts).\n", + "\n", + "One simple way of tokenizing a text is to break it up into alternating word and non-word characters; the function `tokenize` does that.But other tokenizers could be used if desired." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "import re\n", + "\n", + "TokenLanguageModel = LanguageModel # e.g. {'wherefore art thou ': Counter({'Romeo': 1})\n", + "\n", + "cat = ''.join\n", + "\n", + "def train_token_LM(tokens, order: int) -> TokenLanguageModel:\n", + " \"\"\"Train a character-level token language model of given order on the given tokens.\"\"\"\n", + " LM = TokenLanguageModel(Counter)\n", + " LM.order = order\n", + " history = []\n", + " for token in tokens:\n", + " LM[cat(history)][token] += 1\n", + " history = (history + [token])[-order:] \n", + " return LM\n", + "\n", + "def generate_token_text(LM: TokenLanguageModel, length=1000, tokens=()) -> str:\n", + " \"\"\"Generate a random text of `length` tokens, with an optional start, from `LM`.\"\"\"\n", + " tokens = list(tokens)\n", + " while len(tokens) < length:\n", + " history = cat(tokens[-LM.order:])\n", + " tokens.append(random_sample(LM[history]))\n", + " return cat(tokens)\n", + "\n", + "def tokenize(text: str) -> list: \n", + " \"\"\"Break text up into alternating word-character and non-word-character strings.\"\"\"\n", + " return re.findall(r'\\w+|\\W+', text)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "assert tokenize('wherefore art thou Romeo?') == ['wherefore', ' ', 'art', ' ', 'thou', ' ', 'Romeo', '?']\n", + "assert tokenize(''' */\n", + "int probe_irq_off(unsigned long val)\n", + "{''') == [' */\\n', 'int', ' ', 'probe_irq_off', '(', 'unsigned', ' ', 'long', ' ', 'val', ')\\n{']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can train a token model on the Shakespeare data. A model of order 6 keeps a history of three word and 3 non-word tokens (all concatenated together):" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "TLM = train_token_LM(tokenize(data), 6)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Counter({'Romeo': 1})" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "TLM['wherefore art thou ']" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Counter({'stars': 1, 'Grecian': 1})" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "TLM['not in our ']" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Counter({'life': 1, 'business': 1, 'dinner': 1, 'time': 1})" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "TLM['end of my ']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We see that the quality of the token models is similar to character models, and improves from 6 tokens to 8:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "First Citizen:\n", + "Before we proceed any further, hear me speak.\n", + "\n", + "CORIOLANUS:\n", + "Cut me to pieces, Volsces; men and lads,\n", + "Stain all your edges on me. Boy! false hound!\n", + "If you have told Diana's altar to protest\n", + "For aye austerity and single life.\n", + "\n", + "DEMETRIUS:\n", + "Relent, sweet Hermia: and, Lysander, yield\n", + "Thy crazed title to my certain right.\n", + "\n", + "LYSANDER:\n", + "You have her father's eyes up close as oak-\n", + "He thought 'twas witchcraft--but I am heart-burned an hour after.\n", + "\n", + "HERO:\n", + "He is the half part of a blessed man,\n", + "Left to be finished by such as she;\n", + "And she again wants nothing, to name want,\n", + "If want it be not gone already,\n", + "Even at that news he dies; and then the hearts\n", + "Of all the world was of my counsel\n", + "In my whole course of love, the tidings of her death:\n", + "And here he comes in the habit of a light wench: and thereof\n", + "comes that the wenches say 'God damn me;' that's as\n", + "much to say 'God make me a light. Know we this face or no?\n", + "Alas my friend and my dear hap to tell.\n", + "\n", + "FRIAR LAURENCE:\n", + "The grey-eyed morn smiles on the frowning night,\n", + "Chequering the eastern clouds with streaks of light,\n", + "And flecked darkness like a dream than an assurance\n", + "That my remembrance warrants. Had I not reason to prefer mine own?\n", + "\n", + "VALENTINE:\n", + "And I will chain these legs and arms of thine,\n", + "That hast by tyranny these many years, and yet\n", + "I know 'tis done,\n", + "Howe'er my haps, my joys were ne'er acquainted with their wards\n", + "Many a bounteous year must be employ'd?\n", + "\n", + "TITUS ANDRONICUS:\n", + "Tut, I have lost my life betimes\n", + "Than bring a burthen of dishonour home\n", + "By staying there so long till all were told,\n", + "The words would add more anguish than the wounds.\n", + "O valiant lord, the Duke of Buckingham; now, poor Edward Bohun:\n", + "Yet I am doubtful that you have said, my lord.\n", + "\n", + "FLAVIUS:\n", + "\n", + "TIMON:\n", + "Go you, sir, to spare me, till I have issue o' my body; for\n", + "they say barnes are blessings.\n", + "\n", + "COUNTESS:\n", + "Tell me thy reason why thou wilt marry.\n", + "\n", + "Clown:\n", + "My poor body, madam, requires it: I am driven on\n", + "by the flesh; and he must needs go in;\n", + "Her father will be angry: what hast thou done?\n", + "\n", + "HAMLET:\n", + "Nay, I know not the contents:\n", + "Phebe did write it.\n", + "\n", + "ROSALIND:\n", + "Come, come, you are a rare parrot-teacher.\n", + "\n", + "BEATRICE:\n", + "A bird of my tongue.\n", + "\n", + "Second Lord:\n", + "This is your devoted friend, sir, the manifold\n", + "linguist and the armipotent soldier.\n", + "\n", + "BERTRAM:\n", + "I could endure any thing before but a cat, and now\n", + "he's a mad yeoman that sees his son a gentleman\n", + "before him.\n", + "\n", + "KING LEAR:\n", + "To have a thousand loves,\n", + "A mother and a brother,\n", + "In quest of them, unhappy, lose myself.\n", + "Here comes the prince and Claudio hastily.\n", + "\n", + "DON PEDRO:\n", + "Good den, brother.\n", + "\n", + "DON JOHN:\n", + "If it please you: yet Count Claudio may hear; for\n", + "what I would \n" + ] + } + ], + "source": [ + "print(generate_token_text(TLM))" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "First Citizen:\n", + "Give me the Lord preserved me long:\n", + "To build me the fortunes, beyond his heart,\n", + "To stay the villain!\n", + "\n", + "Second Messenger\n", + "That no manner was I crept out of my place beneath.\n", + "\n", + "HAMLET:\n", + "Sir, here that be?\n", + "\n", + "Clown:\n", + "I would you have worn a visor! what costs they have engaol'd my tongue,\n", + "That more respective lenity,\n", + "To seem to under-bear. O, that's dragon-like, awhile.\n", + "\n", + "Hostess:\n", + "A pair so famous college of\n", + "wit-crackers of\n", + "manners, as you do assistance be only mean\n", + "For power,\n", + "Bending the ancient trade than you do this?\n", + "\n", + "BORACHIO:\n", + "Yea, every idle, nice custom 'gainst it:\n", + "We are to me a stool and dead men's son, sir.\n", + "\n", + "TITUS ANDRONICUS:\n", + "Follow thee again,\n", + "And make thee after. When shall thinking too liberal arts\n", + "With thought? I have but pinn'd with some mischance he's hurt i' the half-achieved,\n", + "As to be cut, and as leaky as an\n", + "unstanched thirst\n", + "York and Tartar's bower,\n", + "Whose wanton lust the senate.\n", + "But I shall they\n", + "yet look thee, my boy! thy face,\n", + "While you perceive\n", + "no truth and heath\n" + ] + } + ], + "source": [ + "print(generate_token_text(train_token_LM(data, 8)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## C++ Token Model\n", + "\n", + "Similar remarks hold for token models trained on C++ data:" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/*\n", + " * linux/kernel.h>\n", + "#include \n", + "#include \n", + "#include chip->irq_cpu_online(cpu)) {\n", + "\t\t/* created separates the end of the sym_name != '_')\n", + "\t\t\treturn;\n", + "}\n", + "\n", + "#ifdef COMPAT_RLIM_INFINITY) {\n", + "\t\trt_clear_integral.\n", + " *\n", + " * This gets called when:\n", + " * - an unknown object\\n\");\n", + "\t\treturn;\n", + "\n", + " again:\n", + "\tadd_time_stats(class);\n", + "\tprintk(\"\\nstack backtrace_seq_puts(s, \"\\ttype_len = 0;\n", + "\tint dead_cpu: Callback list\n", + " * - we are done.\n", + "\t */\n", + "\tif (!(torture_cleanup has been done or not.\n", + " *\n", + " * This is used to put_user(r.rlim_max;\n", + "}\n", + "\n", + "/* Clean it up and exit (not in\n", + " * case.\n", + "\t */\n", + "\tdown_write operations = {\n", + "\t.func\t\t\t= stack_trace(&trace_ops *op;\n", + "\tint len1;\n", + "\tint i;\n", + "\tint i;\n", + "\n", + "\tfor (thr = 0; thr < nr_threads; thr++) {\n", + "\t\t*q++ = ':';\n", + "\t*q++ = ':';\n", + "\t*q++ = hex_asc[(error % 10)];\n", + "\tpkt[3] = '\\0';\n", + "\t\t\tparse_error(ps, FILT_ERR_FIELD_NOT_FOUND:\n", + "\t\tif (DST != SRC) {\n", + "\t\t\titer++;\n", + "\t}\n", + "\terr2 = hib_wait_on_bio_chain);\n", + "\t\tswitch (m->opcode) {\n", + "\tcase CPU_DOWN_PREPARE, %CPU_ONLINE, &cs->flags &= ~PF_USED_ASYNC;\n", + "\n", + "\tif (e == FALLTHROUGH, env);\n", + "\n", + "\tif (!axp || axp->pid_count]);\n", + "\t\tcommand = s->command[s->count)\n", + "\t\treturn 1;\n", + "}\n", + "\n", + "int\n", + "_braille_register_begin(). If at least one signal. */\n", + "\tacct_account_idle_task(task, NULL);\n", + "\n", + "\tmutex_unlock();\n", + "\n", + "\t/* find program is distributed in the same CPU;\n", + "\t\t\t * other entities belongs\n", + " */\n", + "int hrtick_update_event_probe_ops = {\n", + "\t.handler_t trigger ops associated with interrupt.h>\n", + "#include \n", + "#include hash_entry(swap, offset)\n", + "{\n", + "\tcycle_t cycle_t T0, T1, delta;\n", + "}\n", + "\n", + "/*\n", + " * Old cruft\n", + " */\n", + "SYSCALL_DEFINE3(sched_granularity(void)\n", + "{\n", + "}\n", + "\n", + "#else\n", + "static inline int throttled_clock for running callbacks(struct perf_event_trigger_data: Trigger-specific module '%s' (not found, NULL);\n", + "\t\tbreak;\n", + "\tcase KDB_DB_SSBPT,\t/* Breakpoint error;\n", + "}\n", + "\n", + "/*\n", + " * kernel\t\t\t\tuser->seq = log_first_idx;\n", + "\telem->next = env->src_cpu, src_nid) * 3 / 4);\n", + "}\n", + "\n", + "ssize_t tbl_size, GFP_KERNEL);\n", + "\tif (error)\n", + "\t\terror = PR_TIMING_STATISTICAL)\n", + "\t\t\terr = -EPERM;\n", + "\t\tif (strncmp(cmp, \"no\", 2)) {\n", + "\t\tint same = 1;\n", + "\t}\n", + "\n", + "\t/* No-CBs CPU, then\n", + "\t\t * PPS freq wander (ns/s) */\n", + "\n", + "/* Resending the column corresponding elements[r].dl))\n", + "\t\t\tlargest representing\n", + " * to limit memory bitmaps - free the sigevent kevent;\n", + "\n", + "\t/* No need to handle failure.\n", + " */\n", + "static inline int is_hardlockup_panic)\n", + "\t\t\tpanic(\"Attempt to move forward\n", + " * @nodemask;\n", + "\tif (percpu_irq(unsigned int cpuset change.\n", + " */\n", + "void *trigger_ops,\n", + "\t\t.spd_release,\n", + "\t};\n", + "\tstruct rq *rq = rq_of_rt_rq(rt_se);\n", + "\n", + "\tif (bp->bp_enable_print,\n", + "};\n", + "EXPORT_SYMBOL(msecs_to_jiffies and returns filter.\n", + " *\n", + " * Create links from the\n", + "\t\t * not then unload or not.\n", + " *\n", + " * Those are no failure means event_trigger_free,\n", + "};\n", + "\n", + "static inline long __start_an\n" + ] + } + ], + "source": [ + "print(generate_token_text(train_token_LM(linux, 8), length=3000))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Character Models *are* Token Models\n", + "\n", + "Although it was pedagogically simpler to present the character models first; we could have skipped that and just shown the code for the token models. Then a character model is just a token model where the data has been \"tokenized\" so that each character is a separate token. We can show that the resulting models are exactly equal:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "train_LM(data, 4) == train_token_LM(data, 4) " ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -1294,7 +1461,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.9.12" } }, "nbformat": 4, diff --git a/ipynb/Sudoku.ipynb b/ipynb/Sudoku.ipynb index 4c631ee..42d38da 100644 --- a/ipynb/Sudoku.ipynb +++ b/ipynb/Sudoku.ipynb @@ -8,43 +8,40 @@ "\n", "# Solving Any Sudoku Puzzle\n", "\n", - "The rules of Sudoku are simple and finite: fill in the empty squares so that each column, row, and 3x3 box contains all the digits from 1 to 9:\n", + "The rules of Sudoku are simple and finite: fill in the empty squares in the puzzle so that each column, row, and 3×3 box contains all the digits from 1 to 9 with no repeats. For example:\n", "\n", - "|Puzzle|Solution|\n", - "|---|---|\n", - "|![](https://upload.wikimedia.org/wikipedia/commons/thumb/e/e0/Sudoku_Puzzle_by_L2G-20050714_standardized_layout.svg/361px-Sudoku_Puzzle_by_L2G-20050714_standardized_layout.svg.png)|![](https://upload.wikimedia.org/wikipedia/commons/thumb/1/12/Sudoku_Puzzle_by_L2G-20050714_solution_standardized_layout.svg/361px-Sudoku_Puzzle_by_L2G-20050714_solution_standardized_layout.svg.png)|\n", + "|Puzzle|-|Solution|\n", + "|---|--|---|\n", + "|![](https://upload.wikimedia.org/wikipedia/commons/thumb/e/e0/Sudoku_Puzzle_by_L2G-20050714_standardized_layout.svg/361px-Sudoku_Puzzle_by_L2G-20050714_standardized_layout.svg.png)|  |![](https://upload.wikimedia.org/wikipedia/commons/thumb/1/12/Sudoku_Puzzle_by_L2G-20050714_solution_standardized_layout.svg/361px-Sudoku_Puzzle_by_L2G-20050714_solution_standardized_layout.svg.png)|\n", "\n", - "This notebook develops a program to solve any Sudoku puzzle, in about 100 lines of Python. You can also see:\n", - " - The original 2006 version of this notebook, as a [raw HTML page](http://norvig.com/sudoku.html): \n", - " - It uses some outdated Python idioms; the Python code in this notebook is more modern.\n", - " - Thus, if you read this notebook, you don't need to see the older version.\n", - " - However, the disqus comments might be of some interest.\n", - " - [A Java program for Sudoku](SudokuJava.ipynb) that solves over 100,000 puzzles per second.\n", - " - [A Python program for Star Battle](StarBattle.ipynb), a related fill-in-the-grid puzzle game.\n", + "This notebook develops a program to solve any Sudoku puzzle, in about 80 lines of Python. You can also see:\n", + " - The [original 2006 version of this program](http://norvig.com/sudoku.html); it has some obsolete Python idioms.\n", + " - [A Java program for Sudoku](SudokuJava.ipynb) that is less clear but faster, solving over 100,000 puzzles per second.\n", + " - [A Python program for Star Battle](StarBattle.ipynb), a related fill-in-the-grid puzzle.\n", + " - [A Python program for KenKen](KenKen.ipynb), a related fill-in-the-grid puzzle.\n", "\n", "\n", "# Sudoku: Implementing Basic Concepts \n", "\n", - "Here are the key concepts and the Python implementation choices I made:\n", + "Here are the key concepts and the Python implementation choices I made. Types are in Uppercase and constants in lowercase:\n", "\n", "- **Digit**: the digits are the characters `'1'` to `'9'`. \n", "- **Digit set**: when several digits could fill a square, use `'123'` to mean 1, 2, or 3 are possible.\n", - "- **Rows**: by convention the 9 rows have labels `'A'` to `'I'` (top to bottom). \n", - "- **Columns**: by convention the 9 columns have labels `'1'` to `'9'` (left to right).\n", - "- **Boxes**: the 9 boxes are 3×3 squares within the grid (outlined with heavy black lines in the diagram). \n", + "- **rows**: by convention the 9 rows have labels `'A'` to `'I'` (top to bottom). \n", + "- **columns**: by convention the 9 columns have labels `'1'` to `'9'` (left to right).\n", "- **Square**: a square is named by the concatenation of its row and column labels, e.g. `'A9'` is the upper-rightmost square.\n", " - `squares` is a tuple of all 81 squares.\n", "- **Grid**: a grid of 81 squares is represented as a dict of `{Square: DigitSet}` such as `{'A9': '123', ...}`.\n", - " - `Fail` represents a grid that has no solution (e.g. if we guess wrong on the filler for some square).\n", - "- **Picture**: for input and output, a string *picture* of the grid is used (details described below).\n", - "- **Unit**: a unit is a row, column, or box; each unit is a list of 9 squares. \n", - " - `all_units` is a tuple of all 27 units.\n", - " - `units[s]` is a tuple of the 3 units that square `s` is a part of.\n", + "- **Boxes**: the 9 boxes are 3×3 squares within the grid (outlined with heavy black lines in the diagram). \n", + " - `all_boxes` is a list of all 9 boxes\n", + "- **Unit**: a unit is a row, column, or box; each unit is a tuple of 9 squares. \n", + " - `all_units` is a list of all 27 units.\n", + " - `units` is a dict such that `units[s]` is a tuple of the 3 units that square `s` is a part of.\n", "- **Peers**: the squares that share a unit are called peers. \n", - " - `peers[s]` is a set of 20 squares that are in some unit with `s`.\n", - "- **Solution**: A grid is a valid solution to a puzzle if:\n", - " - A square that was filled with a digit in the puzzle has the same digit in the solution.\n", - " - Every unit is filled with all nine digits, one to a square, no repeats." + " - `peers` is a dict such that `peers[s]` is a set of 20 squares that are in some unit with `s`.\n", + "- **None**: If a puzzle has no solution, we represent that with `None`.\n", + "- **Picture**: for input and output, a string is used to describe the grid (details described below).\n", + "- **Solution**: A grid is a valid solution to a puzzle if every unit is filled with all nine digits, one to a square, with no repeats, and each square that was filled with a digit in the puzzle has the same digit in the solution." ] }, { @@ -54,30 +51,30 @@ "outputs": [], "source": [ "import re\n", - "\n", - "DigitSet = str # e.g. '123'\n", - "Square = str # e.g. 'A9'\n", - "Picture = str # e.g. \"53..7....6..195....98....6.8...6...34..8.3..17...2...6.6....28....419..5....8..79\"\n", - "Grid = dict # E.g. {'A9': '123', ...}, a dict of {Square: DigitSet}\n", - "Fail = Grid() # The empty Grid is used to indicate failure to find a solution\n", + "from typing import Dict, Optional\n", "\n", "def cross(A, B) -> tuple:\n", " \"Cross product of strings in A and strings in B.\"\n", " return tuple(a + b for a in A for b in B)\n", "\n", + "Digit = str # e.g. '1'\n", "digits = '123456789'\n", + "DigitSet = str # e.g. '123'\n", "rows = 'ABCDEFGHI'\n", "cols = digits\n", + "Square = str # e.g. 'A9'\n", "squares = cross(rows, cols)\n", + "Grid = Dict[Square, DigitSet] # E.g. {'A9': '123', ...}\n", "all_boxes = [cross(rs, cs) for rs in ('ABC','DEF','GHI') for cs in ('123','456','789')]\n", "all_units = [cross(rows, c) for c in cols] + [cross(r, cols) for r in rows] + all_boxes\n", "units = {s: tuple(u for u in all_units if s in u) for s in squares}\n", "peers = {s: set().union(*units[s]) - {s} for s in squares}\n", + "Picture = str \n", "\n", "def is_solution(solution: Grid, puzzle: Grid) -> bool:\n", " \"Is this proposed solution to the puzzle actually valid?\"\n", - " return (solution is not Fail and\n", - " all(solution[s] == puzzle[s] for s in squares if len(puzzle[s]) == 1) and\n", + " return (solution is not None and\n", + " all(solution[s] in puzzle[s] for s in squares) and\n", " all({solution[s] for s in unit} == set(digits) for unit in all_units))" ] }, @@ -168,6 +165,827 @@ "peers['C2']" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Constraint Propagation\n", + "\n", + "Sudoku players know these two key strategies:\n", + "\n", + "1. If a square has only one possible digit, then **eliminate** that digit as a possibility for each of the square's peers.\n", + "2. If a unit has only one possible square that can hold a digit, then **fill** the square with the digit.\n", + "\n", + "This suggests two functions:\n", + "1. `eliminate(grid, s, d)` to eliminate digit `d` as a possibility for square `s`\n", + "2. `fill(grid, s, d)` to fill square `s` with the digit `d`. \n", + "\n", + "You might think that `fill` is the most fundamental operation, and that it could be implemented with `grid[s] = d`. But it turns out the code is simpler if we think of `eliminate` as the fundamental operation, and implement `fill` as \"eliminate all the digits except for `d` from `s`.\" Both functions mutate the grid they are passed, and return the mutated grid if they can perform the operation, or return `None` if there is a contradiction.\n", + "\n", + "We say that these two strategies constitute [constraint propagation](http://en.wikipedia.org/wiki/Constraint_satisfaction): \"constraint\" because they constrain what values can go in what squares, and \"propagation\" because a `fill` of one square can lead to an `eliminate` in other squares, which can in turn cause a `fill` of yet another square, and so on. \n", + "\n", + "The function `constrain` starts the whole process off. We initialize a new grid, `result`, (so that the original puzzle is not mutated), where `result` is filled with the known digits from the original grid. \n", + "\n", + "Here is the code:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "def constrain(grid) -> Grid:\n", + " \"Propagate constraints on a copy of grid to yield a new constrained Grid.\"\n", + " result: Grid = {s: digits for s in squares}\n", + " for s in grid:\n", + " if len(grid[s]) == 1:\n", + " fill(result, s, grid[s])\n", + " return result\n", + "\n", + "def fill(grid: Grid, s: Square, d: Digit) -> Optional[Grid]:\n", + " \"\"\"Eliminate all the digits except d from grid[s].\"\"\"\n", + " if grid[s] == d or all(eliminate(grid, s, d2) for d2 in grid[s] if d2 != d):\n", + " return grid\n", + " else:\n", + " return None\n", + "\n", + "def eliminate(grid: Grid, s: Square, d: Digit) -> Optional[Grid]:\n", + " \"\"\"Eliminate d from grid[s]; implement the two constraint propagation strategies.\"\"\"\n", + " if d not in grid[s]:\n", + " return grid ## Already eliminated\n", + " grid[s] = grid[s].replace(d, '')\n", + " if not grid[s]:\n", + " return None ## None: no legal digit left\n", + " elif len(grid[s]) == 1:\n", + " # 1. If a square has only one possible digit, then eliminate that digit as a possibility for each of the square's peers.\n", + " d2 = grid[s]\n", + " if not all(eliminate(grid, s2, d2) for s2 in peers[s]):\n", + " return None ## None: can't eliminate d2 from some square\n", + " for u in units[s]:\n", + " dplaces = [s for s in u if d in grid[s]]\n", + " # 2. If a unit has only one possible square that can hold a digit, then fill the square with the digit.\n", + " if not dplaces or (len(dplaces) == 1 and not fill(grid, dplaces[0], d)):\n", + " return None ## None: no place in u for d\n", + " return grid" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Input and Output: Pictures and Grids\n", + "\n", + "To show how constraint propagation works, we will need to read in a string representing a puzzle (what we call a `Picture`), convert that picture to a `Grid`, apply constraint propagation, convert the solution back to a picture, and display the picture. Conventions for `Picture`:\n", + "- A filled square is represented by a single digit, e.g.: `5`.\n", + "- A blank square is represented by a period: `.`.\n", + "- Spaces, newlines, and dashes/bars to separate boxes are included on output, but are optional (and ignored) on input.\n", + "- An uncertain square is represented by a DigitSet in braces, e.g.: `{123}`." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "def parse(picture) -> Grid:\n", + " \"\"\"Convert a Picture to a Grid.\"\"\"\n", + " vals = re.findall(r\"[.1-9]|[{][1-9]+[}]\", picture)\n", + " assert len(vals) == 81\n", + " return {s: digits if v == '.' else re.sub(r\"[{}]\", '', v) \n", + " for s, v in zip(squares, vals)}\n", + "\n", + "def picture(grid) -> Picture:\n", + " \"\"\"Convert a Grid to a Picture string, one line at a time.\"\"\"\n", + " if grid is None: \n", + " return \"None\"\n", + " def val(d: DigitSet) -> str: return '.' if d == digits else d if len(d) == 1 else '{' + d + '}'\n", + " maxwidth = max(len(val(grid[s])) for s in grid)\n", + " dash1 = '-' * (maxwidth * 3 + 2)\n", + " dash3 = '\\n' + '+'.join(3 * [dash1])\n", + " def cell(r, c): return val(grid[r + c]).center(maxwidth) + ('|' if c in '36' else ' ')\n", + " def line(r): return ''.join(cell(r, c) for c in cols) + (dash3 if r in 'CF' else '')\n", + " return '\\n'.join(map(line, rows))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here we parse a picture string into a grid, and then print the grid's picture:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5 3 .|. 7 .|. . . \n", + "6 . .|1 9 5|. . . \n", + ". 9 8|. . .|. 6 . \n", + "-----+-----+-----\n", + "8 . .|. 6 .|. . 3 \n", + "4 . .|8 . 3|. . 1 \n", + "7 . .|. 2 .|. . 6 \n", + "-----+-----+-----\n", + ". 6 .|. . .|2 8 . \n", + ". . .|4 1 9|. . 5 \n", + ". . .|. 8 .|. 7 9 \n" + ] + } + ], + "source": [ + "pic1 = \"53..7.... 6..195... .98....6. 8...6...3 4..8.3..1 7...2...6 .6....28. ...419..5 ....8..79\"\n", + "grid1 = parse(pic1)\n", + "print(picture(grid1))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In a grid, filled squares (like `A1`, the upper left corner of `grid1`) have one possible digit, and unfilled squares (like `A9`, the upper right corner of `grid1`) have all 9 possible digits:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'5'" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "grid1['A1']" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'123456789'" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "grid1['A9']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To see how this all works, let's look again at `grid1`:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5 3 .|. 7 .|. . . \n", + "6 . .|1 9 5|. . . \n", + ". 9 8|. . .|. 6 . \n", + "-----+-----+-----\n", + "8 . .|. 6 .|. . 3 \n", + "4 . .|8 . 3|. . 1 \n", + "7 . .|. 2 .|. . 6 \n", + "-----+-----+-----\n", + ". 6 .|. . .|2 8 . \n", + ". . .|4 1 9|. . 5 \n", + ". . .|. 8 .|. 7 9 \n" + ] + } + ], + "source": [ + "print(picture(grid1))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Constraint propagation will at some point consider the square `E5`, in the center of the center box. It is in a row with the digits 4, 8, 3, 1, and in a column with the digits 7, 9, 6, 2, 1, 8. If according to strategy 1 we call `eliminate(grid1, 'E5', d)` for each of these digits, then we're left with only one possible digit, `5`, for `E5`. Thus, we would next call `eliminate(grid1, s2, '5')` for each peer `s2` of 'E5'. This would lead to the square three columns to the left, `E2`, only having one remaining possible digit, `2`. Constraint propagation continues in this fashion.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Many puzzles can be completely solved by `constrain` alone, for example:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5 3 4|6 7 8|9 1 2 \n", + "6 7 2|1 9 5|3 4 8 \n", + "1 9 8|3 4 2|5 6 7 \n", + "-----+-----+-----\n", + "8 5 9|7 6 1|4 2 3 \n", + "4 2 6|8 5 3|7 9 1 \n", + "7 1 3|9 2 4|8 5 6 \n", + "-----+-----+-----\n", + "9 6 1|5 3 7|2 8 4 \n", + "2 8 7|4 1 9|6 3 5 \n", + "3 4 5|2 8 6|1 7 9 \n" + ] + } + ], + "source": [ + "print(picture(constrain(grid1)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "But for other puzzles, `constrain` is not enough:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4 . .|. . .|8 . 5 \n", + ". 3 .|. . .|. . . \n", + ". . .|7 . .|. . . \n", + "-----+-----+-----\n", + ". 2 .|. . .|. 6 . \n", + ". . .|. 8 .|4 . . \n", + ". . .|. 1 .|. . . \n", + "-----+-----+-----\n", + ". . .|6 . 3|. 7 . \n", + "5 . .|2 . .|. . . \n", + "1 . 4|. . .|. . . \n" + ] + } + ], + "source": [ + "grid2 = parse(\"4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......\")\n", + "print(picture(grid2))" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 4 {1679} {12679} | {139} {2369} {269} | 8 {1239} 5 \n", + " {26789} 3 {1256789}| {14589} {24569} {245689}| {12679} {1249} {124679} \n", + " {2689} {15689} {125689}| 7 {234569} {245689}| {12369} {12349} {123469} \n", + "-----------------------------+-----------------------------+-----------------------------\n", + " {3789} 2 {15789} | {3459} {34579} {4579} | {13579} 6 {13789} \n", + " {3679} {15679} {15679} | {359} 8 {25679} | 4 {12359} {12379} \n", + " {36789} 4 {56789} | {359} 1 {25679} | {23579} {23589} {23789} \n", + "-----------------------------+-----------------------------+-----------------------------\n", + " {289} {89} {289} | 6 {459} 3 | {1259} 7 {12489} \n", + " 5 {6789} 3 | 2 {479} 1 | {69} {489} {4689} \n", + " 1 {6789} 4 | {589} {579} {5789} | {23569} {23589} {23689} \n" + ] + } + ], + "source": [ + "print(picture(constrain(grid2)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We see that some progress has been made: the `grid2` puzzle has 17 squares filled in, and after constraint propagation, 20 are filled in. But that leaves 61 squares to go. We could try to add [additional constraint propagation strategies](https://bestofsudoku.com/sudoku-strategy), but there are many strategies, each one complicates the code, and even if we implemented them all, we wouldn't be *guaranteed* they could solve *any* puzzle. \n", + "\n", + "\n", + "# Search Strategy\n", + "\n", + "We need a strategy that will **search** through all possibile solutions, systematically and efficiently, until the solution is found. (A well-formed Sudoku puzzle has exactly one solution). \n", + "\n", + "A naive search can be slow. Consider a brute force search that tries every possible combination of digits. In the constrained `grid2` above, `A2` has 4 possibilities, `{1679}`, `A3` has 5, `{12679}`, and if we keep going and multiply all the choices together, [we get over 1038](http://www.google.com/search?hl=en&q=4*5*3*4*3*4*5*7*5*5*6*5*4*6*4*5*6*6*6*5*5*6*4*5*4*5*4*5*5*4*5*5*3*5*5*5*5*5*3*5*5*5*5*3*2*3*3*4*5*4*3*2*3*4*4*3*3*4*5*5*5) possibilities for the whole\n", + "puzzle. Suppose we have a\n", + "very efficient implementation that takes only ten CPU instructions to evaluate a\n", + "position, and that we have access to next-generation computers\n", + "with 1024–core CPUs running at 100 GHz, and let's say\n", + "we could afford a million of them, and while we're shopping, let's also pick up a time machine and go back 13 billion years to the origin of the universe and start our program running. We can then [estimate](http://www.google.com/search?&q=100+GHz/10+*+1024+*+1+million+*+13+billion+years+%2F+4.6e38+in+percent)\n", + "that we'd be almost 1% done with this one puzzle by now! Brute force is not the search you're looking for.\n", + "\n", + "It is too slow to do constraint propagation first and then search. A smarter strategy is to **interleave** constraint propagation and search as follows:\n", + " - If a previous step of the search on this branch returned `None`, then pass the `None` along.\n", + " - If there are no squares with multiple possibilities in the puzzle, then return the completed grid.\n", + " - Otherwise choose one of the not-yet-determined squares, *s*. \n", + " - For each digit *d* that could possibly fill square *s*:\n", + " - Initiate constraint propagation by calling `fill` on *s* and *d* and a copy of the grid.\n", + " - Recursively search for a solution to the puzzle from there.\n", + " - If the guess was right, we will get a solution.\n", + " - If the guess was wrong, continue on to the next digit *d*.\n", + "\n", + "Because we give up as soon as we get a contradiction, we examine far fewer possibilities than brute force search. Here is the implementation:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "def search(grid) -> Grid:\n", + " \"Depth-first search with constraint propagation to find a solution.\"\n", + " if grid is None: \n", + " return None\n", + " s = min((s for s in squares if len(grid[s]) > 1), \n", + " default=None, key=lambda s: len(grid[s]))\n", + " if s is None: # No squares with multiple possibilities; the search has succeeded\n", + " return grid\n", + " for d in grid[s]:\n", + " solution = search(fill(grid.copy(), s, d))\n", + " if solution:\n", + " return solution\n", + " return None" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are two choices we have to make in implementing the search:\n", + "**variable ordering** (which square do we try first?) and **value\n", + "ordering** (which digit do we try first for the square?). For\n", + "variable ordering, I used a common heuristic called **minimum\n", + "remaining values**, which means that we choose a \n", + "square with the minimum number of possible values. Why? Consider \n", + "`grid2` above. Suppose we chose `B3` first. It has 7\n", + "possibilities, `{1256789}`, so we'd expect to guess wrong with\n", + "probability 6/7. If instead we chose `G2`, which only has 2\n", + "possibilities, `{89}`, we'd expect to be wrong with probability\n", + "only 1/2. Thus we choose the square with the fewest possibilities and\n", + "the best chance of guessing right. For value ordering we won't do\n", + "anything special; we'll consider the digits in numeric order.\n", + "\n", + "Note we create a new **copy** of `grid` for each recursive call to `search`. This way\n", + "each branch of the search tree is independent, and mutations to `grid` done by constraint propagation in one branch do not confuse other branches. When it is time to back up and try a different digit, we have the original unaltered `grid` ready to go.\n", + "\n", + "We could call `search` directly, but I'll define the helper function `solve_puzzles(puzzles)` to call `constrain` and then `search` on each puzzle, and then verify that the solution is correct with `is_solution`, and finally print the puzzle and the solution side by side:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "def solve_puzzles(puzzles, verbose=True) -> int:\n", + " \"Solve and verify each puzzle, and if `verbose`, print puzzle and solution.\"\n", + " for puzzle in puzzles:\n", + " solution = search(constrain(puzzle))\n", + " assert is_solution(solution, puzzle)\n", + " if verbose:\n", + " print_side_by_side('\\nPuzzle:\\n' + picture(puzzle), \n", + " '\\nSolution:\\n' + picture(solution))\n", + " return len(puzzles)\n", + "\n", + "def print_side_by_side(left, right, width=20):\n", + " \"\"\"Print two strings side-by-side, line-by-line, each side `width` wide.\"\"\"\n", + " for L, R in zip(left.splitlines(), right.splitlines()):\n", + " print(L.ljust(width), R.ljust(width)) " + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \n", + "Puzzle: Solution: \n", + "5 3 .|. 7 .|. . . 5 3 4|6 7 8|9 1 2 \n", + "6 . .|1 9 5|. . . 6 7 2|1 9 5|3 4 8 \n", + ". 9 8|. . .|. 6 . 1 9 8|3 4 2|5 6 7 \n", + "-----+-----+----- -----+-----+----- \n", + "8 . .|. 6 .|. . 3 8 5 9|7 6 1|4 2 3 \n", + "4 . .|8 . 3|. . 1 4 2 6|8 5 3|7 9 1 \n", + "7 . .|. 2 .|. . 6 7 1 3|9 2 4|8 5 6 \n", + "-----+-----+----- -----+-----+----- \n", + ". 6 .|. . .|2 8 . 9 6 1|5 3 7|2 8 4 \n", + ". . .|4 1 9|. . 5 2 8 7|4 1 9|6 3 5 \n", + ". . .|. 8 .|. 7 9 3 4 5|2 8 6|1 7 9 \n", + " \n", + "Puzzle: Solution: \n", + "4 . .|. . .|8 . 5 4 1 7|3 6 9|8 2 5 \n", + ". 3 .|. . .|. . . 6 3 2|1 5 8|9 4 7 \n", + ". . .|7 . .|. . . 9 5 8|7 2 4|3 1 6 \n", + "-----+-----+----- -----+-----+----- \n", + ". 2 .|. . .|. 6 . 8 2 5|4 3 7|1 6 9 \n", + ". . .|. 8 .|4 . . 7 9 1|5 8 6|4 3 2 \n", + ". . .|. 1 .|. . . 3 4 6|9 1 2|7 5 8 \n", + "-----+-----+----- -----+-----+----- \n", + ". . .|6 . 3|. 7 . 2 8 9|6 4 3|5 7 1 \n", + "5 . .|2 . .|. . . 5 7 3|2 9 1|6 8 4 \n", + "1 . 4|. . .|. . . 1 6 4|8 7 5|2 9 3 \n" + ] + }, + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "solve_puzzles([grid1, grid2])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We see that `search` is effective at solving the previously-unsolved `grid2`.\n", + "\n", + "Computer scientists call this a [depth-first search](http://en.wikipedia.org/wiki/Depth-first_search) because it (recursively) considers all possible continuations from the grid with *d* in square *s* before it backs up and considers a different digit in *s*. Sudoku players call this the [Nishio strategy](https://www.sudokuonline.io/tips/advanced-sudoku-strategies), after professional puzzle player Tetsuya Nishio, although Nishio only applied it when there were just two remaining possibilities for a square. We can apply it with any number of possibilities; we can even find a solution for the completely empty puzzle where every square has 9 possibilities:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \n", + "Puzzle: Solution: \n", + ". . .|. . .|. . . 1 2 3|4 5 6|7 8 9 \n", + ". . .|. . .|. . . 4 5 6|7 8 9|1 2 3 \n", + ". . .|. . .|. . . 7 8 9|1 2 3|4 5 6 \n", + "-----+-----+----- -----+-----+----- \n", + ". . .|. . .|. . . 2 3 1|6 7 4|8 9 5 \n", + ". . .|. . .|. . . 8 7 5|9 1 2|3 6 4 \n", + ". . .|. . .|. . . 6 9 4|5 3 8|2 1 7 \n", + "-----+-----+----- -----+-----+----- \n", + ". . .|. . .|. . . 3 1 7|2 6 5|9 4 8 \n", + ". . .|. . .|. . . 5 4 2|8 9 7|6 3 1 \n", + ". . .|. . .|. . . 9 6 8|3 4 1|5 7 2 \n" + ] + }, + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "empty = parse('.' * 81)\n", + " \n", + "solve_puzzles([empty])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Verifying the Program\n", + "\n", + "We had success in solving `grid1`, `grid2`, and `empty`, but we are left with some questions:\n", + "- Can the program solve *any* puzzle? \n", + " - We'll test it on some more `puzzles`, taken from some files. The more we test, the more confidence we have.\n", + " - Theoretically, the program should be able to solve any puzzle, but we haven't determined a bound on how long it would take.\n", + "- Are the components of the program correct? \n", + " - We'll run some `unit_tests` to give us some confidence/ There should be more tests.\n", + "- How long does it take to solve a puzzle?\n", + " - We can measure that with the `%time` magic command.\n", + " - For a more complete investigation of run times, see the [Java version](SudokuJava.ipynb)." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'pass'" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def unit_tests():\n", + " \"A suite of unit tests.\"\n", + " assert len(squares) == 81\n", + " assert len(all_units) == 27\n", + " assert cross('AB', '12') == ('A1', 'A2', 'B1', 'B2')\n", + " for s in squares:\n", + " assert len(units[s]) == 3\n", + " assert len(peers[s]) == 20\n", + " assert units['C2'] == (('A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'I2'),\n", + " ('C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9'),\n", + " ('A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3'))\n", + " assert peers['C2'] == {'A2', 'B2', 'D2', 'E2', 'F2', 'G2', 'H2', 'I2',\n", + " 'C1', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9',\n", + " 'A1', 'A3', 'B1', 'B3'}\n", + " return 'pass'\n", + "\n", + "def parse_grids(pictures):\n", + " \"\"\"Parse an iterable of picture lines into a list of grids.\"\"\"\n", + " return [parse(p) for p in pictures if p]\n", + "\n", + "hardest = parse_grids(open('hardest.txt'))\n", + "grids10k = parse_grids(open('sudoku10k.txt'))\n", + "\n", + "unit_tests()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can solve 10,000 puzzles, verifying that each solution is correct (but not printing them):" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 27.2 s, sys: 17.1 ms, total: 27.2 s\n", + "Wall time: 27.4 s\n" + ] + }, + { + "data": { + "text/plain": [ + "10000" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%time solve_puzzles(grids10k, verbose=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "That took less than 3 milliseconds per puzzle, or over 350 puzzles per second. (The [Java version](SudokuJava.ipynb) does over 100,000 puzzles per second. It benefits from being able to run 20 threads in parallel, from using more efficient data structures, and from the Java JVM being more efficient than Python's bytecode interpreter.)\n", + "\n", + "The ten allegedly hardest puzzles also take about 3 milliseconds per puzzle on average:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 33.8 ms, sys: 1.08 ms, total: 34.9 ms\n", + "Wall time: 34.3 ms\n" + ] + }, + { + "data": { + "text/plain": [ + "10" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%time solve_puzzles(hardest, verbose=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see the puzzles and their solutions:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \n", + "Puzzle: Solution: \n", + "8 5 .|. . 2|4 . . 8 5 9|6 1 2|4 3 7 \n", + "7 2 .|. . .|. . 9 7 2 3|8 5 4|1 6 9 \n", + ". . 4|. . .|. . . 1 6 4|3 7 9|5 2 8 \n", + "-----+-----+----- -----+-----+----- \n", + ". . .|1 . 7|. . 2 9 8 6|1 4 7|3 5 2 \n", + "3 . 5|. . .|9 . . 3 7 5|2 6 8|9 1 4 \n", + ". 4 .|. . .|. . . 2 4 1|5 9 3|7 8 6 \n", + "-----+-----+----- -----+-----+----- \n", + ". . .|. 8 .|. 7 . 4 3 2|9 8 1|6 7 5 \n", + ". 1 7|. . .|. . . 6 1 7|4 2 5|8 9 3 \n", + ". . .|. 3 6|. 4 . 5 9 8|7 3 6|2 4 1 \n", + " \n", + "Puzzle: Solution: \n", + ". . 5|3 . .|. . . 1 4 5|3 2 7|6 9 8 \n", + "8 . .|. . .|. 2 . 8 3 9|6 5 4|1 2 7 \n", + ". 7 .|. 1 .|5 . . 6 7 2|9 1 8|5 4 3 \n", + "-----+-----+----- -----+-----+----- \n", + "4 . .|. . 5|3 . . 4 9 6|1 8 5|3 7 2 \n", + ". 1 .|. 7 .|. . 6 2 1 8|4 7 3|9 5 6 \n", + ". . 3|2 . .|. 8 . 7 5 3|2 9 6|4 8 1 \n", + "-----+-----+----- -----+-----+----- \n", + ". 6 .|5 . .|. . 9 3 6 7|5 4 2|8 1 9 \n", + ". . 4|. . .|. 3 . 9 8 4|7 6 1|2 3 5 \n", + ". . .|. . 9|7 . . 5 2 1|8 3 9|7 6 4 \n", + " \n", + "Puzzle: Solution: \n", + "1 2 .|. 4 .|. . . 1 2 8|5 4 7|6 3 9 \n", + ". . 5|. 6 9|. 1 . 3 4 5|8 6 9|2 1 7 \n", + ". . 9|. . .|5 . . 6 7 9|2 1 3|5 4 8 \n", + "-----+-----+----- -----+-----+----- \n", + ". . .|. . .|. 7 . 9 1 2|4 8 6|3 7 5 \n", + "7 . .|. 5 2|. 9 . 7 8 4|3 5 2|1 9 6 \n", + ". 3 .|. . .|. . 2 5 3 6|7 9 1|4 8 2 \n", + "-----+-----+----- -----+-----+----- \n", + ". 9 .|6 . .|. 5 . 8 9 1|6 2 4|7 5 3 \n", + "4 . .|9 . .|8 . 1 4 6 7|9 3 5|8 2 1 \n", + ". . 3|. . .|9 . 4 2 5 3|1 7 8|9 6 4 \n", + " \n", + "Puzzle: Solution: \n", + ". . .|5 7 .|. 3 . 6 2 4|5 7 8|1 3 9 \n", + "1 . .|. . .|. 2 . 1 3 5|4 9 6|8 2 7 \n", + "7 . .|. 2 3|4 . . 7 8 9|1 2 3|4 5 6 \n", + "-----+-----+----- -----+-----+----- \n", + ". . .|. 8 .|. . 4 2 1 6|3 8 5|7 9 4 \n", + ". . 7|. . 4|. . . 8 5 7|9 6 4|2 1 3 \n", + "4 9 .|. . .|6 . 5 4 9 3|2 1 7|6 8 5 \n", + "-----+-----+----- -----+-----+----- \n", + ". 4 2|. . .|3 . . 9 4 2|6 5 1|3 7 8 \n", + ". . .|7 . .|9 . . 5 6 8|7 3 2|9 4 1 \n", + ". . 1|8 . .|. . . 3 7 1|8 4 9|5 6 2 \n", + " \n", + "Puzzle: Solution: \n", + "1 . .|. . 7|. 9 . 1 6 2|8 5 7|4 9 3 \n", + ". 3 .|. 2 .|. . 8 5 3 4|1 2 9|6 7 8 \n", + ". . 9|6 . .|5 . . 7 8 9|6 4 3|5 2 1 \n", + "-----+-----+----- -----+-----+----- \n", + ". . 5|3 . .|9 . . 4 7 5|3 1 2|9 8 6 \n", + ". 1 .|. 8 .|. . 2 9 1 3|5 8 6|7 4 2 \n", + "6 . .|. . 4|. . . 6 2 8|7 9 4|1 3 5 \n", + "-----+-----+----- -----+-----+----- \n", + "3 . .|. . .|. 1 . 3 5 6|4 7 8|2 1 9 \n", + ". 4 .|. . .|. . 7 2 4 1|9 3 5|8 6 7 \n", + ". . 7|. . .|3 . . 8 9 7|2 6 1|3 5 4 \n", + " \n", + "Puzzle: Solution: \n", + "1 . .|. 3 4|. 8 . 1 5 2|9 3 4|6 8 7 \n", + ". . .|8 . .|5 . . 7 6 3|8 2 1|5 4 9 \n", + ". . 4|. 6 .|. 2 1 9 8 4|5 6 7|3 2 1 \n", + "-----+-----+----- -----+-----+----- \n", + ". 1 8|. . .|. . . 6 1 8|4 9 3|2 7 5 \n", + "3 . .|1 . 2|. . 6 3 7 5|1 8 2|4 9 6 \n", + ". . .|. . .|8 1 . 2 4 9|7 5 6|8 1 3 \n", + "-----+-----+----- -----+-----+----- \n", + "5 2 .|. 7 .|9 . . 5 2 1|3 7 8|9 6 4 \n", + ". . 6|. . 9|. . . 4 3 6|2 1 9|7 5 8 \n", + ". 9 .|6 4 .|. . 2 8 9 7|6 4 5|1 3 2 \n", + " \n", + "Puzzle: Solution: \n", + ". . .|9 2 .|. . . 3 8 7|9 2 6|4 1 5 \n", + ". . 6|8 . 3|. . . 5 4 6|8 1 3|9 7 2 \n", + "1 9 .|. 7 .|. . 6 1 9 2|4 7 5|8 3 6 \n", + "-----+-----+----- -----+-----+----- \n", + "2 3 .|. 4 .|1 . . 2 3 5|7 4 9|1 6 8 \n", + ". . 1|. . .|7 . . 9 6 1|2 5 8|7 4 3 \n", + ". . 8|. 3 .|. 2 9 4 7 8|6 3 1|5 2 9 \n", + "-----+-----+----- -----+-----+----- \n", + "7 . .|. 8 .|. 9 1 7 5 4|3 8 2|6 9 1 \n", + ". . .|5 . 7|2 . . 6 1 3|5 9 7|2 8 4 \n", + ". . .|. 6 4|. . . 8 2 9|1 6 4|3 5 7 \n", + " \n", + "Puzzle: Solution: \n", + ". 6 .|5 . 4|. 3 . 8 6 9|5 7 4|1 3 2 \n", + "1 . .|. 9 .|. . 8 1 2 4|3 9 6|7 5 8 \n", + ". . .|. . .|. . . 3 7 5|1 2 8|6 9 4 \n", + "-----+-----+----- -----+-----+----- \n", + "9 . .|. 5 .|. . 6 9 3 2|8 5 7|4 1 6 \n", + ". 4 .|6 . 2|. 7 . 5 4 1|6 3 2|8 7 9 \n", + "7 . .|. 4 .|. . 5 7 8 6|9 4 1|3 2 5 \n", + "-----+-----+----- -----+-----+----- \n", + ". . .|. . .|. . . 2 1 7|4 6 9|5 8 3 \n", + "4 . .|. 8 .|. . 1 4 9 3|7 8 5|2 6 1 \n", + ". 5 .|2 . 3|. 4 . 6 5 8|2 1 3|9 4 7 \n", + " \n", + "Puzzle: Solution: \n", + "7 . .|. . .|4 . . 7 9 8|6 3 5|4 2 1 \n", + ". 2 .|. 7 .|. 8 . 1 2 6|9 7 4|5 8 3 \n", + ". . 3|. . 8|. 7 9 4 5 3|2 1 8|6 7 9 \n", + "-----+-----+----- -----+-----+----- \n", + "9 . .|5 . .|3 . . 9 7 2|5 8 6|3 1 4 \n", + ". 6 .|. 2 .|. 9 . 5 6 4|1 2 3|8 9 7 \n", + ". . 1|. 9 7|. . 6 3 8 1|4 9 7|2 5 6 \n", + "-----+-----+----- -----+-----+----- \n", + ". . .|3 . .|9 . . 6 1 7|3 5 2|9 4 8 \n", + ". 3 .|. 4 .|. 6 . 8 3 5|7 4 9|1 6 2 \n", + ". . 9|. . 1|. 3 5 2 4 9|8 6 1|7 3 5 \n", + " \n", + "Puzzle: Solution: \n", + ". . .|. 7 .|. 2 . 5 9 4|8 7 6|1 2 3 \n", + "8 . .|. . .|. . 6 8 2 3|9 1 4|7 5 6 \n", + ". 1 .|2 . 5|. . . 6 1 7|2 3 5|8 9 4 \n", + "-----+-----+----- -----+-----+----- \n", + "9 . 5|4 . .|. . 8 9 6 5|4 2 1|3 7 8 \n", + ". . .|. . .|. . . 7 8 1|6 5 3|9 4 2 \n", + "3 . .|. . 8|5 . 1 3 4 2|7 9 8|5 6 1 \n", + "-----+-----+----- -----+-----+----- \n", + ". . .|3 . 2|. 8 . 1 5 9|3 4 2|6 8 7 \n", + "4 . .|. . .|. . 9 4 3 6|5 8 7|2 1 9 \n", + ". 7 .|. 6 .|. . . 2 7 8|1 6 9|4 3 5 \n" + ] + }, + { + "data": { + "text/plain": [ + "10" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "solve_puzzles(hardest)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -196,784 +1014,6 @@ " - Efficient, but for debugging it is clear that `C2` is in the third row and second column; it is not obvious that `19` is.\n" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Input and Output: Pictures and Grids\n", - "\n", - "We will need to convert between a `Picture` (used for input/output) and a `Grid` (used for computation). Conventions for `Picture`:\n", - "- A filled square is represented by a single digit, e.g.: `5`.\n", - "- A blank square is represented by a period: `.`.\n", - "- Spaces, newlines, and dashes/bars to separate boxes are included on output, and ignored on input.\n", - "- An uncertain square is represented by a DigitSet in braces, e.g.: `{123}`.\n", - " - This `'{123}'` notation never occurs in a standard Sudoku puzzle, but it is what `picture(grid)` produces for a grid that is partially solved, so I thought it was important to be able to read that format back in with `parse(picture)`.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "def parse(picture) -> Grid:\n", - " \"\"\"Convert a Picture to a Grid.\"\"\"\n", - " vals = re.findall(r\"[.1-9]|[{][1-9]+[}]\", picture)\n", - " assert len(vals) == 81\n", - " return {s: digits if v == '.' else re.sub(r\"[{}]\", '', v) \n", - " for s, v in zip(squares, vals)}\n", - "\n", - "def picture(grid) -> Picture:\n", - " \"\"\"Convert a Grid to a Picture.\"\"\"\n", - " if grid is Fail: \n", - " return \"Fail\"\n", - " def val(d: DigitSet) -> str: return '.' if d == digits else d if len(d) == 1 else '{' + d + '}'\n", - " width = max(len(val(grid[s])) for s in grid)\n", - " dash = '\\n' + '+'.join(['-' * (width * 3 + 2)] * 3) + ' '\n", - " def cell(r, c): return val(grid[r + c]).center(width) + ('|' if c in '36' else ' ')\n", - " def line(r): return ''.join(cell(r, c) for c in cols) + (dash if r in 'CF' else '')\n", - " return '\\n'.join(map(line, rows))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here we parse a picture string into a grid, and then print the grid's picture:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "5 3 .|. 7 .|. . . \n", - "6 . .|1 9 5|. . . \n", - ". 9 8|. . .|. 6 . \n", - "-----+-----+----- \n", - "8 . .|. 6 .|. . 3 \n", - "4 . .|8 . 3|. . 1 \n", - "7 . .|. 2 .|. . 6 \n", - "-----+-----+----- \n", - ". 6 .|. . .|2 8 . \n", - ". . .|4 1 9|. . 5 \n", - ". . .|. 8 .|. 7 9 \n" - ] - } - ], - "source": [ - "grid1 = parse(\"53..7.... 6..195... .98....6. 8...6...3 4..8.3..1 7...2...6 .6....28. ...419..5 ....8..79\")\n", - "print(picture(grid1))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In a grid, filled squares have one possible digit, and unfilled squares have all 9 possible digits:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'5'" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "grid1['A1']" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'123456789'" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "grid1['A3']" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Constraint Propagation\n", - "\n", - "Sudoku players know these two key strategies:\n", - "\n", - "1. If a square has only one possible digit, then **eliminate** that digit from the square's peers.\n", - "2. If a unit has only one possible square that can hold a digit, then **fill** the square with the digit.\n", - "\n", - "This suggests two functions:\n", - "1. `eliminate(grid, s, d)` to eliminate digit `d` as a possibility for square `s`\n", - "2. `fill(grid, s, d)` to fill square `s` with the digit `d`. \n", - "\n", - "You might think that `fill` is the most fundamental operation, and that it could be implemented with `grid[s] = d`. But the code is simpler if we think of `eliminate` as the fundamental operation, and implement `fill` as \"eliminate all the digits except for `d` from `s`.\" Both functions mutate the grid they are passed, and return the mutated grid if they can perform the operation, or return `Fail` if there is a contradiction.\n", - "\n", - "We say that these two strategies constitute [constraint propagation](http://en.wikipedia.org/wiki/Constraint_satisfaction): \"constraint\" because they constrain what values can go in what squares, and \"propagation\" because a `fill` of one square can lead to `eliminate` in other squares, which can in turn cause a `fill` of yet another square, and so on. \n", - "\n", - "Here is the code:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "def fill(grid, s, d) -> Grid:\n", - " \"\"\"Eliminate all the other digits (except d) from grid[s].\"\"\"\n", - " if grid[s] == d or all(eliminate(grid, s, d2) for d2 in grid[s] if d2 != d):\n", - " return grid\n", - " else:\n", - " return Fail\n", - "\n", - "def eliminate(grid, s, d) -> Grid:\n", - " \"\"\"Eliminate d from grid[s]; implement the two constraint propagation strategies.\"\"\"\n", - " if d not in grid[s]:\n", - " return grid ## Already eliminated\n", - " grid[s] = grid[s].replace(d, '')\n", - " if not grid[s]:\n", - " return Fail ## Fail: no legal digit left\n", - " elif len(grid[s]) == 1:\n", - " # 1. If a square has only one possible digit, then eliminate that digit from the square's peers.\n", - " d2 = grid[s]\n", - " if not all(eliminate(grid, s2, d2) for s2 in peers[s]):\n", - " return Fail ## Fail: can't eliminate d2 from some square\n", - " for u in units[s]:\n", - " dplaces = [s for s in u if d in grid[s]]\n", - " # 2. If a unit has only one possible square that can hold a digit, then fill the square with the digit.\n", - " if not dplaces or (len(dplaces) == 1 and not fill(grid, dplaces[0], d)):\n", - " return Fail ## Fail: no place in u for d\n", - " return grid" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To see how this all works, let's look again at `grid1`:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "5 3 .|. 7 .|. . . \n", - "6 . .|1 9 5|. . . \n", - ". 9 8|. . .|. 6 . \n", - "-----+-----+----- \n", - "8 . .|. 6 .|. . 3 \n", - "4 . .|8 . 3|. . 1 \n", - "7 . .|. 2 .|. . 6 \n", - "-----+-----+----- \n", - ". 6 .|. . .|2 8 . \n", - ". . .|4 1 9|. . 5 \n", - ". . .|. 8 .|. 7 9 \n" - ] - } - ], - "source": [ - "print(picture(grid1))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Constraint propagation will at some point consider the square `E5`, in the center of the center box. It is in a row with the digits 4, 8, 3, 1, and in a column with the digits 7, 9, 6, 2, 1, 8. If according to strategy 1 we call `eliminate(grid1, 'E5', d)` for each of these digits, then we're left with only one possible digit, `5`, for `E5`. Thus, we would next call `eliminate(grid1, s2, '5')` for each peer `s2` of 'E5'. This would lead to the square three columns to the left, `E2`, only having one remaining possible digit, `2`. Constraint propagation continues in this fashion.\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The function `constrain` returns the result of initiating constraint propagation on a grid. So that the original puzzle is not mutated, we create a new `constrained` grid that initially has all possible digits for each square, but then is filled with the known digits from the original grid." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def constrain(grid) -> Grid:\n", - " \"Propagate constraints on a copy of grid to yield a new constrained Grid.\"\n", - " constrained: Grid = {s: digits for s in squares}\n", - " for s in grid:\n", - " d = grid[s]\n", - " if len(d) == 1:\n", - " fill(constrained, s, d)\n", - " return constrained" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Many puzzles can be solved by `constrain` alone, for example:" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "5 3 4|6 7 8|9 1 2 \n", - "6 7 2|1 9 5|3 4 8 \n", - "1 9 8|3 4 2|5 6 7 \n", - "-----+-----+----- \n", - "8 5 9|7 6 1|4 2 3 \n", - "4 2 6|8 5 3|7 9 1 \n", - "7 1 3|9 2 4|8 5 6 \n", - "-----+-----+----- \n", - "9 6 1|5 3 7|2 8 4 \n", - "2 8 7|4 1 9|6 3 5 \n", - "3 4 5|2 8 6|1 7 9 \n" - ] - } - ], - "source": [ - "print(picture(constrain(grid1)))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But for other puzzles, `constrain` is not enough:" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " 4 {1679} {12679} | {139} {2369} {269} | 8 {1239} 5 \n", - " {26789} 3 {1256789}| {14589} {24569} {245689}| {12679} {1249} {124679} \n", - " {2689} {15689} {125689}| 7 {234569} {245689}| {12369} {12349} {123469} \n", - "-----------------------------+-----------------------------+----------------------------- \n", - " {3789} 2 {15789} | {3459} {34579} {4579} | {13579} 6 {13789} \n", - " {3679} {15679} {15679} | {359} 8 {25679} | 4 {12359} {12379} \n", - " {36789} 4 {56789} | {359} 1 {25679} | {23579} {23589} {23789} \n", - "-----------------------------+-----------------------------+----------------------------- \n", - " {289} {89} {289} | 6 {459} 3 | {1259} 7 {12489} \n", - " 5 {6789} 3 | 2 {479} 1 | {69} {489} {4689} \n", - " 1 {6789} 4 | {589} {579} {5789} | {23569} {23589} {23689} \n" - ] - } - ], - "source": [ - "grid2 = parse(\"4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......\")\n", - "\n", - "print(picture(constrain(grid2)))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We see that some progress has been made: the original puzzle has 17 squares filled in, and after constraint propagation, 20 are filled in. But that leaves 61 squares to go. We could try to add [additional constraint propagation strategies](https://bestofsudoku.com/sudoku-strategy), but there are many strategies, each one complicates the code, and even if we implemented them all, we wouldn't be *guaranteed* they could solve *any* puzzle. \n", - "\n", - "\n", - "# Search Strategy\n", - "\n", - "We need a strategy that will **search** through all possibile solutions, systematically and efficiently, until the solution is found. (A well-formed Sudoku puzzle has exactly one solution). \n", - "\n", - "A naive search can be slow. Consider a brute force search that tries every possible combination of digits. In the constrained `grid2` above, `A2` has 4 possibilities, `{1679}`, `A3` has 5, `{12679}`, and if we keep going and multiply all the choices together, [we get over 1038](http://www.google.com/search?hl=en&q=4*5*3*4*3*4*5*7*5*5*6*5*4*6*4*5*6*6*6*5*5*6*4*5*4*5*4*5*5*4*5*5*3*5*5*5*5*5*3*5*5*5*5*3*2*3*3*4*5*4*3*2*3*4*4*3*3*4*5*5*5) possibilities for the whole\n", - "puzzle. Suppose we have a\n", - "very efficient implementation that takes only ten CPU instructions to evaluate a\n", - "position, and that we have access to next-generation computers\n", - "with 1024–core CPUs running at 100 GHz, and let's say\n", - "we could afford a million of them, and while we're shopping, let's also pick up a time machine and go back 13 billion years to the origin of the universe and start our program running. We can then [estimate](http://www.google.com/search?&q=100+GHz/10+*+1024+*+1+million+*+13+billion+years+%2F+4.6e38+in+percent)\n", - "that we'd be almost 1% done with this one puzzle by now. Brute force is not the search you're looking for.\n", - "\n", - "A smarter search strategy is to **interleave** constraint propagation and systematic guessing as follows:\n", - " - If the current grid contains a contradiction, don't try more combinations; return failure.\n", - " - If there are no unfilled squares in the puzzle, then stop and return the succesful solution.\n", - " - Otherwise choose some unfilled square, *s*. \n", - " - Guess that some digit *d* fills square *s*. \n", - " - Call `fill` on *s* and *d* to initiate constraint propagation.\n", - " - Recursively search for a solution to the puzzle from there.\n", - " - If the guess was right, we will get a solution.\n", - " - If the guess was wrong, back up and guess a different digit.\n", - " - Because we give up as soon as we get a contradiction, we examine far fewer possibilities than brute force search.\n", - " \n", - "Here is the implementation:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "def search(grid) -> Grid:\n", - " \"Depth-first search with constraint propagation (`fill`) to find a solution.\"\n", - " if grid is Fail: \n", - " return Fail\n", - " unfilled = [s for s in squares if len(grid[s]) > 1]\n", - " if not unfilled: \n", - " return grid\n", - " s = min(unfilled, key=lambda s: len(grid[s]))\n", - " for d in grid[s]:\n", - " solution = search(fill(grid.copy(), s, d))\n", - " if solution:\n", - " return solution\n", - " return Fail" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There are two choices we have to make in implementing the search:\n", - "**variable ordering** (which square do we try first?) and **value\n", - "ordering** (which digit do we try first for the square?). For\n", - "variable ordering, I used a common heuristic called **minimum\n", - "remaining values**, which means that we choose a \n", - "square with the minimum number of possible values. Why? Consider \n", - "`grid2` above. Suppose we chose `B3` first. It has 7\n", - "possibilities, `{1256789}`, so we'd expect to guess wrong with\n", - "probability 6/7. If instead we chose `G2`, which only has 2\n", - "possibilities, `{89}`, we'd expect to be wrong with probability\n", - "only 1/2. Thus we choose the square with the fewest possibilities and\n", - "the best chance of guessing right. For value ordering we won't do\n", - "anything special; we'll consider the digits in numeric order.\n", - "\n", - "Note we create a new **copy** of `grid` for each recursive call to `search`. This way\n", - "each branch of the search tree is independent, and mutations to `grid` done by constraint propagation in one branch do not confuse other branches. When it is time to back up and try a different digit, we have the original unaltered `grid` ready to go.\n", - "\n", - "We could call `search` directly, but I'll define the helper function `solve(puzzles)` to call `constrain` and then `search` on each puzzle, and then verify that the solution is correct with `is_solution`, and finally print the puzzle and the solution side by side:" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "def solve(puzzles, verbose=True) -> int:\n", - " \"Solve and verify each puzzle, and if `verbose`, print puzzle and solution.\"\n", - " sep = ' '\n", - " for puzzle in puzzles:\n", - " solution = search(constrain(puzzle))\n", - " assert is_solution(solution, puzzle)\n", - " if verbose:\n", - " print('\\nPuzzle ', sep, 'Solution')\n", - " for p, s in zip(picture(puzzle).splitlines(), picture(solution).splitlines()):\n", - " print(p, sep, s)\n", - " return len(puzzles)" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Puzzle Solution\n", - "5 3 .|. 7 .|. . . 5 3 4|6 7 8|9 1 2 \n", - "6 . .|1 9 5|. . . 6 7 2|1 9 5|3 4 8 \n", - ". 9 8|. . .|. 6 . 1 9 8|3 4 2|5 6 7 \n", - "-----+-----+----- -----+-----+----- \n", - "8 . .|. 6 .|. . 3 8 5 9|7 6 1|4 2 3 \n", - "4 . .|8 . 3|. . 1 4 2 6|8 5 3|7 9 1 \n", - "7 . .|. 2 .|. . 6 7 1 3|9 2 4|8 5 6 \n", - "-----+-----+----- -----+-----+----- \n", - ". 6 .|. . .|2 8 . 9 6 1|5 3 7|2 8 4 \n", - ". . .|4 1 9|. . 5 2 8 7|4 1 9|6 3 5 \n", - ". . .|. 8 .|. 7 9 3 4 5|2 8 6|1 7 9 \n", - "\n", - "Puzzle Solution\n", - "4 . .|. . .|8 . 5 4 1 7|3 6 9|8 2 5 \n", - ". 3 .|. . .|. . . 6 3 2|1 5 8|9 4 7 \n", - ". . .|7 . .|. . . 9 5 8|7 2 4|3 1 6 \n", - "-----+-----+----- -----+-----+----- \n", - ". 2 .|. . .|. 6 . 8 2 5|4 3 7|1 6 9 \n", - ". . .|. 8 .|4 . . 7 9 1|5 8 6|4 3 2 \n", - ". . .|. 1 .|. . . 3 4 6|9 1 2|7 5 8 \n", - "-----+-----+----- -----+-----+----- \n", - ". . .|6 . 3|. 7 . 2 8 9|6 4 3|5 7 1 \n", - "5 . .|2 . .|. . . 5 7 3|2 9 1|6 8 4 \n", - "1 . 4|. . .|. . . 1 6 4|8 7 5|2 9 3 \n" - ] - }, - { - "data": { - "text/plain": [ - "2" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "solve([grid1, grid2])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We see that `search` is effective at solving the previously-unsolved `grid2`.\n", - "\n", - "Computer scientists call this a [depth-first search](http://en.wikipedia.org/wiki/Depth-first_search) because it (recursively) considers all possible continuations from the grid with *d* in square *s* before it backs up and considers a different digit in *s*. Sudoku players call this the [Nishio strategy](https://www.sudokuonline.io/tips/advanced-sudoku-strategies), after professional puzzle player Tetsuya Nishio, although Nishio only applied it when there were just two remaining possibilities for a square. We can apply it with any number of possibilities; we can even find a solution for the completely empty puzzle where every square has 9 possibilities:" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Puzzle Solution\n", - ". . .|. . .|. . . 1 2 3|4 5 6|7 8 9 \n", - ". . .|. . .|. . . 4 5 6|7 8 9|1 2 3 \n", - ". . .|. . .|. . . 7 8 9|1 2 3|4 5 6 \n", - "-----+-----+----- -----+-----+----- \n", - ". . .|. . .|. . . 2 3 1|6 7 4|8 9 5 \n", - ". . .|. . .|. . . 8 7 5|9 1 2|3 6 4 \n", - ". . .|. . .|. . . 6 9 4|5 3 8|2 1 7 \n", - "-----+-----+----- -----+-----+----- \n", - ". . .|. . .|. . . 3 1 7|2 6 5|9 4 8 \n", - ". . .|. . .|. . . 5 4 2|8 9 7|6 3 1 \n", - ". . .|. . .|. . . 9 6 8|3 4 1|5 7 2 \n" - ] - }, - { - "data": { - "text/plain": [ - "1" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "empty = parse('.' * 81)\n", - " \n", - "solve([empty])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Verifying the Program\n", - "\n", - "We had success in solving `grid1`, `grid2`, and `empty`, but we are left with some questions:\n", - "- Can the program solve *any* puzzle? \n", - " - We'll test it on some more `puzzles`, taken from some files. The more we test, the more confidence we have.\n", - " - Assuming no bugs, the program can solve any puzzle, but we haven't determined a bound on how long it would take.\n", - "- Are the components of the program correct? \n", - " - We'll run some `unit_tests`. There should be more.\n", - "- How long does it take to solve a puzzle?\n", - " - We'll do `%time solve(puzzles)`.\n", - " - For a more complete investigation of run times, see the [Java version](SudokuJava.ipynb)." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "def unit_tests():\n", - " \"A suite of unit tests.\"\n", - " assert len(squares) == 81\n", - " assert len(all_units) == 27\n", - " for s in squares:\n", - " assert len(units[s]) == 3\n", - " assert len(peers[s]) == 20\n", - " assert units['C2'] == (('A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'I2'),\n", - " ('C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9'),\n", - " ('A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3'))\n", - " assert peers['C2'] == {'A2', 'B2', 'D2', 'E2', 'F2', 'G2', 'H2', 'I2',\n", - " 'C1', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9',\n", - " 'A1', 'A3', 'B1', 'B3'}\n", - " return 'pass'\n", - "\n", - "def parse_grids(pictures):\n", - " \"\"\"Parse an iterable of picture lines into a list of grids.\"\"\"\n", - " return [parse(p) for p in pictures if p]\n", - "\n", - "hardest = parse_grids(open('hardest.txt'))\n", - "grids10k = parse_grids(open('sudoku10k.txt'))" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'pass'" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "unit_tests()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can solve 10,000 puzzles, verifying that each solution is correct (but not printing them):" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 27.6 s, sys: 23.5 ms, total: 27.7 s\n", - "Wall time: 27.7 s\n" - ] - }, - { - "data": { - "text/plain": [ - "10000" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "%time solve(grids10k, verbose=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "That took less than 3 milliseconds per puzzle, or over 350 puzzles per second. (The [Java version](SudokuJava.ipynb) does over 100,000 puzzles per second. It benefits from being able to run 20 threads in parallel, from using more efficient data structures, and from the Java JVM being more efficient than Python's bytecode interpreter.)\n", - "\n", - "We can inspect the solutions for the ten allegedly hardest puzzles:" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Puzzle Solution\n", - "8 5 .|. . 2|4 . . 8 5 9|6 1 2|4 3 7 \n", - "7 2 .|. . .|. . 9 7 2 3|8 5 4|1 6 9 \n", - ". . 4|. . .|. . . 1 6 4|3 7 9|5 2 8 \n", - "-----+-----+----- -----+-----+----- \n", - ". . .|1 . 7|. . 2 9 8 6|1 4 7|3 5 2 \n", - "3 . 5|. . .|9 . . 3 7 5|2 6 8|9 1 4 \n", - ". 4 .|. . .|. . . 2 4 1|5 9 3|7 8 6 \n", - "-----+-----+----- -----+-----+----- \n", - ". . .|. 8 .|. 7 . 4 3 2|9 8 1|6 7 5 \n", - ". 1 7|. . .|. . . 6 1 7|4 2 5|8 9 3 \n", - ". . .|. 3 6|. 4 . 5 9 8|7 3 6|2 4 1 \n", - "\n", - "Puzzle Solution\n", - ". . 5|3 . .|. . . 1 4 5|3 2 7|6 9 8 \n", - "8 . .|. . .|. 2 . 8 3 9|6 5 4|1 2 7 \n", - ". 7 .|. 1 .|5 . . 6 7 2|9 1 8|5 4 3 \n", - "-----+-----+----- -----+-----+----- \n", - "4 . .|. . 5|3 . . 4 9 6|1 8 5|3 7 2 \n", - ". 1 .|. 7 .|. . 6 2 1 8|4 7 3|9 5 6 \n", - ". . 3|2 . .|. 8 . 7 5 3|2 9 6|4 8 1 \n", - "-----+-----+----- -----+-----+----- \n", - ". 6 .|5 . .|. . 9 3 6 7|5 4 2|8 1 9 \n", - ". . 4|. . .|. 3 . 9 8 4|7 6 1|2 3 5 \n", - ". . .|. . 9|7 . . 5 2 1|8 3 9|7 6 4 \n", - "\n", - "Puzzle Solution\n", - "1 2 .|. 4 .|. . . 1 2 8|5 4 7|6 3 9 \n", - ". . 5|. 6 9|. 1 . 3 4 5|8 6 9|2 1 7 \n", - ". . 9|. . .|5 . . 6 7 9|2 1 3|5 4 8 \n", - "-----+-----+----- -----+-----+----- \n", - ". . .|. . .|. 7 . 9 1 2|4 8 6|3 7 5 \n", - "7 . .|. 5 2|. 9 . 7 8 4|3 5 2|1 9 6 \n", - ". 3 .|. . .|. . 2 5 3 6|7 9 1|4 8 2 \n", - "-----+-----+----- -----+-----+----- \n", - ". 9 .|6 . .|. 5 . 8 9 1|6 2 4|7 5 3 \n", - "4 . .|9 . .|8 . 1 4 6 7|9 3 5|8 2 1 \n", - ". . 3|. . .|9 . 4 2 5 3|1 7 8|9 6 4 \n", - "\n", - "Puzzle Solution\n", - ". . .|5 7 .|. 3 . 6 2 4|5 7 8|1 3 9 \n", - "1 . .|. . .|. 2 . 1 3 5|4 9 6|8 2 7 \n", - "7 . .|. 2 3|4 . . 7 8 9|1 2 3|4 5 6 \n", - "-----+-----+----- -----+-----+----- \n", - ". . .|. 8 .|. . 4 2 1 6|3 8 5|7 9 4 \n", - ". . 7|. . 4|. . . 8 5 7|9 6 4|2 1 3 \n", - "4 9 .|. . .|6 . 5 4 9 3|2 1 7|6 8 5 \n", - "-----+-----+----- -----+-----+----- \n", - ". 4 2|. . .|3 . . 9 4 2|6 5 1|3 7 8 \n", - ". . .|7 . .|9 . . 5 6 8|7 3 2|9 4 1 \n", - ". . 1|8 . .|. . . 3 7 1|8 4 9|5 6 2 \n", - "\n", - "Puzzle Solution\n", - "1 . .|. . 7|. 9 . 1 6 2|8 5 7|4 9 3 \n", - ". 3 .|. 2 .|. . 8 5 3 4|1 2 9|6 7 8 \n", - ". . 9|6 . .|5 . . 7 8 9|6 4 3|5 2 1 \n", - "-----+-----+----- -----+-----+----- \n", - ". . 5|3 . .|9 . . 4 7 5|3 1 2|9 8 6 \n", - ". 1 .|. 8 .|. . 2 9 1 3|5 8 6|7 4 2 \n", - "6 . .|. . 4|. . . 6 2 8|7 9 4|1 3 5 \n", - "-----+-----+----- -----+-----+----- \n", - "3 . .|. . .|. 1 . 3 5 6|4 7 8|2 1 9 \n", - ". 4 .|. . .|. . 7 2 4 1|9 3 5|8 6 7 \n", - ". . 7|. . .|3 . . 8 9 7|2 6 1|3 5 4 \n", - "\n", - "Puzzle Solution\n", - "1 . .|. 3 4|. 8 . 1 5 2|9 3 4|6 8 7 \n", - ". . .|8 . .|5 . . 7 6 3|8 2 1|5 4 9 \n", - ". . 4|. 6 .|. 2 1 9 8 4|5 6 7|3 2 1 \n", - "-----+-----+----- -----+-----+----- \n", - ". 1 8|. . .|. . . 6 1 8|4 9 3|2 7 5 \n", - "3 . .|1 . 2|. . 6 3 7 5|1 8 2|4 9 6 \n", - ". . .|. . .|8 1 . 2 4 9|7 5 6|8 1 3 \n", - "-----+-----+----- -----+-----+----- \n", - "5 2 .|. 7 .|9 . . 5 2 1|3 7 8|9 6 4 \n", - ". . 6|. . 9|. . . 4 3 6|2 1 9|7 5 8 \n", - ". 9 .|6 4 .|. . 2 8 9 7|6 4 5|1 3 2 \n", - "\n", - "Puzzle Solution\n", - ". . .|9 2 .|. . . 3 8 7|9 2 6|4 1 5 \n", - ". . 6|8 . 3|. . . 5 4 6|8 1 3|9 7 2 \n", - "1 9 .|. 7 .|. . 6 1 9 2|4 7 5|8 3 6 \n", - "-----+-----+----- -----+-----+----- \n", - "2 3 .|. 4 .|1 . . 2 3 5|7 4 9|1 6 8 \n", - ". . 1|. . .|7 . . 9 6 1|2 5 8|7 4 3 \n", - ". . 8|. 3 .|. 2 9 4 7 8|6 3 1|5 2 9 \n", - "-----+-----+----- -----+-----+----- \n", - "7 . .|. 8 .|. 9 1 7 5 4|3 8 2|6 9 1 \n", - ". . .|5 . 7|2 . . 6 1 3|5 9 7|2 8 4 \n", - ". . .|. 6 4|. . . 8 2 9|1 6 4|3 5 7 \n", - "\n", - "Puzzle Solution\n", - ". 6 .|5 . 4|. 3 . 8 6 9|5 7 4|1 3 2 \n", - "1 . .|. 9 .|. . 8 1 2 4|3 9 6|7 5 8 \n", - ". . .|. . .|. . . 3 7 5|1 2 8|6 9 4 \n", - "-----+-----+----- -----+-----+----- \n", - "9 . .|. 5 .|. . 6 9 3 2|8 5 7|4 1 6 \n", - ". 4 .|6 . 2|. 7 . 5 4 1|6 3 2|8 7 9 \n", - "7 . .|. 4 .|. . 5 7 8 6|9 4 1|3 2 5 \n", - "-----+-----+----- -----+-----+----- \n", - ". . .|. . .|. . . 2 1 7|4 6 9|5 8 3 \n", - "4 . .|. 8 .|. . 1 4 9 3|7 8 5|2 6 1 \n", - ". 5 .|2 . 3|. 4 . 6 5 8|2 1 3|9 4 7 \n", - "\n", - "Puzzle Solution\n", - "7 . .|. . .|4 . . 7 9 8|6 3 5|4 2 1 \n", - ". 2 .|. 7 .|. 8 . 1 2 6|9 7 4|5 8 3 \n", - ". . 3|. . 8|. 7 9 4 5 3|2 1 8|6 7 9 \n", - "-----+-----+----- -----+-----+----- \n", - "9 . .|5 . .|3 . . 9 7 2|5 8 6|3 1 4 \n", - ". 6 .|. 2 .|. 9 . 5 6 4|1 2 3|8 9 7 \n", - ". . 1|. 9 7|. . 6 3 8 1|4 9 7|2 5 6 \n", - "-----+-----+----- -----+-----+----- \n", - ". . .|3 . .|9 . . 6 1 7|3 5 2|9 4 8 \n", - ". 3 .|. 4 .|. 6 . 8 3 5|7 4 9|1 6 2 \n", - ". . 9|. . 1|. 3 5 2 4 9|8 6 1|7 3 5 \n", - "\n", - "Puzzle Solution\n", - ". . .|. 7 .|. 2 . 5 9 4|8 7 6|1 2 3 \n", - "8 . .|. . .|. . 6 8 2 3|9 1 4|7 5 6 \n", - ". 1 .|2 . 5|. . . 6 1 7|2 3 5|8 9 4 \n", - "-----+-----+----- -----+-----+----- \n", - "9 . 5|4 . .|. . 8 9 6 5|4 2 1|3 7 8 \n", - ". . .|. . .|. . . 7 8 1|6 5 3|9 4 2 \n", - "3 . .|. . 8|5 . 1 3 4 2|7 9 8|5 6 1 \n", - "-----+-----+----- -----+-----+----- \n", - ". . .|3 . 2|. 8 . 1 5 9|3 4 2|6 8 7 \n", - "4 . .|. . .|. . 9 4 3 6|5 8 7|2 1 9 \n", - ". 7 .|. 6 .|. . . 2 7 8|1 6 9|4 3 5 \n", - "CPU times: user 40.6 ms, sys: 3.5 ms, total: 44.1 ms\n", - "Wall time: 41.5 ms\n" - ] - }, - { - "data": { - "text/plain": [ - "10" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "%time solve(hardest)" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -995,7 +1035,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -1009,7 +1049,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.9.12" } }, "nbformat": 4, diff --git a/ipynb/bikerides.tsv b/ipynb/bikerides.tsv index 77204ae..1acaa7c 100644 --- a/ipynb/bikerides.tsv +++ b/ipynb/bikerides.tsv @@ -1,537 +1,541 @@ -date year title hours miles feet -##### 2019-2023: Mostly Eddington rides; most recent first -Sat, 10/7 2023 Morning Ride 5:33:41 70.25 3,038 -Sat, 9/23 2023 Dumbarton / Niles 4:45:06 65.02 1,122 -Sat, 9/9 2023 Alameda to Treasure Island 4:43:08 62.97 1,378 -Fri, 9/1 2023 Santa Cruz + Ladera 6:14:38 67.59 4,990 -Sat, 8/12 2023 Norris Canyon + Ladera 6:53:48 74.97 5,470 -Fri, 7/28 2023 San Carlos completed 5:24:45 69.49 4,479 -Wed, 7/19 2023 MTV + RWC 5:22:20 64.14 1,145 -Wed, 6/28 2023 Los Altos Hills + MTV 5:51:01 62.77 1,687 -Tue, 6/20 2023 Ed Levin Park with Ted MTB 6:10:19 66.55 4,547 -Fri, 6/9 2023 Completed Los Altos and 50% Sunnyvale 5:37:26 67.20 1,266 -Wed, 6/7 2023 Los Altos 7:03:03 81.54 2,110 -Fri, 6/2 2023 Los Altos Hills 6:47:13 68.33 5,221 -Tue, 5/16 2023 Los Altos Hills paths 5:56:04 64.03 1,869 -Mon, 5/1 2023 Eden Landing 5:43:00 64.24 1,425 -Sun, 4/23 2023 Portola Loop with Juliet 5:09:21 62.43 2,805 -Sun, 3/26 2023 Redwood City 5:42:04 69.11 1,015 -Sat, 3/18 2023 Mostly Palo Alto 5:54:50 69.31 2,549 -Wed, 12/21 2022 Canada / Portola 4:18:54 62.83 2,226 -Sat, 11/19 2022 Cañada 5:02:48 63.06 3,340 -Fri, 10/21 2022 Alviso Levees + Seaport 5:06:26 67.14 931 -Sat, 9/17 2022 San Gregorio / Tunitas 6:33:44 80.53 6,015 -Thu, 7/14 2022 Bike Hut Classic 6:36:30 74.16 6,070 -Sat, 7/9 2022 Purisima Creek Trail 7:20:20 62.93 7,139 -Sat, 7/2 2022 Bear Gulch, West Side 6:29:10 77.73 6,991 -Sat, 6/25 2022 Chabot: Redwood / Skyline / Goldenrod / Cull Canyon 5:02:33 65.15 5,361 -Sat, 5/7 2022 Wine Country Century 6:39:02 100.26 5,253 -Sat, 4/23 2022 Tierra Bella 100K with Anne and David 5:38:12 68.38 4,892 -Sat, 4/9 2022 Kings / Skyline / 92 6:07:05 69.29 5,029 -Sat, 1/29 2022 Woodside plus Montebello 6:49:14 67.73 5,553 -Sat, 1/15 2022 Crestview 5:43:00 64.49 4,446 -Sun, 11/14 2021 Hayward 5:08:23 72.23 1,132 -Sat, 10/16 2021 San Jose 5:47:00 70.18 2,562 -Sat, 9/2 2021 San Jose 4:38:20 60.54 1,079 -Tue, 8/3 2021 San Jose Neighborhoods 4:41:27 62.35 1,825 -Sat, 8/7 2021 Emerald Hills etc. 5:45:30 73.38 3,015 -Sun, 7/11 2021 San Jose 4:05:49 65.10 1,086 -Sat, 6/12 2021 Up OLH, down Page Mill 5:48:48 71.42 3,525 -Sun, 5/16 2021 Palo Alto / SF / San Rafael 5:41:52 70.33 2,073 -Sat, 5/15 2021 San Rafael to Palo Alto over 20 bridges 5:23:44 68.30 871 -Sat, 4/24 2021 Alviso / Niles / Dumbarton 100km with Juliet 5:44:24 69.79 1,230 -Sun, 4/11 2021 OLH / Tunitas 6:06:24 66.04 4,629 -Sat, 4/10 2021 Milpitas 4:56:57 65.41 913 -Sun, 4/4 2021 Everesting 7: Mill Creek / Morrison Canyon 3:04:46 29.38 3,517 -Sat, 4/3 2021 Everesting 6: OLH / West Alpine 4:26:16 46.64 4,400 -Fri, 4/2 2021 Everesting 5: climb 2×(OLH + WOLH) 3:16:30 31.48 4,344 -Thu, 4/1 2021 Everesting 4: Kings / Tunitas / Bike Hut 4:40:36 45.57 4,452 -Wed, 3/31 2021 Everesting 3: Ring of Fire + Ladera 4:40:59 45.78 4,544 -Tue, 3/30 2021 Everesting 2: Kings + WOLH + OLH 3:20:29 35.99 4,377 -Mon, 3/29 2021 Everesting 1: Mt Diablo 2:35:47 22.22 3,406 -Sun, 3/14 2021 Fremont / Union City 5:18:47 69.58 1552 -Fri, 3/12 2021 Santa Clara 5:09:38 69.49 1138 -Sun, 2/7 2021 Saratoga / Campbell 5:53:33 78.38 2270 -Fri, 1/8 2021 Coyote Hills Geocaching 4:58:08 69.08 797 -Sun, 10/11 2020 Los Altos Hills Paths 5:49:29 65.03 1,870 -Mon, 10/5 2020 Half way around the bay on bay trail 6:26:35 80.05 541 -Tue, 9/29 2020 Saratoga Geocaching 4:58:20 64.30 961 -Sun, 9/27 2020 Edenvale Park 5:39:01 75.73 997 -Thu, 9/24 2020 Alum Rock Geocaching 5:07:29 67.19 1093 -Sun, 9/20 2020 Rosicrucian Museum, Alexis Dr. 6:03:57 74.85 600 -Tue, 9/15 2020 First ride after 5 days of smoke 6:11:15 76.05 1181 -Sun, 8/30 2020 Los Gatos 6:21:49 80.92 2,100 -Sun, 8/16 2020 Afternoon Ride 5:31:50 68.39 514 -Fri, 8/14 2020 Niles / Dumbarton 4:53:47 66.12 597 -Sun, 8/9 2020 Ravenswood Bay Trail / Niles / Dumbarton 5:37:20 74.68 1125 -Sat, 8/1 2020 Alviso / Guadalupe / SJC 5:52:52 68.66 718 -Tue, 7/28 2020 Circumnavigate lower Bay / Coyote Creek lagoon / Dumbarton 5:15:29 68.68 521 -Sat, 7/25 2020 Bay Trail 5:31:16 68.19 731 -Thu, 7/23 2020 Bay / Tomas Aquino Trail 5:44:49 76.35 1749 -Sun, 7/19 2020 Saratoga / Mt Eden 5:49:45 70.43 1,954 -Sun, 7/12 2020 Calvares Rd / Niles / Dumbarton 5:30:10 68.23 2,008 -Sun, 7/5 2020 Afternoon Ride 5:16:20 66.07 1,286 -Fri, 7/3 2020 Sawyer Camp Trail 5:18:23 72.12 1,890 -Sun, 6/28 2020 Saratoga 5:31:44 71.01 1,063 -Sun, 6/21 2020 Sawyer Camp Trail 6:35:20 79.78 1,738 -Sun, 6/13 2020 Canada Rd 5:39:40 70.73 1,841 -Fri, 2/28 2020 Sawyer Camp Trail 6:24:49 84.43 3,448 -Wed, 2/12 2020 Bay Trail 5:25:09 66.02 572 -Fri, 2/7 2020 Bay Trail / Cargill 6:07:23 74.04 1,814 -Sun, 1/26 2020 Los Gatos / Bay 5:18:15 67.98 1,286 -Fri, 1/31 2020 Bay Trail / Guadalupe / Geocaching 5:21:39 69.39 1,125 -Sat, 11/16 2019 Canada / Crystal Dam / Sawyer Camp / Bay Trail 5:18:46 66.08 1,972 -Fri, 9/6 2019 San Mateo / Bay Trail 4:59:29 68.01 1,158 -Sat, 8/31 2019 San Gregorio via OLH 5:13:21 65.31 4,026 -Sat, 8/24 2019 Tour de Fox 4:43:39 68.66 2,097 -Sun, 8/18 2019 Afternoon Ride 2:06:52 27.14 331 -Fri, 7/5 2019 Crystal Springs 4:30:55 64.05 1,965 -Sat, 6/15 2019 Morning Ride 2:29:38 29.98 1,785 -Sat, 6/8 2019 Morning Ride 2:53:08 34.42 1,824 -Sun, 6/2 2019 The Sequoia 6:40:43 77.51 6,467 +date title hours miles feet +##### 2019-2024: Mostly Eddington rides; most recent first +Sat, 2/10/2024 Seacliff, etc. 4:43:16 63.41 5,365 +Sat, 2/3/2024 Before the rains 4:48:45 70.88 2,668 +Mon, 1/1/2024 Bear Gulch 6:09:21 70.12 6,627 +Sat, 12/9/2023 December 100K 4:57:49 62.31 2,618 +Sat, 10/7/2023 Morning Ride 5:33:41 70.25 3,038 +Sat, 9/23/2023 Dumbarton / Niles 4:45:06 65.02 1,122 +Sat, 9/9/2023 Alameda to Treasure Island 4:43:08 62.97 1,378 +Fri, 9/1/2023 Santa Cruz + Ladera 6:14:38 67.59 4,990 +Sat, 8/12/2023 Norris Canyon + Ladera 6:53:48 74.97 5,470 +Fri, 7/28/2023 San Carlos completed 5:24:45 69.49 4,479 +Wed, 7/19/2023 MTV + RWC 5:22:20 64.14 1,145 +Wed, 6/28/2023 Los Altos Hills + MTV 5:51:01 62.77 1,687 +Tue, 6/20/2023 Ed Levin Park with Ted MTB 6:10:19 66.55 4,547 +Fri, 6/9/2023 Completed Los Altos and 50% Sunnyvale 5:37:26 67.20 1,266 +Wed, 6/7/2023 Los Altos 7:03:03 81.54 2,110 +Fri, 6/2/2023 Los Altos Hills 6:47:13 68.33 5,221 +Tue, 5/16/2023 Los Altos Hills paths 5:56:04 64.03 1,869 +Mon, 5/1/2023 Eden Landing 5:43:00 64.24 1,425 +Sun, 4/23/2023 Portola Loop with Juliet 5:09:21 62.43 2,805 +Sun, 3/26/2023 Redwood City 5:42:04 69.11 1,015 +Sat, 3/18/2023 Mostly Palo Alto 5:54:50 69.31 2,549 +Wed, 12/21/2022 Canada / Portola 4:18:54 62.83 2,226 +Sat, 11/19/2022 Cañada 5:02:48 63.06 3,340 +Fri, 10/21/2022 Alviso Levees + Seaport 5:06:26 67.14 931 +Sat, 9/17/2022 San Gregorio / Tunitas 6:33:44 80.53 6,015 +Thu, 7/14/2022 Bike Hut Classic 6:36:30 74.16 6,070 +Sat, 7/9/2022 Purisima Creek Trail 7:20:20 62.93 7,139 +Sat, 7/2/2022 Bear Gulch, West Side 6:29:10 77.73 6,991 +Sat, 6/25/2022 Chabot: Redwood / Skyline / Goldenrod / Cull Canyon 5:02:33 65.15 5,361 +Sat, 5/7/2022 Wine Country Century 6:39:02 100.26 5,253 +Sat, 4/23/2022 Tierra Bella 100K with Anne and David 5:38:12 68.38 4,892 +Sat, 4/9/2022 Kings / Skyline / 92 6:07:05 69.29 5,029 +Sat, 1/29/2022 Woodside plus Montebello 6:49:14 67.73 5,553 +Sat, 1/15/2022 Crestview 5:43:00 64.49 4,446 +Sun, 11/14/2021 Hayward 5:08:23 72.23 1,132 +Sat, 10/16/2021 San Jose 5:47:00 70.18 2,562 +Sat, 9/2/2021 San Jose 4:38:20 60.54 1,079 +Tue, 8/3/2021 San Jose Neighborhoods 4:41:27 62.35 1,825 +Sat, 8/7/2021 Emerald Hills etc. 5:45:30 73.38 3,015 +Sun, 7/11/2021 San Jose 4:05:49 65.10 1,086 +Sat, 6/12/2021 Up OLH, down Page Mill 5:48:48 71.42 3,525 +Sun, 5/16/2021 Palo Alto / SF / San Rafael 5:41:52 70.33 2,073 +Sat, 5/15/2021 San Rafael to Palo Alto over 20 bridges 5:23:44 68.30 871 +Sat, 4/24/2021 Alviso / Niles / Dumbarton 100km with Juliet 5:44:24 69.79 1,230 +Sun, 4/11/2021 OLH / Tunitas 6:06:24 66.04 4,629 +Sat, 4/10/2021 Milpitas 4:56:57 65.41 913 +Sun, 4/4/2021 Everesting 7: Mill Creek / Morrison Canyon 3:04:46 29.38 3,517 +Sat, 4/3/2021 Everesting 6: OLH / West Alpine 4:26:16 46.64 4,400 +Fri, 4/2/2021 Everesting 5: climb 2×(OLH + WOLH) 3:16:30 31.48 4,344 +Thu, 4/1/2021 Everesting 4: Kings / Tunitas / Bike Hut 4:40:36 45.57 4,452 +Wed, 3/31/2021 Everesting 3: Ring of Fire + Ladera 4:40:59 45.78 4,544 +Tue, 3/30/2021 Everesting 2: Kings + WOLH + OLH 3:20:29 35.99 4,377 +Mon, 3/29/2021 Everesting 1: Mt Diablo 2:35:47 22.22 3,406 +Sun, 3/14/2021 Fremont / Union City 5:18:47 69.58 1552 +Fri, 3/12/2021 Santa Clara 5:09:38 69.49 1138 +Sun, 2/7/2021 Saratoga / Campbell 5:53:33 78.38 2270 +Fri, 1/8/2021 Coyote Hills Geocaching 4:58:08 69.08 797 +Sun, 10/11/2020 Los Altos Hills Paths 5:49:29 65.03 1,870 +Mon, 10/5/2020 Half way around the bay on bay trail 6:26:35 80.05 541 +Tue, 9/29/2020 Saratoga Geocaching 4:58:20 64.30 961 +Sun, 9/27/2020 Edenvale Park 5:39:01 75.73 997 +Thu, 9/24/2020 Alum Rock Geocaching 5:07:29 67.19 1093 +Sun, 9/20/2020 Rosicrucian Museum, Alexis Dr. 6:03:57 74.85 600 +Tue, 9/15/2020 First ride after 5 days of smoke 6:11:15 76.05 1181 +Sun, 8/30/2020 Los Gatos 6:21:49 80.92 2,100 +Sun, 8/16/2020 Afternoon Ride 5:31:50 68.39 514 +Fri, 8/14/2020 Niles / Dumbarton 4:53:47 66.12 597 +Sun, 8/9/2020 Ravenswood Bay Trail / Niles / Dumbarton 5:37:20 74.68 1125 +Sat, 8/1/2020 Alviso / Guadalupe / SJC 5:52:52 68.66 718 +Tue, 7/28/2020 Circumnavigate lower Bay / Coyote Creek lagoon / Dumbarton 5:15:29 68.68 521 +Sat, 7/25/2020 Bay Trail 5:31:16 68.19 731 +Thu, 7/23/2020 Bay / Tomas Aquino Trail 5:44:49 76.35 1749 +Sun, 7/19/2020 Saratoga / Mt Eden 5:49:45 70.43 1,954 +Sun, 7/12/2020 Calvares Rd / Niles / Dumbarton 5:30:10 68.23 2,008 +Sun, 7/5/2020 Afternoon Ride 5:16:20 66.07 1,286 +Fri, 7/3/2020 Sawyer Camp Trail 5:18:23 72.12 1,890 +Sun, 6/28/2020 Saratoga 5:31:44 71.01 1,063 +Sun, 6/21/2020 Sawyer Camp Trail 6:35:20 79.78 1,738 +Sun, 6/13/2020 Canada Rd 5:39:40 70.73 1,841 +Fri, 2/28/2020 Sawyer Camp Trail 6:24:49 84.43 3,448 +Wed, 2/12/2020 Bay Trail 5:25:09 66.02 572 +Fri, 2/7/2020 Bay Trail / Cargill 6:07:23 74.04 1,814 +Sun, 1/26/2020 Los Gatos / Bay 5:18:15 67.98 1,286 +Fri, 1/31/2020 Bay Trail / Guadalupe / Geocaching 5:21:39 69.39 1,125 +Sat, 11/16/2019 Canada / Crystal Dam / Sawyer Camp / Bay Trail 5:18:46 66.08 1,972 +Fri, 9/6/2019 San Mateo / Bay Trail 4:59:29 68.01 1,158 +Sat, 8/31/2019 San Gregorio via OLH 5:13:21 65.31 4,026 +Sat, 8/24/2019 Tour de Fox 4:43:39 68.66 2,097 +Sun, 8/18/2019 Afternoon Ride 2:06:52 27.14 331 +Fri, 7/5/2019 Crystal Springs 4:30:55 64.05 1,965 +Sat, 6/15/2019 Morning Ride 2:29:38 29.98 1,785 +Sat, 6/8/2019 Morning Ride 2:53:08 34.42 1,824 +Sun, 6/2/2019 The Sequoia 6:40:43 77.51 6,467 ##### 2013 to 2018: rides over 25 miles, longest first -Sat, 5/13 2017 Morgan Hill iCare Classic 7:27:21 100.05 4,596 -Sat, 5/12 2018 ICare Classic, Morgan Hill 6:47:46 91.29 4,160 -Sat, 5/6 2017 Wine Country Century 7:15:22 89.49 5,246 -Fri, 8/10 2018 Bike Ride Northwest Day 6 6:14:18 84.70 4,380 -Sat, 10/1 2016 Half Moon Bay overnight campout 7:30:38 80.07 6,039 -Tue, 8/7 2018 Bike Ride Northwest Day 3 6:10:39 78.96 5,092 -Sun, 6/15 2014 Sierra to the Sea Day 1 5:34:06 78.53 4,777 -Sat, 4/23 2016 Wildflower Century 5:36:10 77.22 4,193 -Mon, 6/16 2014 Sierra to the Sea Day 2 5:35:06 74.68 2,451 -Fri, 8/12 2016 Half Moon Bay / Harvey's 73/7300 Birthday Ride 6:44:14 74.35 6,610 -Wed, 10/14 2015 Half Moon Bay 6:07:55 72.97 7,644 -Fri, 1/27 2017 Morning Ride 5:16:50 70.07 2,539 -Sat, 8/15 2015 Coyote Creek Trail with Juliet 5:49:01 69.30 1,745 -Tue, 6/17 2014 Sierra to the Sea Day 3 4:48:41 68.64 825 -Thu, 8/9 2018 Bike Ride Northwest Day 5 4:58:07 68.41 3,862 -Sat, 4/15 2017 Pescadero 6:13:12 68.34 6,130 -Sun, 6/4 2017 Sequoia Challenge 6:17:25 66.52 7,520 -Wed, 8/5 2015 Palo Alto, CA 4:56:15 66.33 2,054 -Fri, 8/28 2015 Pescadaro via OLH 5:18:51 66.01 6,137 -Fri, 11/20 2015 Los Gatos 5:29:40 65.73 5,380 -Sun, 6/3 2018 The Sequoia 5:58:15 64.92 6,677 -Thu, 6/19 2014 Sierra to the Sea Day 5 4:32:55 63.69 2,584 -Mon, 8/3 2015 Palo Alto, CA 4:45:09 63.61 1,877 -Fri, 6/10 2016 Morning Ride 4:21:38 62.84 3,196 -Sun, 6/2 2013 Woodside Loop and Baylands 4:27:42 62.14 2,169 -Wed, 6/12 2013 ride 4:39:46 61.39 2,207 -Sat, 4/18 2015 Tunitas + Lobitos Creeks 5:14:38 61.27 6,611 -Sat, 11/11 2017 Los Gatos / Bay Trail 4:26:24 60.74 1,316 -Sun, 4/17 2016 La Honda / Skyline 5:10:41 60.35 4,551 -Mon, 7/3 2017 Los Gatos / Bay Trail 4:25:41 60.28 1,329 -Sat, 4/16 2016 OLH / San Gregorio / Tunitas 4:57:40 60.12 4,744 -Sun, 1/29 2017 Los Gatos / Guadalupe / San Tomas / Bay Trails 4:33:38 60.11 1,447 -Mon, 11/26 2018 Lunch Ride Los Gatos 4:21:35 60.03 1,070 -Sat, 10/31 2015 Saratoga Ramble 5:01:57 59.11 3,528 -Sat, 8/18 2018 Tour de Menlo 4:03:48 58.81 2,467 -Sat, 4/26 2014 OLH / Tunitas Creek 5:15:26 58.69 6,742 -Sat, 6/15 2013 Palo Alto to Santa Cruz 4:27:54 58.42 4,431 -Sat, 10/11 2014 OLH / Tunitas 5:05:23 58.29 6,044 -Sat, 7/9 2016 Santa Cruz 3:50:23 58.23 4,042 -Wed, 6/18 2014 Sierra to the Sea Day 4 4:57:53 57.64 5,561 -Tue, 7/7 2015 Palo Alto, CA 4:13:20 57.60 1,280 -Mon, 8/6 2018 Bike Ride Northwest Day 2 4:35:48 57.58 4,514 -Su, 1/26 2020 Los Gatos 4:24:58 57.05 1,086 -Fri, 6/20 2014 Sierra to the Sea Day 6 4:33:39 56.91 4,453 -Sat, 6/10 2017 Los Gatos / Creek Trails 3:50:24 56.28 1,365 -Sat, 3/4 2017 Lunch Ride 3:58:25 56.26 1,378 -Sun, 8/5 2018 Bike Ride Northwest Day 1 3:34:42 55.77 1,824 -Sat, 8/13 2016 Petaluma / Point Reyes 4:30:12 54.75 5,286 -Sun, 6/7 2015 Tour de Cure 3:59:47 54.65 2,748 -Wed, 12/16 2015 Los Gatos 4:00:41 53.86 2,595 -Fri, 6/2 2017 Morning Ride 3:57:45 53.49 1,375 -Sun, 6/8 2014 Tour de Cure 75K 4:03:05 53.10 2,596 -Sun, 11/6 2016 Los Gatos 3:38:28 52.49 1,263 -Fri, 2/10 2017 Morning Ride 3:52:39 52.02 1,739 -Sat, 2/23 2019 Crystal Springs Dam 4:02:11 51.93 1,946 -Fri, 8/11 2017 Saratoga with Peter H 4:30:31 51.74 2,871 -Sat, 2/4 2017 Canada Rd 3:46:34 51.66 1,762 -Sun, 6/9 2013 Silicon Valley Tour de Cure 75K 3:58:37 51.63 2,929 -Sun, 9/3 2017 Morning Ride 4:22:45 51.31 2,526 -Mon, 7/27 2015 Palo Alto Cycling 3:48:23 50.93 1,306 -Wed, 7/29 2015 Palo Alto, CA 3:47:53 50.92 1,873 -Sun, 6/26 2016 Los Gatos 3:16:33 50.78 1,181 -Mon, 8/10 2015 Palo Alto, CA 3:41:54 50.73 1,325 -Sat, 10/15 2016 Los Gatos 3:28:16 50.64 1,368 -Tue, 12/19 2017 Los Gatos 3:46:37 50.49 1,929 -Fri, 6/28 2013 Kaffeehaus San Mateo 3:38:11 50.38 1,028 -Thu, 10/5 2017 Big Sur 4:33:33 50.38 4,528 -Sun, 11/20 2016 Lunch Ride 3:32:01 50.13 1,847 -Sat, 1/13 2018 Canada to Coyote Point 3:59:24 50.09 1,499 -Fri, 1/13 2017 Los Gatos Creek 3:52:28 50.01 1,598 -Sun, 6/11 2017 Tour de Cure with Juliet 4:11:01 49.49 2,713 -Sun, 9/11 2016 Santa Cruz via Mountain Charlie Rd 4:03:04 48.78 3,300 -Sat, 7/23 2016 Morning Ride 3:30:50 48.45 1,063 -Fri, 3/25 2016 Morning Ride 3:39:53 47.93 2,438 -Sat, 8/20 2016 Tour de Menlo with Juliet 3:30:24 47.52 2,133 -Wed, 11/11 2015 Morning Ride 3:45:01 46.54 2,559 -Fri, 7/21 2017 Morning Ride 3:35:00 46.19 748 -Sat, 9/2 2017 Canada / Sheep Camp Trail 3:45:45 45.72 2,034 -Mon, 1/19 2015 Canada Rd, etc. 2:57:14 45.64 1,836 -Sun, 11/27 2016 Morning Ride 3:04:11 45.60 1,378 -Sun, 7/2 2017 Afternoon Ride 3:10:38 45.30 581 -Sat, 10/3 2015 Los Gatos 2:59:26 45.21 1,148 -Sat, 7/2 2016 Morning Ride 3:23:20 45.21 1,991 -Mon, 1/16 2017 Morning Ride 3:34:28 45.15 1,434 -Sun, 1/15 2017 Lunch Ride 3:18:47 45.12 1,873 -Sun, 4/9 2017 Lunch Ride 3:10:28 44.76 636 -Sun, 10/15 2017 Los Gatos 2:51:53 44.71 1,437 -Tue, 10/8 2013 work 3:22:55 44.58 961 -Thu, 7/16 2015 Mountain View, California 3:29:45 44.34 1,339 -Sat, 10/28 2017 Mindego Ridge Winery 4:17:45 44.28 4,331 -Sun, 9/2 2018 Saratoga 3:31:02 44.04 1,900 -Fri, 9/23 2016 Los Gatos 2:53:22 43.93 1,339 -Sat, 7/25 2015 Palo Alto, California 4:02:27 43.62 4,819 -Sat, 7/8 2017 Dumbarton / Niles 3:14:55 43.54 627 -Sat, 6/9 2018 Kings Mountain 3:41:47 43.47 3,543 -Fri, 3/10 2017 Morning Ride 4:13:51 43.22 4,554 -Sat, 3/26 2016 Morning Ride 3:36:57 43.18 3,173 -Sun, 10/18 2015 Afternoon Ride 3:00:02 43.04 2,323 -Sat, 9/20 2014 Kings Mountain 3:26:36 43.00 3,299 -Sat, 8/11 2018 Bike Ride Northwest Day 7 3:25:20 42.39 2,241 -Sat, 3/18 2017 Morning Ride 3:37:01 42.29 2,231 -Sat, 7/11 2015 Walnut Creek, California 3:09:38 42.29 3,284 -Thu, 1/22 2015 OLH, etc. 3:25:09 42.15 2,957 -Sun, 11/9 2014 Palo Alto, CA 3:24:57 42.10 3,096 -Sun, 5/15 2016 Afternoon Ride 2:55:51 41.98 1,693 -Sat, 3/25 2017 Morning Ride 3:37:45 41.94 2,874 -Sun, 6/14 2015 Palo Alto, CA 2:46:38 41.67 1,086 -Sat, 7/28 2018 Coyote Hills / Dumbarton 3:25:39 41.66 1,463 -Sun, 8/7 2016 Canada 2:42:58 41.19 1,526 -Sat, 2/11 2017 Morning Ride 3:19:34 41.16 2,172 -Sun, 12/3 2017 Bay Trail 2:53:58 40.86 568 -Mon, 5/29 2017 Morning Ride 3:52:04 40.83 3,678 -Sat, 2/18 2017 Lunch Ride 3:01:13 40.82 630 -Wed, 11/9 2016 Lunch Ride 2:52:06 40.82 1,667 -Sat, 5/20 2017 Lunch Ride 3:07:43 40.82 709 -Sat, 3/24 2018 Morning Ride 2:56:29 40.69 561 -Sun, 11/19 2017 Belmont / Bay Trail 2:53:36 40.60 453 -Sun, 3/26 2017 Lunch Ride 2:54:38 40.57 518 -Sat, 5/25 2019 Crestview 3:17:16 40.56 2,890 -Sat, 9/16 2017 Tour de Coop 3:24:53 40.55 1,125 -Thu, 9/3 2015 Morning Ride 3:02:58 40.40 1,181 -Sun, 2/5 2017 Morning Ride 2:53:48 40.40 1,608 -Sun, 10/23 2016 Lunch Ride 2:41:33 40.33 1,699 -Sat, 12/8 2018 Morning Ride 3:27:10 40.33 2,300 -Sun, 2/12 2017 Lunch Ride 3:39:38 40.27 2,717 -Sat, 12/2 2017 Dumbarton / Niles 2:57:48 40.26 610 -Sun, 11/12 2017 Morning Ride 2:54:57 40.24 656 -Sat, 11/15 2014 Kings Mountain again 3:25:14 40.18 2,952 -Sun, 10/9 2016 Lunch Ride 2:41:06 40.14 1,106 -Mon, 11/6 2017 Morning Ride 2:48:12 40.13 509 -Sat, 12/17 2016 Morning Ride 3:11:31 40.10 2,133 -Fri, 1/12 2018 Morning Ride 2:58:30 40.09 427 -Sun, 12/18 2016 Morning Ride 3:04:01 40.03 1,886 -Sun, 1/25 2015 Canada Rd 2:45:51 39.99 1,772 -Sat, 6/21 2014 Sierra to the Sea Day 7 3:36:53 39.77 3,325 -Sat, 9/23 2017 Canada / Crestview 3:25:13 39.77 2,444 -Fri, 12/27 2013 OLH to Page Mill 3:24:14 39.73 3,365 -Fri, 10/30 2015 OLH / West Alpine 3:28:46 39.51 4,505 -Sat, 9/5 2015 Ring of Fire 3:31:55 39.18 3,553 -Wed, 8/28 2013 Train Track Crash 2:50:55 39.16 928 -Sun, 2/22 2015 Canada Rd Plus 2:57:25 39.14 1,834 -Sat, 4/8 2017 Lunch Ride 3:28:52 39.09 3,117 -Sat, 11/14 2015 Morning Ride 3:09:31 39.00 2,254 -Sun, 3/1 2015 Canada Rd 2:38:11 38.81 1,856 -Wed, 11/22 2017 Morning Ride 2:55:22 38.77 892 -Sat, 10/29 2016 Morning Ride 3:14:22 38.72 2,201 -Sat, 4/6 2019 Coyote Hills / Dumbarton 2:50:52 38.71 650 -Wed, 7/22 2015 Palo Alto, CA 2:55:45 38.68 627 -Mon, 6/22 2015 Palo Alto, CA 2:51:10 38.55 725 -Fri, 12/16 2016 Morning Ride 2:54:02 38.46 1,053 -Sun, 10/7 2018 Westridge, Canada with Ted 3:09:25 38.29 1,667 -Sat, 2/21 2015 Kings Mountain 3:13:05 38.21 3,015 -Sat, 9/12 2015 Morning Ride 3:06:03 38.20 3,342 -Sun, 2/15 2015 Canada Rd 2:35:29 38.06 1,470 -Sun, 11/16 2014 Canada Rd 2:30:41 37.88 1,548 -Sun, 10/5 2014 Canada Road Sunday 2:39:39 37.84 1,644 -Mon, 11/23 2015 Home from Pigeon Point 3:29:29 37.70 3,133 -Sat, 1/6 2018 Morning Ride 2:53:45 37.66 1,503 -Sun, 1/7 2018 Dumbarton / Niles 2:39:17 37.64 472 -Sun, 12/1 2013 Mt. Hamilton 3:47:01 37.56 4,921 -Sun, 3/18 2018 Lunch Ride 2:47:48 37.52 827 -Sat, 9/22 2018 Morning Ride 2:52:01 37.45 1,798 -Sun, 10/22 2017 Afternoon Ride 2:33:27 37.41 1,024 -Sun, 5/4 2014 MTV-Woodside 2:41:21 37.41 1,495 -Sat, 1/23 2016 Morning Ride 3:39:10 37.35 2,949 -Sun, 9/16 2018 Dumbarton / Niles 2:38:41 37.26 722 -Sun, 6/10 2018 Canada / Emerald Hills 3:21:31 37.26 2,621 -Mon, 5/4 2015 Commute 2:43:06 37.25 189 -Sat, 7/7 2018 Runymede Trail 3:01:51 37.07 1,834 -Sun, 12/11 2016 Afternoon Ride 2:34:13 37.05 1,463 -Sun, 11/29 2015 Mt. Hamilton 3:40:48 37.00 4,902 -Sat, 1/30 2016 Morning Ride 2:31:37 36.88 1,345 -Mon, 2/18 2019 Niles 2:39:39 36.83 571 -Sun, 1/21 2018 Alviso 2:35:58 36.82 404 -Sat, 8/26 2017 Lunch Ride 2:41:49 36.71 1,115 -Sat, 9/13 2014 Emerald Hills 2:55:30 36.71 2,143 -Sun, 5/22 2016 Canada 2:11:12 36.68 1,332 -Sat, 11/25 2017 Mt. Hamilton 3:41:22 36.65 4,806 -Fri, 10/13 2017 Voyage 2:46:15 36.63 505 -Tue, 5/12 2015 Palo Alto, CA 2:47:15 36.54 499 -Mon, 4/20 2015 Commute 2:57:44 36.31 119 -Fri, 5/13 2016 Morning Ride 2:41:03 36.21 456 -Sat, 12/3 2016 Morning Ride 3:08:37 36.20 2,881 -Sun, 12/7 2014 Palo Alto, CA 2:43:35 36.16 1,569 -Sun, 9/30 2018 Canada 2:48:41 36.12 1,480 -Fri, 3/30 2018 Morning Ride 2:38:07 36.11 285 -Sun, 11/5 2017 Lunch Ride 3:24:01 35.91 2,231 -Sun, 2/8 2015 Commute 2:28:46 35.85 1,142 -Sat, 3/30 2019 Morning Ride 3:23:55 35.78 2,861 -Sun, 11/22 2015 Pigeon Point 3:14:41 35.73 3,230 -Mon, 9/19 2016 Sunnyvale 2:37:04 35.68 745 -Thu, 4/9 2015 Palo Alto Cycling 2:35:47 35.51 117 -Sun, 8/21 2016 Canada 2:20:21 35.42 1,348 -Tue, 10/28 2014 Woodside 2:36:36 35.39 1,560 -Sun, 1/27 2019 Crystal Springs Dam Bridge 2:28:21 35.38 1,480 -Thu, 4/26 2018 San Mateo 2:38:26 35.31 666 -Thu, 12/22 2016 Half Moon Bay 2:53:00 35.26 2,671 -Tue, 6/7 2016 Los Altos 2:41:13 35.18 1,070 -Sat, 3/15 2014 Canada - Steve 2:51:50 35.01 1,607 -Sun, 12/6 2015 Canada Rd 2:15:05 34.67 1,237 -Wed, 9/13 2017 Healdburg / Jimtown 2:08:03 34.45 912 -Thu, 10/2 2014 Mountain View, California 3:08:06 34.28 2,864 -Sat, 2/1 2014 OLH to Page Mill 3:07:43 34.26 3,099 -Sat, 10/21 2017 Pescadero 5:20:08 67.05 4,938 -Sat, 7/30 2016 Morning Ride 2:50:15 34.19 2,799 -Mon, 7/10 2017 Morning Ride 2:38:59 34.18 984 -Thu, 7/31 2014 Work commute 2:36:53 34.15 564 -Sat, 12/15 2018 Lunch Ride 2:50:19 34.10 1,683 -Sat, 1/20 2018 Afternoon Ride 2:28:18 34.02 525 -Sun, 12/4 2016 Morning Ride 2:29:29 34.00 1,161 -Sat, 10/8 2016 Lunch Ride 2:25:17 33.95 1,375 -Thu, 10/16 2014 Commute 2:40:01 33.76 615 -Fri, 11/11 2016 OLH 2:44:59 33.69 2,175 -Sat, 9/3 2016 Morning Ride 3:07:28 33.65 2,792 -Sun, 4/19 2015 Canada Rd 2:23:17 33.56 1,466 -Sat, 8/12 2017 Afternoon Ride 2:41:24 33.54 633 -Sat, 2/14 2015 Rancho San Antonio 2:45:23 33.53 2,075 -Sat, 2/27 2016 Morning Ride 2:47:15 33.51 2,005 -Sun, 9/17 2017 Canada and off road 2:36:04 33.50 1,772 -Sat, 2/24 2018 Los Altos Hills on and off road 3:10:47 33.49 2,343 -Sun, 1/26 2014 Canada Rd 2:05:59 33.12 1,446 -Sat, 4/27 2019 Morning Ride 2:31:48 33.04 1,722 -Sun, 5/12 2019 Peninsula Bike Trail to Bay Trail 2:29:05 33.00 846 -Sun, 8/20 2017 Lunch Ride 2:23:02 32.96 1,407 -Sun, 3/6 2016 Lunch Ride 2:57:54 32.86 2,566 -Sun, 3/5 2017 Lunch Ride 2:57:09 32.82 1,713 -Sat, 12/16 2017 Morning Ride 2:48:58 32.72 2,320 -Tue, 8/23 2016 Morning Ride 2:32:15 32.61 354 -Fri, 12/9 2016 Morning Ride 3:04:52 32.55 2,365 -Sun, 11/1 2015 Lunch Ride 2:20:35 32.49 1,553 -Sat, 4/14 2018 Lunch Ride 2:21:49 32.43 666 -Fri, 1/26 2018 Morning Ride 2:23:38 32.41 295 -Sun, 8/16 2015 Palo Alto, CA 2:29:53 32.40 1,650 -Sat, 5/9 2015 OLH 2:30:17 32.33 2,788 -Wed, 1/31 2018 Morning Ride 3:04:15 32.28 2,526 -Sat, 4/4 2015 Rancho San Antonio 2:55:37 32.26 2,136 -Sat, 3/16 2019 Morning Ride 2:58:43 32.26 2,316 -Sun, 12/27 2015 Canada Rd with Juliet 2:24:56 32.22 1,491 -Mon, 5/18 2015 Palo Alto, CA 2:24:45 32.22 807 -Sun, 4/8 2018 Lunch Ride 2:21:54 32.18 1,296 -Sun, 5/3 2015 Canada Rd 2:12:24 32.02 1,384 -Wed, 6/17 2015 Mountain View, California 2:13:42 32.01 650 -Sat, 11/7 2015 Crystal Springs Part 1 2:15:00 32.00 1000 -Sun, 8/23 2015 Afternoon Ride 2:24:29 31.90 2,444 -Sun, 4/12 2015 Palo Alto Cycling 2:01:49 31.76 1,210 -Sun, 11/13 2016 Afternoon Ride 2:23:46 31.71 1,273 -Fri, 10/7 2016 Morning Ride 2:26:46 31.65 2,382 -Thu, 8/15 2013 Palo Alto, CA 2:19:27 31.64 555 -Wed, 10/30 2013 work 2:22:00 31.55 995 -Sun, 9/18 2016 Morning Ride 2:20:46 31.48 1,506 -Tue, 6/18 2013 work etc (headwinds) 2:03:34 31.48 809 -Sun, 3/11 2018 Lunch Ride 2:23:14 31.42 686 -Sat, 3/12 2016 Lunch Ride 2:10:01 31.39 1,198 -Sun, 1/22 2017 Dumbarton 2:23:36 31.27 591 -Sat, 9/29 2018 Kings 2:36:47 31.23 1,949 -Sat, 3/23 2019 Morning Ride 2:33:27 31.19 1,529 -Sat, 4/13 2019 Alviso 2:10:04 31.18 397 -Mon, 11/10 2014 Commute 2:22:39 31.18 800 -Mon, 5/25 2015 Palo Alto, CA 2:19:26 31.14 1,591 -Mon, 2/16 2015 Portola Valley Loop 2:09:15 31.11 1,283 -Wed, 9/21 2016 Morning Ride 2:16:04 31.09 551 -Sat, 3/11 2017 Afternoon Ride 2:19:05 31.04 1,368 -Sun, 7/29 2018 Lunch Ride 2:26:56 30.92 1,578 -Fri, 11/10 2017 Lunch Ride 2:20:46 30.92 312 -Mon, 6/12 2017 Morning Ride 2:20:16 30.90 554 -Thu, 12/29 2016 Morning Ride 2:13:46 30.86 1,083 -Sat, 4/22 2017 Lunch Ride 2:21:31 30.80 1,237 -Mon, 9/7 2015 Healdsburg 2:09:43 30.76 1,037 -Sat, 1/14 2017 Afternoon Ride 2:12:37 30.71 466 -Mon, 9/4 2017 Kings Mountain 2:40:57 30.69 2,431 -Sun, 8/9 2015 Palo Alto, CA 2:15:39 30.66 1,348 -Sat, 12/12 2015 Lunch Ride 2:16:20 30.56 1,434 -Thu, 12/28 2017 Morning Ride 2:17:38 30.54 430 -Mon, 1/30 2017 Morning Ride 2:19:47 30.49 696 -Sun, 8/27 2017 Lunch Ride 2:16:36 30.47 774 -Fri, 10/12 2018 Lunch Ride 2:16:25 30.44 348 -Sat, 2/28 2015 Palo Alto Cycling 2:40:24 30.37 2,535 -Sat, 6/24 2017 Afternoon Ride 2:06:05 30.31 338 -Tue, 6/2 2015 Mountain View, California 2:09:58 30.30 784 -Mon, 5/27 2019 Canada / Sheep camp / Water dog lake 2:37:11 30.27 1,444 -Sat, 1/2 2016 Los Altos Hills 2:29:48 30.27 1,952 -Fri, 6/23 2017 Morning Ride 2:17:16 30.27 515 -Sun, 4/2 2017 Morning Ride 2:20:47 30.23 407 -Sat, 8/25 2018 Morning Ride 2:22:42 30.22 2,257 -Sun, 9/14 2014 Palo Alto, CA 2:06:48 30.18 1,204 -Sat, 8/19 2017 Lunch Ride 2:06:39 30.17 627 -Sun, 3/12 2017 Morning Ride 2:18:13 30.16 600 -Sat, 11/21 2015 Morning Ride 2:20:54 30.16 1,499 -Wed, 10/12 2016 Morning Ride 2:19:51 30.11 328 -Sun, 7/8 2018 Lunch Ride 2:12:19 30.04 768 -Tue, 11/22 2016 Morning Ride 2:18:57 30.04 518 -Wed, 3/4 2015 Commute 2:15:56 30.02 1,125 -Sun, 3/25 2018 Dumbarton / Coyote Hills / Middle of the Bay 2:31:51 30.01 709 -Tue, 2/14 2017 Morning Ride 2:22:16 29.92 463 -Sun, 5/31 2015 Palo Alto, California 2:35:24 29.85 2,568 -Tue, 11/1 2016 Morning Ride 2:15:55 29.78 515 -Sun, 6/5 2016 Morning Ride 2:18:02 29.70 1,293 -Sun, 5/26 2019 San Carlos / Bay Trail 2:17:51 29.55 620 -Thu, 8/13 2015 Palo Alto, CA 2:13:58 29.51 778 -Sun, 7/14 2013 Palo Alto, CA 2:23:07 29.34 739 -Sun, 4/3 2016 Lunch Ride 2:03:38 29.31 810 -Mon, 3/9 2015 Palo Alto Cycling 1:57:50 29.21 115 -Fri, 2/23 2018 Morning Ride 2:08:28 29.21 166 -Sat, 10/7 2017 Afternoon Ride 2:02:43 29.18 394 -Sat, 1/17 2015 Palo Alto Cycling 2:08:38 29.06 1,587 -Sat, 1/18 2014 Los Altos Hills 2:32:23 29.03 1,918 -Sat, 6/20 2015 Palo Alto, CA 2:06:45 28.87 1,650 -Sun, 11/3 2013 work 2:11:08 28.74 841 -Wed, 8/16 2017 Lunch Ride 2:15:48 28.67 732 -Sat, 1/9 2016 Morning Ride 2:07:35 28.54 1,289 -Sat, 9/19 2015 Los Altos Hills 2:31:24 28.51 1,611 -Wed, 8/12 2015 Palo Alto, CA 2:02:14 28.48 751 -Sun, 7/23 2017 San Francisco 2:57:35 28.43 2,562 -Mon, 8/24 2015 Morning Ride 2:10:31 28.35 427 -Wed, 6/26 2013 work 2:01:29 28.33 627 -Sun, 4/28 2019 Arastradsero 2:29:59 28.21 1,355 -Sun, 7/19 2015 Palo Alto, CA 2:00:27 28.17 1,037 -Fri, 12/28 2018 Lunch Ride with Juliet 2:17:33 28.10 331 -Tue, 5/17 2016 Morning Ride 2:06:55 28.09 551 -Wed, 10/5 2016 Morning Ride 2:10:47 28.05 276 -Wed, 6/22 2016 Morning Ride 2:00:40 27.99 374 -Mon, 3/16 2015 Commute 2:06:51 27.97 70 -Sun, 2/25 2018 Lunch Ride 2:10:54 27.88 958 -Sat, 10/27 2018 Morning Ride 2:29:13 27.88 1,581 -Mon, 6/20 2016 Morning Ride 1:52:13 27.77 531 -Sun, 12/2 2018 Lunch Ride 2:34:14 27.72 1,506 -Mon, 3/19 2018 Morning Ride 2:06:35 27.62 860 -Mon, 12/17 2018 Morning Ride 2:16:12 27.60 246 -Sat, 10/14 2017 Afternoon Ride 2:04:53 27.52 387 -Sat, 10/18 2014 Bikepacking Monte Bello 2:54:20 27.44 2,992 -Sun, 4/15 2018 Morning Ride 2:17:16 27.43 1,667 -Mon, 4/10 2017 Morning Ride 2:03:19 27.40 282 -Tue, 5/7 2019 Lunch Ride 2:07:55 27.37 820 -Sun, 11/25 2018 Afternoon Ride 1:56:26 27.34 203 -Wed, 4/29 2015 Commute 2:03:14 27.33 75 -Tue, 1/17 2017 Morning Ride 2:07:17 27.30 535 -Tue, 9/30 2014 Palo Alto, CA 2:07:33 27.28 636 -Sat, 9/17 2016 TourDeCoop.org 2:04:27 27.24 479 -Sat, 1/12 2019 Lunch Ride 1:58:47 27.24 1,079 -Mon, 3/30 2015 Woodside Loop 1:47:46 27.22 1,081 -Tue, 11/17 2015 Afternoon Ride 2:04:48 27.12 830 -Sun, 8/13 2017 Woodside Loop (Battery ran down) 2:00:00 27.00 1,000 -Sat, 1/31 2015 Alpine Rd 2:29:26 26.98 2,362 -Sun, 2/28 2016 Woodside Loop 1:43:34 26.93 843 -Sun, 4/1 2018 Lunch Ride 2:30:35 26.92 1,831 -Wed, 5/3 2017 Palo Alto Road Cycling 2:06:05 26.86 1,411 -Fri, 9/9 2016 Morning Ride 1:56:44 26.85 1,086 -Sun, 9/23 2018 Morning Ride 1:57:48 26.83 512 -Sat, 10/4 2014 Woodside Loop 1:52:09 26.71 1,213 -Thu, 5/7 2015 Commute 1:55:36 26.58 68 -Sun, 3/24 2019 OLH / WOLH 2:42:45 26.57 2,267 -Thu, 8/31 2017 Morning Ride 2:00:02 26.54 817 -Sun, 4/24 2016 Afternoon Ride 1:50:08 26.53 935 -Sun, 12/17 2017 Lunch Ride 1:50:51 26.53 217 -Tue, 7/4 2017 Lunch Ride 1:53:04 26.50 459 -Tue, 5/8 2018 Afternoon Ride 2:34:11 26.50 1,791 -Tue, 5/26 2015 Palo Alto, CA 2:01:47 26.50 591 -Thu, 8/4 2016 Afternoon Ride 1:55:47 26.49 659 -Wed, 6/8 2016 Lunch Ride 1:48:19 26.48 597 -Sun, 9/9 2018 Lunch Ride 2:00:49 26.46 479 -Sat, 5/21 2016 Maker Faire 1:57:14 26.44 207 -Wed, 4/1 2015 Commute 1:55:15 26.44 71 -Wed, 6/29 2016 Morning Ride 1:49:33 26.39 561 -Sat, 9/9 2017 Lunch Ride 1:59:01 26.38 1,112 -Fri, 6/3 2016 Morning Ride 1:56:57 26.38 502 -Fri, 3/31 2017 Morning Ride 2:01:41 26.36 495 -Sat, 12/13 2014 Westridge (up the back way; after the rain) 2:01:01 26.35 1,495 -Sun, 4/14 2019 Huddart - Paul Hopkins 2:08:57 26.34 1,601 -Sat, 6/29 2013 Untitled 1:48:50 26.33 1,091 -Sat, 4/25 2015 Woodside 1:54:30 26.28 1,220 -Tue, 1/31 2017 Morning Ride 2:01:18 26.27 384 -Tue, 6/28 2016 Morning Ride 1:55:31 26.27 571 -Sat, 5/19 2018 Morning Ride 2:00:24 26.25 958 -Thu, 6/30 2016 Morning Ride 1:52:16 26.24 499 -Sun, 2/19 2017 Afternoon Ride 1:56:46 26.23 1,138 -Thu, 8/18 2016 Morning Ride 1:58:06 26.23 712 -Sun, 4/20 2014 Bay Trail / Ponderosa Park 1:52:52 26.23 102 -Sun, 5/10 2015 Arastadero 1:48:58 26.22 1,148 -Thu, 5/19 2016 Morning Ride 1:52:56 26.14 938 -Mon, 12/31 2018 Morning Ride 2:00:24 26.11 545 -Fri, 12/15 2017 Afternoon Ride 1:51:36 26.07 226 -Sun, 1/18 2015 Woodside 1:38:16 26.02 1,257 -Mon, 10/3 2016 Morning Ride 1:54:45 26.01 646 -Tue, 7/14 2015 Mountain View, California 2:02:36 25.99 502 -Fri, 12/18 2015 Lunch Ride 1:58:57 25.93 909 -Tue, 6/13 2017 Morning Ride 1:55:30 25.89 289 -Mon, 8/4 2014 To the Sea 2:17:11 25.87 2,080 -Wed, 9/25 2013 work and with kris 2:07:40 25.81 593 -Sun, 9/13 2015 Afternoon Ride 1:49:31 25.78 1,152 -Sun, 3/3 2019 Afternoon Ride 2:00:06 25.73 390 -Thu, 1/26 2017 Morning Ride 1:56:09 25.67 259 -Sun, 5/6 2018 Morning Ride 1:55:05 25.67 1,148 -Sat, 7/29 2017 Lunch Ride 1:53:50 25.65 988 -Wed, 8/20 2014 Palo Alto, CA 1:49:35 25.61 497 -Sat, 4/19 2014 Lunch Ride 1:49:37 25.60 1,111 -Mon, 9/3 2018 Afternoon Ride 1:59:08 25.54 331 -Mon, 6/2 2014 Work 1:59:33 25.51 627 -Fri, 12/11 2015 Afternoon Ride 2:12:40 25.44 1,719 -Fri, 2/9 2018 Morning Ride 1:56:11 25.41 318 -Sun, 8/26 2018 Afternoon Ride 1:49:16 25.37 377 -Sun, 7/9 2017 Afternoon Ride 1:50:30 25.36 682 -Mon, 6/26 2017 Morning Ride 1:52:26 25.34 226 -Fri, 9/14 2018 Lunch Ride 1:50:05 25.34 541 -Fri, 3/1 2019 Morning Ride 2:00:23 25.34 121 -Sun, 1/13 2019 Lunch Ride 1:51:40 25.33 715 -Wed, 4/12 2017 Morning Ride 1:59:22 25.33 686 -Wed, 6/14 2017 Morning Ride 1:51:32 25.33 308 -Sat, 11/28 2015 Lunch Ride 1:43:56 25.31 1,122 -Mon, 11/27 2017 Morning Ride 1:55:39 25.31 184 -Fri, 11/28 2014 Palo Alto, CA 1:45:13 25.30 1,112 -Fri, 4/13 2018 Morning Ride 1:53:39 25.28 344 -Fri, 9/15 2017 Morning Ride 1:56:55 25.28 384 -Tue, 12/31 2013 Woodside Loop - Last Ride of the Year 1:45:28 25.27 1,207 -Sun, 10/14 2018 Lunch Ride 2:13:13 25.23 1,421 -Sat, 11/22 2014 Atherton to Woodside 1:43:10 25.21 1,095 -Sat, 6/4 2016 Morning Ride 1:57:23 25.19 922 -Mon, 8/8 2016 Morning Ride 1:52:38 25.17 423 -Fri, 11/6 2015 Morning Ride 1:51:45 25.15 538 -Mon, 12/18 2017 Afternoon Ride 1:52:56 25.15 741 -Fri, 6/24 2016 Foothill Expway 1:35:25 25.11 623 -Sat, 6/25 2016 Afternoon Ride 1:44:43 25.10 568 -Sat, 1/25 2014 Woodside 1:33:38 25.08 1,243 -Tue, 4/29 2014 Woodside 1:46:03 25.08 886 -Thu, 4/13 2017 Morning Ride 2:06:13 25.06 266 -Sun, 6/12 2016 Morning Ride 1:49:25 25.04 387 -Thu, 6/8 2017 Morning Ride 1:56:19 25.02 285 -Thu, 10/13 2016 Morning Ride 1:47:30 25.02 253 -Sun, 1/19 2014 Palo Alto, CA 1:36:55 25.01 1,201 -Mon, 9/5 2016 Afternoon Ride 1:42:56 25.00 902 -Sat, 11/8 2014 Atherton / Woodside 1:40:51 25.00 972 -Sat, 4/11 2015 Woodside 1:32:24 24.73 1,035 -Sat, 6/1 2013 Woodside Loop 1:41:20 24.45 1,125 -Sun, 6/16 2013 Woodside Loop 1:38:19 24.52 1,137 -Sun, 6/23 2013 Climb 2:16:29 24.30 2,001 -Sat, 7/13 2013 Doug's Event 1:51:55 21.35 1,677 -Sun, 8/4 2013 Kris's first trike ride 1:51:21 20.96 988 -Sun, 11/24 2013 Alpine Rd 1:42:36 21.02 1,289 -Fri, 11/29 2013 Woodside Loop 1:33:36 22.75 1,011 +Sat, 5/13/2017 Morgan Hill iCare Classic 7:27:21 100.05 4,596 +Sat, 5/12/2018 ICare Classic, Morgan Hill 6:47:46 91.29 4,160 +Sat, 5/6/2017 Wine Country Century 7:15:22 89.49 5,246 +Fri, 8/10/2018 Bike Ride Northwest Day 6 6:14:18 84.70 4,380 +Sat, 10/1/2016 Half Moon Bay overnight campout 7:30:38 80.07 6,039 +Tue, 8/7/2018 Bike Ride Northwest Day 3 6:10:39 78.96 5,092 +Sun, 6/15/2014 Sierra to the Sea Day 1 5:34:06 78.53 4,777 +Sat, 4/23/2016 Wildflower Century 5:36:10 77.22 4,193 +Mon, 6/16/2014 Sierra to the Sea Day 2 5:35:06 74.68 2,451 +Fri, 8/12/2016 Half Moon Bay / Harvey's 73/7300 Birthday Ride 6:44:14 74.35 6,610 +Wed, 10/14/2015 Half Moon Bay 6:07:55 72.97 7,644 +Fri, 1/27/2017 Morning Ride 5:16:50 70.07 2,539 +Sat, 8/15/2015 Coyote Creek Trail with Juliet 5:49:01 69.30 1,745 +Tue, 6/17/2014 Sierra to the Sea Day 3 4:48:41 68.64 825 +Thu, 8/9/2018 Bike Ride Northwest Day 5 4:58:07 68.41 3,862 +Sat, 4/15/2017 Pescadero 6:13:12 68.34 6,130 +Sun, 6/4/2017 Sequoia Challenge 6:17:25 66.52 7,520 +Wed, 8/5/2015 Palo Alto, CA 4:56:15 66.33 2,054 +Fri, 8/28/2015 Pescadaro via OLH 5:18:51 66.01 6,137 +Fri, 11/20/2015 Los Gatos 5:29:40 65.73 5,380 +Sun, 6/3/2018 The Sequoia 5:58:15 64.92 6,677 +Thu, 6/19/2014 Sierra to the Sea Day 5 4:32:55 63.69 2,584 +Mon, 8/3/2015 Palo Alto, CA 4:45:09 63.61 1,877 +Fri, 6/10/2016 Morning Ride 4:21:38 62.84 3,196 +Sun, 6/2/2013 Woodside Loop and Baylands 4:27:42 62.14 2,169 +Wed, 6/12/2013 ride 4:39:46 61.39 2,207 +Sat, 4/18/2015 Tunitas + Lobitos Creeks 5:14:38 61.27 6,611 +Sat, 11/11/2017 Los Gatos / Bay Trail 4:26:24 60.74 1,316 +Sun, 4/17/2016 La Honda / Skyline 5:10:41 60.35 4,551 +Mon, 7/3/2017 Los Gatos / Bay Trail 4:25:41 60.28 1,329 +Sat, 4/16/2016 OLH / San Gregorio / Tunitas 4:57:40 60.12 4,744 +Sun, 1/29/2017 Los Gatos / Guadalupe / San Tomas / Bay Trails 4:33:38 60.11 1,447 +Mon, 11/26/2018 Lunch Ride Los Gatos 4:21:35 60.03 1,070 +Sat, 10/31/2015 Saratoga Ramble 5:01:57 59.11 3,528 +Sat, 8/18/2018 Tour de Menlo 4:03:48 58.81 2,467 +Sat, 4/26/2014 OLH / Tunitas Creek 5:15:26 58.69 6,742 +Sat, 6/15/2013 Palo Alto to Santa Cruz 4:27:54 58.42 4,431 +Sat, 10/11/2014 OLH / Tunitas 5:05:23 58.29 6,044 +Sat, 7/9/2016 Santa Cruz 3:50:23 58.23 4,042 +Wed, 6/18/2014 Sierra to the Sea Day 4 4:57:53 57.64 5,561 +Tue, 7/7/2015 Palo Alto, CA 4:13:20 57.60 1,280 +Mon, 8/6/2018 Bike Ride Northwest Day 2 4:35:48 57.58 4,514 +Su, 1/26/2020 Los Gatos 4:24:58 57.05 1,086 +Fri, 6/20/2014 Sierra to the Sea Day 6 4:33:39 56.91 4,453 +Sat, 6/10/2017 Los Gatos / Creek Trails 3:50:24 56.28 1,365 +Sat, 3/4/2017 Lunch Ride 3:58:25 56.26 1,378 +Sun, 8/5/2018 Bike Ride Northwest Day 1 3:34:42 55.77 1,824 +Sat, 8/13/2016 Petaluma / Point Reyes 4:30:12 54.75 5,286 +Sun, 6/7/2015 Tour de Cure 3:59:47 54.65 2,748 +Wed, 12/16/2015 Los Gatos 4:00:41 53.86 2,595 +Fri, 6/2/2017 Morning Ride 3:57:45 53.49 1,375 +Sun, 6/8/2014 Tour de Cure 75K 4:03:05 53.10 2,596 +Sun, 11/6/2016 Los Gatos 3:38:28 52.49 1,263 +Fri, 2/10/2017 Morning Ride 3:52:39 52.02 1,739 +Sat, 2/23/2019 Crystal Springs Dam 4:02:11 51.93 1,946 +Fri, 8/11/2017 Saratoga with Peter H 4:30:31 51.74 2,871 +Sat, 2/4/2017 Canada Rd 3:46:34 51.66 1,762 +Sun, 6/9/2013 Silicon Valley Tour de Cure 75K 3:58:37 51.63 2,929 +Sun, 9/3/2017 Morning Ride 4:22:45 51.31 2,526 +Mon, 7/27/2015 Palo Alto Cycling 3:48:23 50.93 1,306 +Wed, 7/29/2015 Palo Alto, CA 3:47:53 50.92 1,873 +Sun, 6/26/2016 Los Gatos 3:16:33 50.78 1,181 +Mon, 8/10/2015 Palo Alto, CA 3:41:54 50.73 1,325 +Sat, 10/15/2016 Los Gatos 3:28:16 50.64 1,368 +Tue, 12/19/2017 Los Gatos 3:46:37 50.49 1,929 +Fri, 6/28/2013 Kaffeehaus San Mateo 3:38:11 50.38 1,028 +Thu, 10/5/2017 Big Sur 4:33:33 50.38 4,528 +Sun, 11/20/2016 Lunch Ride 3:32:01 50.13 1,847 +Sat, 1/13/2018 Canada to Coyote Point 3:59:24 50.09 1,499 +Fri, 1/13/2017 Los Gatos Creek 3:52:28 50.01 1,598 +Sun, 6/11/2017 Tour de Cure with Juliet 4:11:01 49.49 2,713 +Sun, 9/11/2016 Santa Cruz via Mountain Charlie Rd 4:03:04 48.78 3,300 +Sat, 7/23/2016 Morning Ride 3:30:50 48.45 1,063 +Fri, 3/25/2016 Morning Ride 3:39:53 47.93 2,438 +Sat, 8/20/2016 Tour de Menlo with Juliet 3:30:24 47.52 2,133 +Wed, 11/11/2015 Morning Ride 3:45:01 46.54 2,559 +Fri, 7/21/2017 Morning Ride 3:35:00 46.19 748 +Sat, 9/2/2017 Canada / Sheep Camp Trail 3:45:45 45.72 2,034 +Mon, 1/19/2015 Canada Rd, etc. 2:57:14 45.64 1,836 +Sun, 11/27/2016 Morning Ride 3:04:11 45.60 1,378 +Sun, 7/2/2017 Afternoon Ride 3:10:38 45.30 581 +Sat, 10/3/2015 Los Gatos 2:59:26 45.21 1,148 +Sat, 7/2/2016 Morning Ride 3:23:20 45.21 1,991 +Mon, 1/16/2017 Morning Ride 3:34:28 45.15 1,434 +Sun, 1/15/2017 Lunch Ride 3:18:47 45.12 1,873 +Sun, 4/9/2017 Lunch Ride 3:10:28 44.76 636 +Sun, 10/15/2017 Los Gatos 2:51:53 44.71 1,437 +Tue, 10/8/2013 work 3:22:55 44.58 961 +Thu, 7/16/2015 Mountain View, California 3:29:45 44.34 1,339 +Sat, 10/28/2017 Mindego Ridge Winery 4:17:45 44.28 4,331 +Sun, 9/2/2018 Saratoga 3:31:02 44.04 1,900 +Fri, 9/23/2016 Los Gatos 2:53:22 43.93 1,339 +Sat, 7/25/2015 Palo Alto, California 4:02:27 43.62 4,819 +Sat, 7/8/2017 Dumbarton / Niles 3:14:55 43.54 627 +Sat, 6/9/2018 Kings Mountain 3:41:47 43.47 3,543 +Fri, 3/10/2017 Morning Ride 4:13:51 43.22 4,554 +Sat, 3/26/2016 Morning Ride 3:36:57 43.18 3,173 +Sun, 10/18/2015 Afternoon Ride 3:00:02 43.04 2,323 +Sat, 9/20/2014 Kings Mountain 3:26:36 43.00 3,299 +Sat, 8/11/2018 Bike Ride Northwest Day 7 3:25:20 42.39 2,241 +Sat, 3/18/2017 Morning Ride 3:37:01 42.29 2,231 +Sat, 7/11/2015 Walnut Creek, California 3:09:38 42.29 3,284 +Thu, 1/22/2015 OLH, etc. 3:25:09 42.15 2,957 +Sun, 11/9/2014 Palo Alto, CA 3:24:57 42.10 3,096 +Sun, 5/15/2016 Afternoon Ride 2:55:51 41.98 1,693 +Sat, 3/25/2017 Morning Ride 3:37:45 41.94 2,874 +Sun, 6/14/2015 Palo Alto, CA 2:46:38 41.67 1,086 +Sat, 7/28/2018 Coyote Hills / Dumbarton 3:25:39 41.66 1,463 +Sun, 8/7/2016 Canada 2:42:58 41.19 1,526 +Sat, 2/11/2017 Morning Ride 3:19:34 41.16 2,172 +Sun, 12/3/2017 Bay Trail 2:53:58 40.86 568 +Mon, 5/29/2017 Morning Ride 3:52:04 40.83 3,678 +Sat, 2/18/2017 Lunch Ride 3:01:13 40.82 630 +Wed, 11/9/2016 Lunch Ride 2:52:06 40.82 1,667 +Sat, 5/20/2017 Lunch Ride 3:07:43 40.82 709 +Sat, 3/24/2018 Morning Ride 2:56:29 40.69 561 +Sun, 11/19/2017 Belmont / Bay Trail 2:53:36 40.60 453 +Sun, 3/26/2017 Lunch Ride 2:54:38 40.57 518 +Sat, 5/25/2019 Crestview 3:17:16 40.56 2,890 +Sat, 9/16/2017 Tour de Coop 3:24:53 40.55 1,125 +Thu, 9/3/2015 Morning Ride 3:02:58 40.40 1,181 +Sun, 2/5/2017 Morning Ride 2:53:48 40.40 1,608 +Sun, 10/23/2016 Lunch Ride 2:41:33 40.33 1,699 +Sat, 12/8/2018 Morning Ride 3:27:10 40.33 2,300 +Sun, 2/12/2017 Lunch Ride 3:39:38 40.27 2,717 +Sat, 12/2/2017 Dumbarton / Niles 2:57:48 40.26 610 +Sun, 11/12/2017 Morning Ride 2:54:57 40.24 656 +Sat, 11/15/2014 Kings Mountain again 3:25:14 40.18 2,952 +Sun, 10/9/2016 Lunch Ride 2:41:06 40.14 1,106 +Mon, 11/6/2017 Morning Ride 2:48:12 40.13 509 +Sat, 12/17/2016 Morning Ride 3:11:31 40.10 2,133 +Fri, 1/12/2018 Morning Ride 2:58:30 40.09 427 +Sun, 12/18/2016 Morning Ride 3:04:01 40.03 1,886 +Sun, 1/25/2015 Canada Rd 2:45:51 39.99 1,772 +Sat, 6/21/2014 Sierra to the Sea Day 7 3:36:53 39.77 3,325 +Sat, 9/23/2017 Canada / Crestview 3:25:13 39.77 2,444 +Fri, 12/27/2013 OLH to Page Mill 3:24:14 39.73 3,365 +Fri, 10/30/2015 OLH / West Alpine 3:28:46 39.51 4,505 +Sat, 9/5/2015 Ring of Fire 3:31:55 39.18 3,553 +Wed, 8/28/2013 Train Track Crash 2:50:55 39.16 928 +Sun, 2/22/2015 Canada Rd Plus 2:57:25 39.14 1,834 +Sat, 4/8/2017 Lunch Ride 3:28:52 39.09 3,117 +Sat, 11/14/2015 Morning Ride 3:09:31 39.00 2,254 +Sun, 3/1/2015 Canada Rd 2:38:11 38.81 1,856 +Wed, 11/22/2017 Morning Ride 2:55:22 38.77 892 +Sat, 10/29/2016 Morning Ride 3:14:22 38.72 2,201 +Sat, 4/6/2019 Coyote Hills / Dumbarton 2:50:52 38.71 650 +Wed, 7/22/2015 Palo Alto, CA 2:55:45 38.68 627 +Mon, 6/22/2015 Palo Alto, CA 2:51:10 38.55 725 +Fri, 12/16/2016 Morning Ride 2:54:02 38.46 1,053 +Sun, 10/7/2018 Westridge, Canada with Ted 3:09:25 38.29 1,667 +Sat, 2/21/2015 Kings Mountain 3:13:05 38.21 3,015 +Sat, 9/12/2015 Morning Ride 3:06:03 38.20 3,342 +Sun, 2/15/2015 Canada Rd 2:35:29 38.06 1,470 +Sun, 11/16/2014 Canada Rd 2:30:41 37.88 1,548 +Sun, 10/5/2014 Canada Road Sunday 2:39:39 37.84 1,644 +Mon, 11/23/2015 Home from Pigeon Point 3:29:29 37.70 3,133 +Sat, 1/6/2018 Morning Ride 2:53:45 37.66 1,503 +Sun, 1/7/2018 Dumbarton / Niles 2:39:17 37.64 472 +Sun, 12/1/2013 Mt. Hamilton 3:47:01 37.56 4,921 +Sun, 3/18/2018 Lunch Ride 2:47:48 37.52 827 +Sat, 9/22/2018 Morning Ride 2:52:01 37.45 1,798 +Sun, 10/22/2017 Afternoon Ride 2:33:27 37.41 1,024 +Sun, 5/4/2014 MTV-Woodside 2:41:21 37.41 1,495 +Sat, 1/23/2016 Morning Ride 3:39:10 37.35 2,949 +Sun, 9/16/2018 Dumbarton / Niles 2:38:41 37.26 722 +Sun, 6/10/2018 Canada / Emerald Hills 3:21:31 37.26 2,621 +Mon, 5/4/2015 Commute 2:43:06 37.25 189 +Sat, 7/7/2018 Runymede Trail 3:01:51 37.07 1,834 +Sun, 12/11/2016 Afternoon Ride 2:34:13 37.05 1,463 +Sun, 11/29/2015 Mt. Hamilton 3:40:48 37.00 4,902 +Sat, 1/30/2016 Morning Ride 2:31:37 36.88 1,345 +Mon, 2/18/2019 Niles 2:39:39 36.83 571 +Sun, 1/21/2018 Alviso 2:35:58 36.82 404 +Sat, 8/26/2017 Lunch Ride 2:41:49 36.71 1,115 +Sat, 9/13/2014 Emerald Hills 2:55:30 36.71 2,143 +Sun, 5/22/2016 Canada 2:11:12 36.68 1,332 +Sat, 11/25/2017 Mt. Hamilton 3:41:22 36.65 4,806 +Fri, 10/13/2017 Voyage 2:46:15 36.63 505 +Tue, 5/12/2015 Palo Alto, CA 2:47:15 36.54 499 +Mon, 4/20/2015 Commute 2:57:44 36.31 119 +Fri, 5/13/2016 Morning Ride 2:41:03 36.21 456 +Sat, 12/3/2016 Morning Ride 3:08:37 36.20 2,881 +Sun, 12/7/2014 Palo Alto, CA 2:43:35 36.16 1,569 +Sun, 9/30/2018 Canada 2:48:41 36.12 1,480 +Fri, 3/30/2018 Morning Ride 2:38:07 36.11 285 +Sun, 11/5/2017 Lunch Ride 3:24:01 35.91 2,231 +Sun, 2/8/2015 Commute 2:28:46 35.85 1,142 +Sat, 3/30/2019 Morning Ride 3:23:55 35.78 2,861 +Sun, 11/22/2015 Pigeon Point 3:14:41 35.73 3,230 +Mon, 9/19/2016 Sunnyvale 2:37:04 35.68 745 +Thu, 4/9/2015 Palo Alto Cycling 2:35:47 35.51 117 +Sun, 8/21/2016 Canada 2:20:21 35.42 1,348 +Tue, 10/28/2014 Woodside 2:36:36 35.39 1,560 +Sun, 1/27/2019 Crystal Springs Dam Bridge 2:28:21 35.38 1,480 +Thu, 4/26/2018 San Mateo 2:38:26 35.31 666 +Thu, 12/22/2016 Half Moon Bay 2:53:00 35.26 2,671 +Tue, 6/7/2016 Los Altos 2:41:13 35.18 1,070 +Sat, 3/15/2014 Canada - Steve 2:51:50 35.01 1,607 +Sun, 12/6/2015 Canada Rd 2:15:05 34.67 1,237 +Wed, 9/13/2017 Healdburg / Jimtown 2:08:03 34.45 912 +Thu, 10/2/2014 Mountain View, California 3:08:06 34.28 2,864 +Sat, 2/1/2014 OLH to Page Mill 3:07:43 34.26 3,099 +Sat, 10/21/2017 Pescadero 5:20:08 67.05 4,938 +Sat, 7/30/2016 Morning Ride 2:50:15 34.19 2,799 +Mon, 7/10/2017 Morning Ride 2:38:59 34.18 984 +Thu, 7/31/2014 Work commute 2:36:53 34.15 564 +Sat, 12/15/2018 Lunch Ride 2:50:19 34.10 1,683 +Sat, 1/20/2018 Afternoon Ride 2:28:18 34.02 525 +Sun, 12/4/2016 Morning Ride 2:29:29 34.00 1,161 +Sat, 10/8/2016 Lunch Ride 2:25:17 33.95 1,375 +Thu, 10/16/2014 Commute 2:40:01 33.76 615 +Fri, 11/11/2016 OLH 2:44:59 33.69 2,175 +Sat, 9/3/2016 Morning Ride 3:07:28 33.65 2,792 +Sun, 4/19/2015 Canada Rd 2:23:17 33.56 1,466 +Sat, 8/12/2017 Afternoon Ride 2:41:24 33.54 633 +Sat, 2/14/2015 Rancho San Antonio 2:45:23 33.53 2,075 +Sat, 2/27/2016 Morning Ride 2:47:15 33.51 2,005 +Sun, 9/17/2017 Canada and off road 2:36:04 33.50 1,772 +Sat, 2/24/2018 Los Altos Hills on and off road 3:10:47 33.49 2,343 +Sun, 1/26/2014 Canada Rd 2:05:59 33.12 1,446 +Sat, 4/27/2019 Morning Ride 2:31:48 33.04 1,722 +Sun, 5/12/2019 Peninsula Bike Trail to Bay Trail 2:29:05 33.00 846 +Sun, 8/20/2017 Lunch Ride 2:23:02 32.96 1,407 +Sun, 3/6/2016 Lunch Ride 2:57:54 32.86 2,566 +Sun, 3/5/2017 Lunch Ride 2:57:09 32.82 1,713 +Sat, 12/16/2017 Morning Ride 2:48:58 32.72 2,320 +Tue, 8/23/2016 Morning Ride 2:32:15 32.61 354 +Fri, 12/9/2016 Morning Ride 3:04:52 32.55 2,365 +Sun, 11/1/2015 Lunch Ride 2:20:35 32.49 1,553 +Sat, 4/14/2018 Lunch Ride 2:21:49 32.43 666 +Fri, 1/26/2018 Morning Ride 2:23:38 32.41 295 +Sun, 8/16/2015 Palo Alto, CA 2:29:53 32.40 1,650 +Sat, 5/9/2015 OLH 2:30:17 32.33 2,788 +Wed, 1/31/2018 Morning Ride 3:04:15 32.28 2,526 +Sat, 4/4/2015 Rancho San Antonio 2:55:37 32.26 2,136 +Sat, 3/16/2019 Morning Ride 2:58:43 32.26 2,316 +Sun, 12/27/2015 Canada Rd with Juliet 2:24:56 32.22 1,491 +Mon, 5/18/2015 Palo Alto, CA 2:24:45 32.22 807 +Sun, 4/8/2018 Lunch Ride 2:21:54 32.18 1,296 +Sun, 5/3/2015 Canada Rd 2:12:24 32.02 1,384 +Wed, 6/17/2015 Mountain View, California 2:13:42 32.01 650 +Sat, 11/7/2015 Crystal Springs Part 1 2:15:00 32.00 1000 +Sun, 8/23/2015 Afternoon Ride 2:24:29 31.90 2,444 +Sun, 4/12/2015 Palo Alto Cycling 2:01:49 31.76 1,210 +Sun, 11/13/2016 Afternoon Ride 2:23:46 31.71 1,273 +Fri, 10/7/2016 Morning Ride 2:26:46 31.65 2,382 +Thu, 8/15/2013 Palo Alto, CA 2:19:27 31.64 555 +Wed, 10/30/2013 work 2:22:00 31.55 995 +Sun, 9/18/2016 Morning Ride 2:20:46 31.48 1,506 +Tue, 6/18/2013 work etc (headwinds) 2:03:34 31.48 809 +Sun, 3/11/2018 Lunch Ride 2:23:14 31.42 686 +Sat, 3/12/2016 Lunch Ride 2:10:01 31.39 1,198 +Sun, 1/22/2017 Dumbarton 2:23:36 31.27 591 +Sat, 9/29/2018 Kings 2:36:47 31.23 1,949 +Sat, 3/23/2019 Morning Ride 2:33:27 31.19 1,529 +Sat, 4/13/2019 Alviso 2:10:04 31.18 397 +Mon, 11/10/2014 Commute 2:22:39 31.18 800 +Mon, 5/25/2015 Palo Alto, CA 2:19:26 31.14 1,591 +Mon, 2/16/2015 Portola Valley Loop 2:09:15 31.11 1,283 +Wed, 9/21/2016 Morning Ride 2:16:04 31.09 551 +Sat, 3/11/2017 Afternoon Ride 2:19:05 31.04 1,368 +Sun, 7/29/2018 Lunch Ride 2:26:56 30.92 1,578 +Fri, 11/10/2017 Lunch Ride 2:20:46 30.92 312 +Mon, 6/12/2017 Morning Ride 2:20:16 30.90 554 +Thu, 12/29/2016 Morning Ride 2:13:46 30.86 1,083 +Sat, 4/22/2017 Lunch Ride 2:21:31 30.80 1,237 +Mon, 9/7/2015 Healdsburg 2:09:43 30.76 1,037 +Sat, 1/14/2017 Afternoon Ride 2:12:37 30.71 466 +Mon, 9/4/2017 Kings Mountain 2:40:57 30.69 2,431 +Sun, 8/9/2015 Palo Alto, CA 2:15:39 30.66 1,348 +Sat, 12/12/2015 Lunch Ride 2:16:20 30.56 1,434 +Thu, 12/28/2017 Morning Ride 2:17:38 30.54 430 +Mon, 1/30/2017 Morning Ride 2:19:47 30.49 696 +Sun, 8/27/2017 Lunch Ride 2:16:36 30.47 774 +Fri, 10/12/2018 Lunch Ride 2:16:25 30.44 348 +Sat, 2/28/2015 Palo Alto Cycling 2:40:24 30.37 2,535 +Sat, 6/24/2017 Afternoon Ride 2:06:05 30.31 338 +Tue, 6/2/2015 Mountain View, California 2:09:58 30.30 784 +Mon, 5/27/2019 Canada / Sheep camp / Water dog lake 2:37:11 30.27 1,444 +Sat, 1/2/2016 Los Altos Hills 2:29:48 30.27 1,952 +Fri, 6/23/2017 Morning Ride 2:17:16 30.27 515 +Sun, 4/2/2017 Morning Ride 2:20:47 30.23 407 +Sat, 8/25/2018 Morning Ride 2:22:42 30.22 2,257 +Sun, 9/14/2014 Palo Alto, CA 2:06:48 30.18 1,204 +Sat, 8/19/2017 Lunch Ride 2:06:39 30.17 627 +Sun, 3/12/2017 Morning Ride 2:18:13 30.16 600 +Sat, 11/21/2015 Morning Ride 2:20:54 30.16 1,499 +Wed, 10/12/2016 Morning Ride 2:19:51 30.11 328 +Sun, 7/8/2018 Lunch Ride 2:12:19 30.04 768 +Tue, 11/22/2016 Morning Ride 2:18:57 30.04 518 +Wed, 3/4/2015 Commute 2:15:56 30.02 1,125 +Sun, 3/25/2018 Dumbarton / Coyote Hills / Middle of the Bay 2:31:51 30.01 709 +Tue, 2/14/2017 Morning Ride 2:22:16 29.92 463 +Sun, 5/31/2015 Palo Alto, California 2:35:24 29.85 2,568 +Tue, 11/1/2016 Morning Ride 2:15:55 29.78 515 +Sun, 6/5/2016 Morning Ride 2:18:02 29.70 1,293 +Sun, 5/26/2019 San Carlos / Bay Trail 2:17:51 29.55 620 +Thu, 8/13/2015 Palo Alto, CA 2:13:58 29.51 778 +Sun, 7/14/2013 Palo Alto, CA 2:23:07 29.34 739 +Sun, 4/3/2016 Lunch Ride 2:03:38 29.31 810 +Mon, 3/9/2015 Palo Alto Cycling 1:57:50 29.21 115 +Fri, 2/23/2018 Morning Ride 2:08:28 29.21 166 +Sat, 10/7/2017 Afternoon Ride 2:02:43 29.18 394 +Sat, 1/17/2015 Palo Alto Cycling 2:08:38 29.06 1,587 +Sat, 1/18/2014 Los Altos Hills 2:32:23 29.03 1,918 +Sat, 6/20/2015 Palo Alto, CA 2:06:45 28.87 1,650 +Sun, 11/3/2013 work 2:11:08 28.74 841 +Wed, 8/16/2017 Lunch Ride 2:15:48 28.67 732 +Sat, 1/9/2016 Morning Ride 2:07:35 28.54 1,289 +Sat, 9/19/2015 Los Altos Hills 2:31:24 28.51 1,611 +Wed, 8/12/2015 Palo Alto, CA 2:02:14 28.48 751 +Sun, 7/23/2017 San Francisco 2:57:35 28.43 2,562 +Mon, 8/24/2015 Morning Ride 2:10:31 28.35 427 +Wed, 6/26/2013 work 2:01:29 28.33 627 +Sun, 4/28/2019 Arastradsero 2:29:59 28.21 1,355 +Sun, 7/19/2015 Palo Alto, CA 2:00:27 28.17 1,037 +Fri, 12/28/2018 Lunch Ride with Juliet 2:17:33 28.10 331 +Tue, 5/17/2016 Morning Ride 2:06:55 28.09 551 +Wed, 10/5/2016 Morning Ride 2:10:47 28.05 276 +Wed, 6/22/2016 Morning Ride 2:00:40 27.99 374 +Mon, 3/16/2015 Commute 2:06:51 27.97 70 +Sun, 2/25/2018 Lunch Ride 2:10:54 27.88 958 +Sat, 10/27/2018 Morning Ride 2:29:13 27.88 1,581 +Mon, 6/20/2016 Morning Ride 1:52:13 27.77 531 +Sun, 12/2/2018 Lunch Ride 2:34:14 27.72 1,506 +Mon, 3/19/2018 Morning Ride 2:06:35 27.62 860 +Mon, 12/17/2018 Morning Ride 2:16:12 27.60 246 +Sat, 10/14/2017 Afternoon Ride 2:04:53 27.52 387 +Sat, 10/18/2014 Bikepacking Monte Bello 2:54:20 27.44 2,992 +Sun, 4/15/2018 Morning Ride 2:17:16 27.43 1,667 +Mon, 4/10/2017 Morning Ride 2:03:19 27.40 282 +Tue, 5/7/2019 Lunch Ride 2:07:55 27.37 820 +Sun, 11/25/2018 Afternoon Ride 1:56:26 27.34 203 +Wed, 4/29/2015 Commute 2:03:14 27.33 75 +Tue, 1/17/2017 Morning Ride 2:07:17 27.30 535 +Tue, 9/30/2014 Palo Alto, CA 2:07:33 27.28 636 +Sat, 9/17/2016 TourDeCoop.org 2:04:27 27.24 479 +Sat, 1/12/2019 Lunch Ride 1:58:47 27.24 1,079 +Mon, 3/30/2015 Woodside Loop 1:47:46 27.22 1,081 +Tue, 11/17/2015 Afternoon Ride 2:04:48 27.12 830 +Sun, 8/13/2017 Woodside Loop (Battery ran down) 2:00:00 27.00 1,000 +Sat, 1/31/2015 Alpine Rd 2:29:26 26.98 2,362 +Sun, 2/28/2016 Woodside Loop 1:43:34 26.93 843 +Sun, 4/1/2018 Lunch Ride 2:30:35 26.92 1,831 +Wed, 5/3/2017 Palo Alto Road Cycling 2:06:05 26.86 1,411 +Fri, 9/9/2016 Morning Ride 1:56:44 26.85 1,086 +Sun, 9/23/2018 Morning Ride 1:57:48 26.83 512 +Sat, 10/4/2014 Woodside Loop 1:52:09 26.71 1,213 +Thu, 5/7/2015 Commute 1:55:36 26.58 68 +Sun, 3/24/2019 OLH / WOLH 2:42:45 26.57 2,267 +Thu, 8/31/2017 Morning Ride 2:00:02 26.54 817 +Sun, 4/24/2016 Afternoon Ride 1:50:08 26.53 935 +Sun, 12/17/2017 Lunch Ride 1:50:51 26.53 217 +Tue, 7/4/2017 Lunch Ride 1:53:04 26.50 459 +Tue, 5/8/2018 Afternoon Ride 2:34:11 26.50 1,791 +Tue, 5/26/2015 Palo Alto, CA 2:01:47 26.50 591 +Thu, 8/4/2016 Afternoon Ride 1:55:47 26.49 659 +Wed, 6/8/2016 Lunch Ride 1:48:19 26.48 597 +Sun, 9/9/2018 Lunch Ride 2:00:49 26.46 479 +Sat, 5/21/2016 Maker Faire 1:57:14 26.44 207 +Wed, 4/1/2015 Commute 1:55:15 26.44 71 +Wed, 6/29/2016 Morning Ride 1:49:33 26.39 561 +Sat, 9/9/2017 Lunch Ride 1:59:01 26.38 1,112 +Fri, 6/3/2016 Morning Ride 1:56:57 26.38 502 +Fri, 3/31/2017 Morning Ride 2:01:41 26.36 495 +Sat, 12/13/2014 Westridge (up the back way; after the rain) 2:01:01 26.35 1,495 +Sun, 4/14/2019 Huddart - Paul Hopkins 2:08:57 26.34 1,601 +Sat, 6/29/2013 Untitled 1:48:50 26.33 1,091 +Sat, 4/25/2015 Woodside 1:54:30 26.28 1,220 +Tue, 1/31/2017 Morning Ride 2:01:18 26.27 384 +Tue, 6/28/2016 Morning Ride 1:55:31 26.27 571 +Sat, 5/19/2018 Morning Ride 2:00:24 26.25 958 +Thu, 6/30/2016 Morning Ride 1:52:16 26.24 499 +Sun, 2/19/2017 Afternoon Ride 1:56:46 26.23 1,138 +Thu, 8/18/2016 Morning Ride 1:58:06 26.23 712 +Sun, 4/20/2014 Bay Trail / Ponderosa Park 1:52:52 26.23 102 +Sun, 5/10/2015 Arastadero 1:48:58 26.22 1,148 +Thu, 5/19/2016 Morning Ride 1:52:56 26.14 938 +Mon, 12/31/2018 Morning Ride 2:00:24 26.11 545 +Fri, 12/15/2017 Afternoon Ride 1:51:36 26.07 226 +Sun, 1/18/2015 Woodside 1:38:16 26.02 1,257 +Mon, 10/3/2016 Morning Ride 1:54:45 26.01 646 +Tue, 7/14/2015 Mountain View, California 2:02:36 25.99 502 +Fri, 12/18/2015 Lunch Ride 1:58:57 25.93 909 +Tue, 6/13/2017 Morning Ride 1:55:30 25.89 289 +Mon, 8/4/2014 To the Sea 2:17:11 25.87 2,080 +Wed, 9/25/2013 work and with kris 2:07:40 25.81 593 +Sun, 9/13/2015 Afternoon Ride 1:49:31 25.78 1,152 +Sun, 3/3/2019 Afternoon Ride 2:00:06 25.73 390 +Thu, 1/26/2017 Morning Ride 1:56:09 25.67 259 +Sun, 5/6/2018 Morning Ride 1:55:05 25.67 1,148 +Sat, 7/29/2017 Lunch Ride 1:53:50 25.65 988 +Wed, 8/20/2014 Palo Alto, CA 1:49:35 25.61 497 +Sat, 4/19/2014 Lunch Ride 1:49:37 25.60 1,111 +Mon, 9/3/2018 Afternoon Ride 1:59:08 25.54 331 +Mon, 6/2/2014 Work 1:59:33 25.51 627 +Fri, 12/11/2015 Afternoon Ride 2:12:40 25.44 1,719 +Fri, 2/9/2018 Morning Ride 1:56:11 25.41 318 +Sun, 8/26/2018 Afternoon Ride 1:49:16 25.37 377 +Sun, 7/9/2017 Afternoon Ride 1:50:30 25.36 682 +Mon, 6/26/2017 Morning Ride 1:52:26 25.34 226 +Fri, 9/14/2018 Lunch Ride 1:50:05 25.34 541 +Fri, 3/1/2019 Morning Ride 2:00:23 25.34 121 +Sun, 1/13/2019 Lunch Ride 1:51:40 25.33 715 +Wed, 4/12/2017 Morning Ride 1:59:22 25.33 686 +Wed, 6/14/2017 Morning Ride 1:51:32 25.33 308 +Sat, 11/28/2015 Lunch Ride 1:43:56 25.31 1,122 +Mon, 11/27/2017 Morning Ride 1:55:39 25.31 184 +Fri, 11/28/2014 Palo Alto, CA 1:45:13 25.30 1,112 +Fri, 4/13/2018 Morning Ride 1:53:39 25.28 344 +Fri, 9/15/2017 Morning Ride 1:56:55 25.28 384 +Tue, 12/31/2013 Woodside Loop - Last Ride of the Year 1:45:28 25.27 1,207 +Sun, 10/14/2018 Lunch Ride 2:13:13 25.23 1,421 +Sat, 11/22/2014 Atherton to Woodside 1:43:10 25.21 1,095 +Sat, 6/4/2016 Morning Ride 1:57:23 25.19 922 +Mon, 8/8/2016 Morning Ride 1:52:38 25.17 423 +Fri, 11/6/2015 Morning Ride 1:51:45 25.15 538 +Mon, 12/18/2017 Afternoon Ride 1:52:56 25.15 741 +Fri, 6/24/2016 Foothill Expway 1:35:25 25.11 623 +Sat, 6/25/2016 Afternoon Ride 1:44:43 25.10 568 +Sat, 1/25/2014 Woodside 1:33:38 25.08 1,243 +Tue, 4/29/2014 Woodside 1:46:03 25.08 886 +Thu, 4/13/2017 Morning Ride 2:06:13 25.06 266 +Sun, 6/12/2016 Morning Ride 1:49:25 25.04 387 +Thu, 6/8/2017 Morning Ride 1:56:19 25.02 285 +Thu, 10/13/2016 Morning Ride 1:47:30 25.02 253 +Sun, 1/19/2014 Palo Alto, CA 1:36:55 25.01 1,201 +Mon, 9/5/2016 Afternoon Ride 1:42:56 25.00 902 +Sat, 11/8/2014 Atherton / Woodside 1:40:51 25.00 972 +Sat, 4/11/2015 Woodside 1:32:24 24.73 1,035 +Sat, 6/1/2013 Woodside Loop 1:41:20 24.45 1,125 +Sun, 6/16/2013 Woodside Loop 1:38:19 24.52 1,137 +Sun, 6/23/2013 Climb 2:16:29 24.30 2,001 +Sat, 7/13/2013 Doug's Event 1:51:55 21.35 1,677 +Sun, 8/4/2013 Kris's first trike ride 1:51:21 20.96 988 +Sun, 11/24/2013 Alpine Rd 1:42:36 21.02 1,289 +Fri, 11/29/2013 Woodside Loop 1:33:36 22.75 1,011 #### 2012 Rides -Thu, 1/5 2012 Tekapo Lake to Omarama New Zealand 5:27:34 79.42 2,145 -Fri, 1/6 2012 Omarama to Wanaka New Zealand 4:28:51 70.35 3,262 -Sat, 1/7 2012 Wanaka to Queenstown New Zealand 3:23:12 45.18 3,488 -Sun, 1/8 2012 Queenstown to Clyde New Zealand 3:59:59 60.02 3,522 -Fri, 1/9 2012 Otago Rail Trail Century 7:52:17 102.41 2,286 -Thu, 6/14 2012 Coyote Creek Century with Juliet 8:08:15 100.07 1,513 \ No newline at end of file +Thu, 1/5/2012 Tekapo Lake to Omarama New Zealand 5:27:34 79.42 2,145 +Fri, 1/6/2012 Omarama to Wanaka New Zealand 4:28:51 70.35 3,262 +Sat, 1/7/2012 Wanaka to Queenstown New Zealand 3:23:12 45.18 3,488 +Sun, 1/8/2012 Queenstown to Clyde New Zealand 3:59:59 60.02 3,522 +Fri, 1/9/2012 Otago Rail Trail Century 7:52:17 102.41 2,286 +Thu, 6/14/2012 Coyote Creek Century with Juliet 8:08:15 100.07 1,513 \ No newline at end of file