Ознакомьтесь с некоторыми из наиболее распространенных форматов файлов и полезных библиотек Python.

Введение

В настоящее время очень распространено использование приложений и устройств для отслеживания фитнеса (таких как Garmin, FitBit, Strava и т. Д.) Для отслеживания ваших упражнений, особенно кардиотренировок, таких как бег и езда на велосипеде. Существует множество телефонных и веб-приложений, которые позволяют просматривать и анализировать свои действия все более изощренными способами. Некоторые из этих приложений предоставляются производителями устройств (например, Garmin Connect), тогда как другие не зависят от устройства и производителя, например Strava.

Но, возможно, вы не хотите полагаться на производителя вашего устройства или одного из сторонних поставщиков для хранения ваших данных и доступа к ним. (Например, серверы Garmin отключились на несколько дней в июле 2020 года, в результате чего пользователи Garmin полностью не смогли получить доступ к Garmin Connect.) Или, может быть, вы хотите просмотреть и проанализировать свои действия таким образом, который невозможно с этими приложениями.

К счастью, с некоторыми базовыми навыками программирования несложно разбирать и анализировать эти данные самостоятельно, и тогда единственным ограничением того, что вы можете с ними делать, является ваше воображение. В этой статье мы обсудим основы получения данных об активности и их анализа с помощью Python. Мы рассмотрим наиболее распространенные форматы для хранения и экспорта данных о деятельности и исследуем пару полезных библиотек Python для их анализа. Также будет несколько примеров сценариев для анализа файлов данных и построения pandas DataFrames с данными (иначе мы не будем здесь говорить о пандах, и для того, чтобы следовать этой статье, не требуется никакого знакомства с пандами - некоторые базовые знания Python и XML будут полезный). Примеры сценариев можно найти в следующем репозитории GitHub (минимальная версия Python, необходимая для запуска, - 3.6, а зависимости можно найти в Pipfile):



Когда мы говорим «данные активности», мы в первую очередь говорим о данных GPS и времени, которые описывают некоторые действия, связанные с движением, такие как бег, ходьба или цикл, а также дополнительные данные, которые могут быть предоставлены вашим устройством, такие как высота, данные о частоте пульса и каденции.

Методы, описанные в этой статье, в основном основаны на моих экспериментах с занятиями, записанными моими часами Garmin vívoactive 3 (которые я буду называть VA3 для удобства) или моими старыми часами Garmin Forerunner 30 (которые я буду называть FR30. ), который я в основном использовал для записи пробежек и прогулок. Существуют различия в том, как каждое устройство записывает и экспортирует данные, поэтому ваш пробег может отличаться, но форматы файлов и библиотеки, которые мы будем обсуждать, должны быть полезны для работы с широким спектром популярных устройств и приложений для отслеживания.

Если явно не указано иное, я не участвовал в разработке каких-либо услуг, программного обеспечения или статей, обсуждаемых в этой статье или связанных с ними.

Как получить данные

Экспорт из приложения

Многие из наиболее популярных приложений дают вам возможность экспортировать свои действия в распространенные форматы файлов. Например, и Strava, и Garmin Connect позволяют экспортировать занятия в форматы GPX и TCX и загружать исходный исходный файл (который может быть файлом FIT). Инструкции Strava находятся здесь, а инструкции Garmin Connect - здесь. Конечно, вам необходимо иметь учетную запись и уже загрузили данные о своей активности в соответствующее приложение.

Если у вас есть много действий, которые вы хотите проанализировать, вы, вероятно, захотите загружать файлы сразу, а не по одному. Не все приложения предлагают функцию экспорта массовых действий. Тем не менее, Strava, Garmin и другие компании, которые соблюдают Общие правила защиты данных (GDPR), должны предоставить вам возможность загружать все личные данные, которые они хранят о вас, включая данные о вашей активности (среди прочего). См. Здесь для Strava и здесь для Garmin. Вы также можете найти сторонние скрипты, которые массово загружают ваши занятия более удобным способом, например этот скрипт Python для Garmin Connect (любой такой скрипт, скорее всего, запросит ваше имя пользователя и пароль для загрузки ваших занятий , так что будьте осторожны и используйте только те программы, которым доверяете).

Для приложений, отличных от Strava и Garmin Connect, см. Их ответы на часто задаваемые вопросы или техническую поддержку, чтобы узнать о вариантах экспорта.

Непосредственно с устройства

