Create artistic visualisations with your exercise data (Python version)

Overview

strava_py

Create artistic visualisations with your exercise data (Python version).

This is a port of the R strava package to Python.

Examples

Facets

A plot of activities as small multiples. The concept behind this plot was originally inspired by Sisu.

facets

Map

A map of activities viewed in plan.

map

How to use

Bulk export from Strava

The process for downloading data is described on the Strava website here: [https://support.strava.com/hc/en-us/articles/216918437-Exporting-your-Data-and-Bulk-Export#Bulk], but in essence, do the following:

  1. Log in to Strava
  2. Select "Settings" from the main drop-down menu at top right of the screen
  3. Select "My Account" from the navigation menu to the left of the screen.
  4. Under the "Download or Delete Your Account" heading, click the "Get Started" button.
  5. Under the "Download Request", heading, click the "Request Your Archive" button. Don't click anything else on that page, i.e. particularly not the "Request Account Deletion" button.
  6. Wait for an email to be sent
  7. Click the link in email to download zipped folder containing activities
  8. Unzip files

Process the data

The main function for importing and processing activity files expects a path to a directory of unzipped GPX and / or FIT files. If required, the fit2gpx package provides useful tools for pre-processing bulk files exported from Strava, e.g. unzipping activity files (see Use Case 3: Strava Bulk Export Tools).

df = process_data(<path to folder with GPX and / or FIT files>)

Plot activities as small multiples

plot_facets(df, output_file = 'plot.png')

Plot activity map

plot_map(df, lon_min=None, lon_max= None, lat_min=None, lat_max=None,
             alpha=0.3, linewidth=0.3, output_file="map.png")
Comments
  • Getting:

    Getting: "gpxpy.gpx.GPXException: latitude is mandatory in None (got None)"

    Hey,

    I tried using the CLI tool but I'm getting this error: gpxpy.gpx.GPXException: latitude is mandatory in None (got None)

    I tried setting --lon_max, --lat_max to 180,90 and --lat_min, --lon_min to 0,0 but still getting the same error.

    Is this a problem with a specific .gpx file? And if so how can I find that one because the error doesn't show which file.

    Thanks

    opened by Alpha249 7
  • Starting cli.py gives error: ModuleNotFoundError: No module named 'stravavis'

    Starting cli.py gives error: ModuleNotFoundError: No module named 'stravavis'

    Thanks for the app, it looks quite nice. But as I'm a bit new to Python, I'm having trouble to get it started.

    I cloned the repo and tried from its root directory: jmizv@MacBook strava_py % python3 src/stravavis/cli.py /Users/jmizv/Downloads/gpx

    But I just got that error message:

    Traceback (most recent call last):
      File "/Users/jmizv/IdeaProjects/strava_py/src/stravavis/cli.py", line 91, in <module>
        main()
      File "/Users/jmizv/IdeaProjects/strava_py/src/stravavis/cli.py", line 41, in main
        from stravavis.plot_calendar import plot_calendar
    ModuleNotFoundError: No module named 'stravavis'
    jmizv@MacBook strava_py % 
    

    I'm probably missing something obvious but I don't see it. Could you, @marcusvolz, please give an advice?

    opened by jmizv 7
  • Add CLI as strava_py

    Add CLI as strava_py

    This PR adds a CLI for creating visualisations, and puts it into an installable package, ready for distributions via https://pypi.org so it can be installed using pip. (I can help with how to do that later!)

    First, I moved the strava_py directory into a src directory. This is commonly used for Python projects. The main benefits is to make sure when you're testing, you're testing against something that has been installed, and not something that happens to have the same directory name in your current dir. Much more:

    • https://packaging.python.org/en/latest/tutorials/packaging-projects/#a-simple-project
    • https://blog.ionelmc.ro/2014/05/25/python-packaging/
    • https://hynek.me/articles/testing-packaging/

    The way to install from source:

    pip install .
    

    Or if you're developing, -e means an editable install, so you can make changes to your local source tree and they're reflected in what's run:

    pip install -e .
    

    Then I went for strava_py as the CLI name (this can be changed):

    $ strava_py --help
    usage: strava_py [-h] [-o OUTPUT_FILE] path
    
    positional arguments:
      path                  Input path to folder with GPX and / or FIT files
    
    options:
      -h, --help            show this help message and exit
      -o OUTPUT_FILE, --output_file OUTPUT_FILE
                            Output PNG file (default: plot.png)
    

    Example run:

    $ strava_py /tmp/my_strava_activities
    Processing data...
    Processing: 6628958226.gpx
    Processing: 6626094662.gpx
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 142); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 162); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 203); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 249); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 285); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 326); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 351); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 397); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 443); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 487); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 531); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 575); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 619); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 663); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 707); adding dummy dev data...
      warnings.warn(msg)
    Processing: 6678512291.fit
    Processing: 6619621876.gpx
    Processing: 6604948302.gpx
    Processing: 6860888905.fit
    Processing: 6678648994.fit
    Processing: 6676505895.fit
    Processing: 6810573487.fit
    Processing: 6623947053.gpx
    Processing: 6630963143.gpx
    Plotting facets...
    Saved to plot.png
    

    I'll also add a bunch of inline comments to explain what some things do, please feel free to ask more about any of this!

    opened by hugovk 3
  • Elevations

    Elevations

    @hugovk Thanks for reviewing these and for the recent PRs. This one just adds the elevations small multiples plot.

    I think I might have missed the suggested commits in your previous PRs, could you let me know if everything went through ok? Thanks!

    opened by marcusvolz 2
  • ValueError: All objects passed were None

    ValueError: All objects passed were None

    Hi, I have been trying to apply this package on my data pull from Strava but in the first step of processing data I'm getting this error of "ValueError: All objects passed were None" attaching a screenshot for reference. I tried to give address of either the activities or routes folder which has gpx files but still the error remains constant Screenshot 2022-12-25 at 16 03 22

    opened by shu3hamiitkgp 1
  • Fix squashed maps

    Fix squashed maps

    This is similar to https://github.com/marcusvolz/strava/issues/5.

    Here's the same GPX using main:

    strava-map

    Like https://stackoverflow.com/a/14457180/724176, rather than using latitude and longitude values, this uses a Mercator projection calculation to come up with x and y values instead.

    Dummy, unit map width and height values are used, as we don't know and don't need to know the final size as matplotlib will take care of that.

    And with this PR:

    strava-map

    Measuring performance:

    With main, on 2,946 GPX files (but commenting out some bits of cli.py to only run "Processing data..." and "Plotting map..."), it takes 11m45s on a Mac M1 16GB.

    With the PR it takes a comparable 11m43s, which is really good because the transform is done by Pandas so presumably in C.

    opened by hugovk 1
  • UnicodeDecodeError: 'charmap' codec can't decode byte 0x8d in position 379: character maps to <undefined>

    UnicodeDecodeError: 'charmap' codec can't decode byte 0x8d in position 379: character maps to

    When I execute stravavis against this single attached file (5670595833.gpx.txt, you need to remove the txt extension) it fails with the below error. I tend to add emojis to my strava activity titles so this is in my opinion the problem. Not sure if you could exclude this easily? The files are certainly not encoded with Cp1252.

    C:\Users\...>stravavis C:\Users\...
    Processing data...
    Processing ----------------------------------------   0% -:--:--
    multiprocessing.pool.RemoteTraceback:
    """
    Traceback (most recent call last):
      File "C:\Program Files\Python310\lib\multiprocessing\pool.py", line 125, in worker
        result = (True, func(*args, **kwds))
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\stravavis\process_data.py", line 13, in process_file
        return process_gpx(fpath)
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\stravavis\process_data.py", line 21, in process_gpx
        activity = gpxpy.parse(open(gpxfile))
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\gpxpy\__init__.py", line 37, in parse
        parser = mod_parser.GPXParser(xml_or_file)
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\gpxpy\parser.py", line 70, in __init__
        self.init(xml_or_file)
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\gpxpy\parser.py", line 82, in init
        text = xml_or_file.read() if hasattr(xml_or_file, 'read') else xml_or_file # type: ignore
      File "C:\Program Files\Python310\lib\encodings\cp1252.py", line 23, in decode
        return codecs.charmap_decode(input,self.errors,decoding_table)[0]
    UnicodeDecodeError: 'charmap' codec can't decode byte 0x8d in position 379: character maps to <undefined>
    """
    
    The above exception was the direct cause of the following exception:
    
    Traceback (most recent call last):
      File "C:\Program Files\Python310\lib\runpy.py", line 196, in _run_module_as_main
        return _run_code(code, main_globals, None,
      File "C:\Program Files\Python310\lib\runpy.py", line 86, in _run_code
        exec(code, run_globals)
      File "C:\Users\...\AppData\Roaming\Python\Python310\Scripts\stravavis.exe\__main__.py", line 7, in <module>
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\stravavis\cli.py", line 51, in main
        df = process_data(args.path)
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\stravavis\process_data.py", line 108, in process_data
        processed = list(it)
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\rich\progress.py", line 168, in track
        yield from progress.track(
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\rich\progress.py", line 1211, in track
        for value in sequence:
      File "C:\Program Files\Python310\lib\multiprocessing\pool.py", line 870, in next
        raise value
    UnicodeDecodeError: 'charmap' codec can't decode byte 0x8d in position 379: character maps to <undefined>
    

    5670595833.gpx.txt

    bug 
    opened by jmizv 1
  • Add --bbox CLI param as a shortcut

    Add --bbox CLI param as a shortcut

    It's a bit tedious to enter four params and values on the command line when creating a map:

    stravavis activities --lon_min 23.516665 --lat_min 60.000467 --lon_max 26.390877 --lat_max 61.360291

    This PR adds a --bbox shortcut for those, that takes a bounding box of comma-separated values of lon_min, lat_min, lon_max, lat_max:

    stravavis activities --bbox 23.516665,60.000467,26.390877,61.360291

    It's quite easy to find bounding boxes online, for example: http://bboxfinder.com/#60.000467,23.516665,61.360291,26.390877


    I also noticed that these lat/lon values are not actually passed from the CLI to the plotting function, and neither are alpha or linewidth, so let's fix that too :)


    Finally, bump some GitHub Actions versions for the CI.

    enhancement 
    opened by hugovk 1
  • Updated README: Add installation instructions and link directly to images

    Updated README: Add installation instructions and link directly to images

    Images are broken on PyPI:

    image

    Instead of linking to things like https://github.com/marcusvolz/strava_py/blob/main/plots/facets001.png, which is an HTML page showing the image, and which GitHub can apparently handle redirects to for the README, link directly to the image file, like https://raw.githubusercontent.com/marcusvolz/strava_py/main/plots/facets001.png

    This also adds installation instructions to the README.

    opened by hugovk 1
  • More progress bars for slow loops, and refactor

    More progress bars for slow loops, and refactor

    Also in Python we generally prefer to iterate over objects in a list (or tuple, or other so-called "iterable") rather than using an index of range(len(thing)).

    So for example, instead of:

    n = len(activities)
    for i in range(n):
        print(activities[i])
    

    This is more direct and readable:

    for activity in activities:
        print(activity)
    

    If we do need an index as well, enumerate is often used:

    for i, activity in enumerate(activities):
        print(i, activity)
    
    opened by hugovk 1
  • Use multiprocessing to speed up processing of input files

    Use multiprocessing to speed up processing of input files

    Use multiprocessing to speed up processing of input files, using the number of processes which matches the CPU count.

    On my old dual-core Mac, the processing step speeds up:

    • 601 files (all of 2021): 2m7s -> 1m20s
    • 2,629 files (my full Strava archive): 10m29s -> 5m9s

    Will be a larger gain for machines with more CPUs.

    multiprocessing needs a single worker function, this is the new process_file which decides which of process_gpx and process_fit to call. They also need to be top-level functions to work with multiprocessing.

    I also formatted the file using the popular Black autoformatter:

    python -m pip install -U black
    black . 
    

    I also fixed a bug when using an input path like ~/dir, it needs to expand ~ before checking it's a dir (and appending *).

    opened by hugovk 1
Releases(v0.1.0)
  • v0.1.0(Dec 30, 2022)

    What's Changed

    • Add --bbox CLI param as a shortcut by @hugovk in https://github.com/marcusvolz/strava_py/pull/18
    • Add support for Python 3.11 by @hugovk in https://github.com/marcusvolz/strava_py/pull/26
    • Catch exception to skip invalid file by @hugovk in https://github.com/marcusvolz/strava_py/pull/27
    • Fix encoding on Windows by @hugovk in https://github.com/marcusvolz/strava_py/pull/20
    • Fix squashed maps by @hugovk in https://github.com/marcusvolz/strava_py/pull/21
    • Fix README typo by @hugovk in https://github.com/marcusvolz/strava_py/pull/24

    Full Changelog: https://github.com/marcusvolz/strava_py/compare/v0.0.2...v0.1.0

    Source code(tar.gz)
    Source code(zip)
  • v0.0.2(Sep 1, 2022)

    What's Changed

    • Updated README: Add installation instructions and link directly to images by @hugovk in https://github.com/marcusvolz/strava_py/pull/17

    Full Changelog: https://github.com/marcusvolz/strava_py/compare/v0.0.1...v0.0.2

    Source code(tar.gz)
    Source code(zip)
  • v0.0.1(Sep 1, 2022)

    What's Changed

    • Add CLI as strava_py by @hugovk in https://github.com/marcusvolz/strava_py/pull/1
    • added plot_map by @marcusvolz in https://github.com/marcusvolz/strava_py/pull/2
    • Rename package and CLI program to stravavis by @hugovk in https://github.com/marcusvolz/strava_py/pull/3
    • Allow input path specification and show progress bar by @hugovk in https://github.com/marcusvolz/strava_py/pull/4
    • Elevations by @marcusvolz in https://github.com/marcusvolz/strava_py/pull/5
    • Test run on GitHub Actions by @hugovk in https://github.com/marcusvolz/strava_py/pull/6
    • Landscape by @marcusvolz in https://github.com/marcusvolz/strava_py/pull/8
    • Use multiprocessing to speed up processing of input files by @hugovk in https://github.com/marcusvolz/strava_py/pull/9
    • Calendar by @marcusvolz in https://github.com/marcusvolz/strava_py/pull/10
    • Fix local variable 'activities' referenced before assignment by @hugovk in https://github.com/marcusvolz/strava_py/pull/11
    • Fix ValueError with --activities_path by @hugovk in https://github.com/marcusvolz/strava_py/pull/12
    • Dumbbell by @marcusvolz in https://github.com/marcusvolz/strava_py/pull/13
    • Allow --activities_path to point to file or directory by @hugovk in https://github.com/marcusvolz/strava_py/pull/14
    • More progress bars for slow loops, and refactor by @hugovk in https://github.com/marcusvolz/strava_py/pull/15

    New Contributors

    • @hugovk made their first contribution in https://github.com/marcusvolz/strava_py/pull/1
    • @marcusvolz made their first contribution in https://github.com/marcusvolz/strava_py/pull/2

    Full Changelog: https://github.com/marcusvolz/strava_py/commits/v0.0.1

    Source code(tar.gz)
    Source code(zip)
Owner
Marcus Volz
Data visualisation | mathematical visualisation | 3D visualisation
Marcus Volz
Tidy data structures, summaries, and visualisations for missing data

naniar naniar provides principled, tidy ways to summarise, visualise, and manipulate missing data with minimal deviations from the workflows in ggplot

Nicholas Tierney 611 Dec 22, 2022
Script to create an animated data visualisation for categorical timeseries data - GIF choropleth map with annotations.

choropleth_ldn Simple script to create a chloropleth map of London with categorical timeseries data. The script in main.py creates a gif of the most f

null 1 Oct 7, 2021
Python library that makes it easy for data scientists to create charts.

Chartify Chartify is a Python library that makes it easy for data scientists to create charts. Why use Chartify? Consistent input data format: Spend l

Spotify 3.2k Jan 4, 2023
Python library that makes it easy for data scientists to create charts.

Chartify Chartify is a Python library that makes it easy for data scientists to create charts. Why use Chartify? Consistent input data format: Spend l

Spotify 2.8k Feb 18, 2021
Python library that makes it easy for data scientists to create charts.

Chartify Chartify is a Python library that makes it easy for data scientists to create charts. Why use Chartify? Consistent input data format: Spend l

Spotify 3.2k Jan 1, 2023
Using SQLite within Python to create database and analyze Starcraft 2 units data (Pandas also used)

SQLite python Starcraft 2 English This project shows the usage of SQLite with python. To create, modify and communicate with the SQLite database from

null 1 Dec 30, 2021
:bowtie: Create a dashboard with python!

Installation | Documentation | Gitter Chat | Google Group Bowtie Introduction Bowtie is a library for writing dashboards in Python. No need to know we

Jacques Kvam 753 Dec 22, 2022
Create a visualization for Trump's Tweeted Words Using Python

Data Trump's Tweeted Words This plot illustrates twitter word occurences. We already did the coding I needed for this plot, so I was very inspired to

null 7 Mar 27, 2022
Python package to Create, Read, Write, Edit, and Visualize GSFLOW models

pygsflow pyGSFLOW is a python package to Create, Read, Write, Edit, and Visualize GSFLOW models API Documentation pyGSFLOW API documentation can be fo

pyGSFLOW 21 Dec 14, 2022
Create HTML profiling reports from pandas DataFrame objects

Pandas Profiling Documentation | Slack | Stack Overflow Generates profile reports from a pandas DataFrame. The pandas df.describe() function is great

null 10k Jan 1, 2023
Create HTML profiling reports from pandas DataFrame objects

Pandas Profiling Documentation | Slack | Stack Overflow Generates profile reports from a pandas DataFrame. The pandas df.describe() function is great

null 6.8k Feb 18, 2021
Painlessly create beautiful matplotlib plots.

Announcement Thank you to everyone who has used prettyplotlib and made it what it is today! Unfortunately, I no longer have the bandwidth to maintain

Olga Botvinnik 1.6k Jan 6, 2023
Create 3d loss surface visualizations, with optimizer path. Issues welcome!

MLVTK A loss surface visualization tool Simple feed-forward network trained on chess data, using elu activation and Adam optimizer Simple feed-forward

null 7 Dec 21, 2022
Create a table with row explanations, column headers, using matplotlib

Create a table with row explanations, column headers, using matplotlib. Intended usage was a small table containing a custom heatmap.

null 4 Aug 14, 2022
Use Perspective to create the chart for the trader’s dashboard

Task Overview | Installation Instructions | Link to Module 3 Introduction Experience Technology at JP Morgan Chase Try out what real work is like in t

Abdulazeez Jimoh 1 Jan 22, 2022
Create Badges with stats of Scratch User, Project and Studio. Use those badges in Github readmes, etc.

Scratch-Stats-Badge Create customized Badges with stats of Scratch User, Studio or Project. Use those badges in Github readmes, etc. Examples Document

Siddhesh Chavan 5 Aug 28, 2022
Plotly Dash Command Line Tools - Easily create and deploy Plotly Dash projects from templates

??️ dash-tools - Create and Deploy Plotly Dash Apps from Command Line | | | | | Create a templated multi-page Plotly Dash app with CLI in less than 7

Andrew Hossack 50 Dec 30, 2022
Create matplotlib visualizations from the command-line

MatplotCLI Create matplotlib visualizations from the command-line MatplotCLI is a simple utility to quickly create plots from the command-line, levera

Daniel Moura 46 Dec 16, 2022
BrowZen correlates your emotional states with the web sites you visit to give you actionable insights about how you spend your time browsing the web.

BrowZen BrowZen correlates your emotional states with the web sites you visit to give you actionable insights about how you spend your time browsing t

Nick Bild 36 Sep 28, 2022