Некоторые устройства можно подключить к вашему компьютеру через USB-соединение, чтобы вы могли напрямую получать доступ к файлам занятий на них. Возможно ли это и где именно найти файлы активности, будет зависеть от вашего устройства, поэтому в случае сомнений просмотрите ответы на часто задаваемые вопросы или техническую поддержку, предоставляемую производителем вашего устройства. Например, и FR30, и VA3 имеют каталог под названием GARMIN, который содержит (среди прочего) каталог под названием ACTIVITY. Этот каталог содержит данные об активности в виде файлов FIT.

Как разбирать данные

Разбор файлов GPX с помощью gpxpy

Формат обмена GPS (GPX) - это открытый формат на основе XML, который обычно используется для хранения данных на основе GPS. Из трех форматов, которые мы обсудим в этой статье, возможно, работать с GPX проще всего. Это простой и хорошо документированный формат, в котором есть несколько полезных инструментов и библиотек для работы с данными GPX. (С другой стороны, файлы TCX и FIT могут содержать больше информации об активности, чем файлы GPX.) Мы будем работать с gpxpy библиотекой для Python, чтобы работать с файлами GPX.

Но сначала давайте посмотрим, как выглядит файл GPX. В качестве примера, вот скриншот, показывающий (первые несколько строк) два файла GPX рядом, один загружен со Strava, а другой - с Garmin Connect (но оба сгенерированы с использованием одних и тех же базовых данных из моего FR30).

В каждом файле вы можете видеть, что корневой элемент - это элемент gpx с несколькими атрибутами, описывающими создателя файла GPX и используемые в нем пространства имен XML. Внутри элемента gpx есть элемент metadata с метаданными о самом файле и элемент trk, представляющий дорожку, которая представляет собой упорядоченный список точек, описывающих путь. Это примерно соответствует тому, что мы обычно рассматриваем как одно действие (бег, цикл, прогулка и т. д.).

Элемент trk содержит некоторые метаданные о действии, такие как его имя и тип действия (подробнее об этом позже), а также один или несколько элементов trkseg, каждый из которых представляет сегмент трека, который представляет собой список. точек трека, которые логически связаны по порядку . Другими словами, trkseg должен содержать непрерывные данные GPS. Если вы просто включаете GPS, пробегаете 10 км и затем выключаете GPS, когда закончите, то все это обычно представляет собой один сегмент трека. Однако, если по какой-либо причине вы выключили и снова включили GPS (или потеряли, а затем восстановили функциональность GPS) во время занятия, trk может состоять из нескольких trkseg элементов. (По крайней мере, такова теория, согласно документации; когда я приостанавливаю и перезапускаю свой VA3 во время пробежки, кажется, что весь пробег представляет собой единый сегмент дорожки.)

Каждый элемент trkseg должен содержать один или несколько (вероятно, много) элементов trkpt или «точки отслеживания», каждый из которых представляет одну (географическую) точку, обнаруженную вашим устройством GPS. Эти точки обычно находятся на расстоянии нескольких секунд друг от друга.

Как минимум trkpt должен содержать данные о широте и долготе (как атрибуты lat и lon элемента) и может дополнительно включать данные о времени и высоте (ele) в качестве дочерних элементов (данные, созданные фитнес-трекером, с большой вероятностью содержат как минимум включая время). trkpt может также содержать элемент extensions, который может содержать дополнительную информацию. В приведенном выше примере элементы расширения (в формате Garmin TrackPointExtension (TPE)) используются для хранения данных частоты пульса и частоты вращения педалей, предоставляемых FR30.

Я хочу указать на три основных различия между двумя отображенными выше файлами GPX. Во-первых, type элемента trk: файл Garmin описывает это как «работает», тогда как файл Strava просто описывает это как «9». Не существует стандартизированного способа представления типа трека. Garmin использует такие слова, как «бег», «ходьба», «походы» и т. Д., Тогда как Strava использует числовые коды, такие как «4» для походов, «9» для бега, «10» для ходьбы и т. Д. Я не нашел исчерпывающего сопоставления цифровых кодов Strava с видами деятельности. Если вы хотите найти код для определенного типа активности, вы можете отредактировать тип существующей активности в Strava (щелкните значок карандаша в левой части страницы активности), а затем экспортировать его в GPX, чтобы проверить значение. в элементе type.

Во-вторых, указанные высоты точек трека различаются, что может показаться удивительным, учитывая, что они основаны на одних и тех же базовых данных. Некоторые фитнес-трекеры (в том числе, похоже, FR30) либо не записывают данные о высоте, либо делают очень неточные записи на основе сигнала GPS. В этих случаях такие приложения, как Strava и Garmin, используют свои собственные внутренние базы данных и алгоритмы высот, чтобы либо генерировать свои собственные данные о высоте, либо корректировать данные, записанные устройством, чтобы получить более реалистичное чтение (см. Здесь для получения дополнительной информации от Strava ). Методы создания или корректировки данных о высоте в каждом приложении будут немного отличаться, и вы видите разницу здесь.

Наконец, вы заметите, что данные о широте и долготе, сообщаемые файлом Garmin, являются гораздо более точными, иногда давая значение примерно с 30 десятичными знаками, тогда как файл Strava дает значение с семью десятичными знаками. Файл Garmin, по-видимому, отражает точность необработанных данных, сообщаемых FR30, тогда как Strava, похоже, округляет данные. Важно отметить, что точность - это не то же самое, что точность. Сообщение о широте и долготе с точностью до тридцати десятичных знаков предполагает поистине микроскопический уровень точности, тогда как точность GPS в вашем фитнес-трекере, скорее всего, составляет в лучшем случае несколько метров. Поэтому вся эта дополнительная точность, о которой сообщает ваш фитнес-трекер, не особенно полезна. Однако это может иметь небольшое, но заметное влияние на общее зарегистрированное расстояние действия (рассчитанное путем сложения расстояний между всеми точками), поэтому общее расстояние может незначительно отличаться в зависимости от того, откуда поступают данные.

Итак, давайте взглянем на библиотеку gpxpy. Сначала убедитесь, что он установлен:

pip install gpxpy

Теперь давайте запустим интерпретатор Python в том же каталоге, что и наш файл GPX (в остальной части статьи я использую данные для другой активности, чем та, которую мы видели выше). Разобрать файл так же просто, как:

>>> import gpxpy
>>> with open('activity_strava.gpx') as f:
...     gpx = gpxpy.parse(f)
... 
>>> gpx
GPX(tracks=[GPXTrack(name='Morning Walk', segments=[GPXTrackSegment(points=[...])])])

Вы можете видеть, что вызов gpxpy.parse для объекта файла GPX даст вам объект GPX. Это структура данных, которая отражает структуру самого файла GPX. Среди прочего, он содержит список из GPXTrack объектов, каждый из которых представляет трек. Каждый объект GPXTrack содержит некоторые метаданные о дорожке и список сегментов.

>>> len(gpx.tracks)
1
>>> track = gpx.tracks[0]
>>> track
GPXTrack(name='Morning Walk', segments=[GPXTrackSegment(points=[...])])
>>> track.type
'10'
>>> track.name
'Morning Walk'
>>> track.segments
[GPXTrackSegment(points=[...])]

Каждый GPXTrackSegment, в свою очередь, содержит список GPXTrackPoint объектов, каждый из которых отражает одну точку трека.

>>> segment = track.segments[0]
>>> len(segment.points)
2433
>>> random_point = segment.points[44]
>>> random_point
GPXTrackPoint(40.642868, 14.593911, elevation=147.2, time=datetime.datetime(2020, 10, 13, 7, 44, 13, tzinfo=SimpleTZ("Z")))
>>> random_point.latitude
40.642868
>>> random_point.longitude
14.593911
>>> random_point.elevation
147.2
>>> random_point.time
datetime.datetime(2020, 10, 13, 7, 44, 13, tzinfo=SimpleTZ("Z"))

Также можно получить доступ к информации, которая хранится как расширение в файле GPX. В этом случае соответствующие элементы XML (т. Е. Дочерние элементы элемента extensions в файле GPX) сохраняются в списке.

>>> random_point.extensions
[<Element {http://www.garmin.com/xmlschemas/TrackPointExtension/v1}TrackPointExtension at 0x7f32bcc93540>]
>>> tpe = random_point.extensions[0]
>>> for child in tpe:
...     print(child.tag, child.text)
... 
{http://www.garmin.com/xmlschemas/TrackPointExtension/v1}hr 134
{http://www.garmin.com/xmlschemas/TrackPointExtension/v1}cad 43

В дополнение к хранению данных, проанализированных из базового файла GPX, у объектов GPXTrack и GPXTrackSegment есть несколько полезных методов для вычисления того, что мы, возможно, захотим узнать на основе данных. Например, вы можете рассчитать общую длину дорожки или сегмента:

>>> segment.length_2d()  # ignoring elevation
8104.369313043303
>>> segment.length_3d()  # including elevation
8256.807195641411

Или данные о времени движения или скорости (в метрах в секунду):

>>> segment.get_moving_data()
MovingData(moving_time=7829.0, stopped_time=971.0, moving_distance=8096.192269756624, stopped_distance=160.6149258847903, max_speed=1.7427574692488983)
>>> segment.get_speed(44)  # The number of the point at which you want to measure speed
1.157300752926421

Существуют различные другие методы, доступные для расчета других показателей, а также методы корректировки или изменения данных, например, путем добавления или удаления точек, разделения сегментов, значений сглаживания и т. Д. Вы можете изучить их, вызвав help для соответствующего объекта.

Наконец, вот - сценарий Python для анализа файла GPX и помещения некоторых ключевых данных в фрейм данных pandas. Вызов этого скрипта в нашем файле GPX:

python3 parse_gpx.py activity_strava.gpx

… Выведет что-то вроде следующего:

Разбор файлов TCX с помощью lxml

Формат Training Center XML (TCX) - еще один распространенный формат для хранения данных о занятиях, созданный компанией Garmin. Самый простой способ понять разницу между GPX и TCX - посмотреть на два файла рядом:

Первое, что вы, вероятно, заметите, это то, что точки данных в файле TCX сгруппированы в «Круги» и что каждый элемент Lap имеет некоторые связанные с ним полезные данные, такие как общее время, затраченное на круг, сожженные калории, среднее и максимальная частота пульса и т. д. Что представляет собой «круг», зависит от того, как настроено устройство; в этом случае упражнение делится на «круги» или отрезки по 1 000 метров.

Еще вы могли заметить, что первый элемент Trackpoint в файле TCX содержит данные о высоте, расстоянии, частоте пульса и скорости, но не данные о широте или долготе. Это случается время от времени и отражает структуру необработанных (FIT) данных, записанных устройством. Я могу только догадываться, что это происходит потому, что эти данные (которые не зависят от GPS) сообщаются отдельно от данных широты и долготы. Поскольку элемент trkpt в файле GPX должен содержать широту и долготу, файлы GPX не могут записывать высоту (и т. Д.) Отдельно; он должен быть связан с некоторыми данными о широте и долготе. Таким образом, файл GPX, загруженный из Garmin Connect, кажется, просто игнорирует те точки данных, которые не имеют данных о широте и долготе, тогда как кажется, что файл GPX, загруженный из Strava, включает их и «заполняет» отсутствующие данные о широте и долготе, используя данные из следующий пункт.

Помимо вышеперечисленных пунктов, структура файла TCX не сильно отличается от структуры файла GPX. Корневой элемент - это элемент TrainingCenterDatabase, который содержит элемент Activities. Этот элемент содержит один или несколько Activity элементов, каждый из которых описывает действие. Помимо некоторых метаданных, элемент Activity содержит ряд Lap элементов. Каждый элемент Lap содержит некоторые метаданные о соответствующем круге (или отрезке), а также элемент Track, который содержит множество Trackpoint элементов, каждый из которых представляет точку данных, о которой было сообщено устройством, и которые могут (или не могут) содержать, среди прочего другие данные, широта и долгота, высота, частота пульса, частота вращения педалей, расстояние и скорость.

Я не знаю ни одной установленной библиотеки Python для работы с файлами TCX, но, учитывая, что это просто тип XML-файла, вы можете использовать lxml или стандартную xml библиотеку Python для его анализа. Вот - сценарий Python, который использует библиотеку lxml для анализа файла TCX и помещения некоторых ключевых данных в фрейм данных pandas, аналогичный тому, который был связан для файлов GPX выше. Обратите внимание, что мы также используем библиотеку python-dateutil, чтобы легко анализировать временные метки в формате ISO 8601. Используя дополнительную информацию, содержащуюся в файле TCX, мы создаем дополнительный DataFrame с информацией о круге. Вызов этого сценария следующим образом (убедитесь, что у вас установлены lxml и python-dateutil):

python3 parse_tcx.py activity_strava.tcx

… Даст вам что-то вроде этого:

Разбор файлов FIT с помощью fitdecode

В отличие от форматов GPX и TCX, основанных на XML, протокол Flexible and Interoperable Data Transfer (FIT) представляет собой двоичный формат, созданный Garmin. fitdecode - это библиотека Python для анализа файлов FIT. Документация к библиотеке находится здесь. Его можно установить так:

pip install fitdecode

fitdecodelibrary позволяет вам создать объект FitReader, который читает файл FIT. Затем вы можете перебирать FitReader для доступа к каждому «кадру» или блоку данных, присутствующих в файле FIT, по порядку. Каждый кадр представлен объектом FitHeader, FitDefinitionMessage, FitDataMessage или FitCRC, в зависимости от типа базовой записи данных. FitDataMessage - это тот, который нас интересует, потому что это объект, который будет содержать фактические данные. Но не все FitDataMessage будут актуальными; многие из них могут просто содержать данные о состоянии устройства или метаданные о самом файле. В настоящее время мы ищем FitDataMessage, где атрибут name равен lap или record:

with fitdecode.FitReader('activity_garmin.fit') as fit_file:
    for frame in fit_file:
        if isinstance(frame, fitdecode.records.FitDataMessage):
            if frame.name == 'lap':
                # This frame contains data about a lap.
            
            elif frame.name == 'record':
                # This frame contains data about a "track point".

Вы можете проверить, какие поля данных содержит данный фрейм, следующим образом:

for field in frame.fields:
    # field is a FieldData object
    print(field.name)

И вы можете использовать методы has_field, get_field и get_value объекта FitDataMessage для доступа к соответствующим данным.

# Assuming the frame is a "record"
if frame.has_field('position_lat') and frame.has_field('position_long'):
    print('latitude:', frame.get_value('position_lat'))
    print('longitude:', frame.get_value('position_long'))
                
# Or you can provide a "fallback" argument to give you a default
# value if the field is not present:
print('non_existent_field:', frame.get_value('non_existent_field', fallback='field not present'))

Приведенный выше код (если он вызывается в контексте, где frame является FitDataMessage объектом типа сообщения record и имеет данные о широте и долготе) выдаст что-то вроде вывода:

latitude: 484805747
longitude: 174290634
non_existent_field: field not present

Теперь вы заметите, что latitude и longitude хранятся как целые числа. Согласно этому сообщению StackOverflow, способ преобразовать эти целые числа в градусы - это разделить их на (2**32)/360:

>>> 484805747 / ((2**32)/360)
40.63594828359783
>>> 174290634 / ((2**32)/360)
14.608872178941965

Ниже приведены лишь некоторые из наиболее полезных полей, присутствующих в файлах FIT, созданных моим VA3:

  • Для lap фреймов: start_time, start_position_lat, start_position_long, total_elapsed_time, total_distance, total_calories, avg_speed, max_speed, total_ascent, total_descent, avg_heart_rate, max_heart_rate, avg_cadence, max_cadence, avg_power, max_power
  • Для record фреймов: timestamp, position_lat, position_long, distance, altitude, enhanced_altitude, speed, enhanced_speed, heart_rate, cadence

Есть и другие, и разные устройства могут сообщать разные данные, поэтому стоит изучить свои собственные файлы, чтобы увидеть, что вы можете найти. Кроме того, не все поля всегда будут присутствовать - например, как мы видели в предыдущем разделе, иногда данные о широте и долготе могут не передаваться. Поэтому рекомендуется использовать метод has_field или предоставить резервный аргумент для get_value.

Вот - это базовый сценарий, который анализирует файл FIT и создает кадры данных pandas с информацией о точках круга и трека, аналогично сценарию, который я связал в предыдущем разделе для файлов TCX.

Что дальше?

Теперь, когда вы знаете основы получения и анализа данных фитнес-трекера с помощью Python, вам решать, что вы хотите с ним делать. Одна из вещей, которую вы, вероятно, захотите сделать, - это каким-то образом визуализировать эти данные, используя matplotlib, seaborn, plotly или какую-либо другую библиотеку визуализации данных.

Вот несколько статей и библиотек, которые могут вас заинтересовать:

  • Эта статья частично повторяет те же темы, которые мы рассмотрели в этой статье, но продолжает обсуждение основ построения и преобразования данных GPS и включает полезное обсуждение того, как рассчитать расстояние между двумя точками.
  • В этой статье рассказывается, как визуализировать данные GPS с помощью Folium.
  • Если ваше устройство не сообщает данные о высоте, посмотрите srtm.py от того же автора, что и gpxpy, это позволит вам искать данные о высоте, используя данные НАСА Shuttle Radar Topography Mission.

Спасибо за чтение, надеюсь, вы нашли эту статью полезной!