Рубрики
Без рубрики

Кластеризации Strava Club Riders

Узнайте, как кластер членов вашего клуба Strava на основе катания на верховой езде. Tagged с Python, Kmeans, Strava.

Вы когда -нибудь задумывались, насколько хорошо вы квалифицируете в своем клубе Strava? Вы хотите узнать уровень клуба вашего друга? Хотели бы вы найти те, которые имеют аналогичные результаты верховой езды, чем вы в своем клубе Strava?

Ну, я задал себе подобные вопросы и решил исследовать API Strava и провести небольшой анализ, чтобы группировать членов вашего клуба Strava в не Количество кластеров, основанных на производительности езды 🚴‍♀ 🚴🏿‍🚴🏻‍♀ 🚴🏼‍ 🚴🏼‍tice 🚴🏼‍tic🚴🏽‍ 🚴🏼‍чего 🚴🏻‍♂💨💨💨 Что это хорошая в клубной поездке?

Здесь я хотел бы сосредоточиться на процессе кластеризации, а не на использовании API Strava, потому что я думаю, что более поздняя тема широко распространена там. Если вы не знакомы с API, вы можете взглянуть на официальную документацию здесь

Давайте начнем, не так ли?

Я чувствую, что должен начать отсюда: определения

Для меня клубное занятие и, в частности, в клубной поездке – это то, что происходит в типичное воскресенье, вы просыпаетесь рано, хорошо завтракаете, одевайтесь в Лайкра, и у вас есть куча часов с парнями из клуба.

Ну, оказывается, определение не разделяется в мире Strava. Оставайся со мной здесь Анкет Strava рассматривает мероприятия в клубе конкретного клуба как все виды деятельности от всех пользователей этого клуба. На простом английском языке, если вы присоединитесь к клубу, все ваши действия будут указаны в качестве клубных мероприятий такого клуба.

План простой – Чем проще, тем лучше они говорят – Прежде всего, мы позаботимся о том, чтобы у нас были припасы вашего любимого горячего напитка, в моем случае Earl Grey Tea 🍵. ИМХО это всегда должен быть первым шагом, прежде чем пытаться сделать что -нибудь славное. ОК, двигаться вперед ..

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

Мой чайник включен, мой граф Грей собирается подготовиться

Пришло время посмотреть на какой -то код:

import requests

ACCESS_TOKEN = 'your_access_token_here'
n_clubs = 30
endpoint = "https://www.strava.com/api/v3/athlete/clubs?&pagenobody =1&per_page={}&access_token={}"
r = requests.get(endpoint.format(n_clubs,ACCESS_TOKEN))
my_clubs = r.json()

Я думаю, что этот шаг не был подробно описан в плане 🙃 В любом случае, в основном то, что это делает, это получение списка всех ваших клубов Strava, в которые вы присоединились. Там вы сможете найти ключ я бы для каждого из ваших клубов. После того, как вы определили из списка клуб, который вас интересует, отметьте его я D – С этого момента мы будем называть это Club_id Анкет

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

Теперь, как мы планировали, мы будем использовать Club_id Чтобы получить столько данных, что нам разрешено:

import pandas as pd

endpoint = "https://www.strava.com/api/v3/clubs/{}/activities?&page={}&per_page={}&access_token={}"
df = None
for ii in range(2):
    r = requests.get(endpoint.format(str(club_id),str(ii+1),'100',ACCESS_TOKEN))
    club_activity = r.json()
    df = pd.concat([df, pd.DataFrame(club_activity)])
df = df.reset_index(drop=True)
# Unpack the nested athlete dictionary into columns
df = pd.concat([df, pd.DataFrame((d for idx, d in df['athlete'].iteritems()))], axis=1)
df.drop(['athlete','resource_state'],axis=1,inplace=True)
df['full_name'] = df.firstname + ' ' + df.lastname

Это должно привести к созданию данных данных с базовой информацией о деятельности клуба интересующего клуба.

In[]: df.head()
Out[]: 
   distance  elapsed_time  moving_time             name 
0   55359.5          6709         6709   Afternoon Ride
1   23363.7          5911         5595   Afternoon Ride
2   28746.8          4961         4823   Afternoon Ride
3   64576.7         13551        10647   Afternoon Ride
4   24094.0          2712         2712     Morning Ride


   total_elevation_gain         type  workout_type        full_name(*)
0                 816.0         Ride          10.0          Sanglier
1                 427.0         Ride           NaN  Julius Pompilius
2                 724.0         Ride           NaN      Moralélastix
3                1343.7         Ride           NaN           Amnésix
4                 146.0  VirtualRide           NaN         Sténograf

(*) По причинам конфиденциальности я буду отображать Астерикс символы вместо реальных имен.

Несколько заметок здесь,

  • К счастью, единицы, кажется, в Si, Это приятное прикосновение! 🙌🙌
  • У нас есть две функции для описания времени из приведенных выше данных: elapsed_time должен включать перерывы, тогда как MOVEST_TIME должно быть то, что описывает имя. Если бы это было так, я ожидаю, что будут иметь более высокие значения истекающего времени, чем время для движения, всегда. Как вы можете видеть, это не так, то, что заставляет меня думать, что некоторые поездки не входят в систему с включенной автопаузой 🤦‍♂️ Ай, давай, ребята!
  • Средняя скорость не показана, поэтому мы вычисляем ее с df ['speed_kph']. Distance/df.moving_time*3.6 Извините за тех людей, которые не автопауза, так как их скорость будет уменьшена
  • Похоже, что не все идут по дороге, Стенографу было довольно удобно делать раннюю сессию дома!

Мы хотели бы, чтобы данные были индексированы спортсменом, один из способов достижения их – это использование метода Groupby, прикованной средней статистикой.

summary = df[df.type=='Ride'].groupby('full_name')['distance','total_elevation_gain','speed_kph'].mean()
In[]: summary.head()
Out[]: 
                  distance  total_elevation_gain  speed_kph
full_name                                                                 
Abraracourcix      34325.1                 502.1       15.2
Absolumentexclus   50507.7                 796.8       23.7
Amnésix            48812.6                 981.7       21.5
Amonbofis          54889.8                1014.0       20.5
Aplusbégalix       92074.0                 956.0       27.5

Итак, вы видите, это дает нам 3 функции – расстояние, общее усиление высоты и скорость – для каждого гонщика. Обратите внимание, что мы игнорируем виртуальные поездки, фильтруя тип поездки. Следуя, мы будем использовать именно эти данные для классов гонщиков в группах.

У меня мало чая .. держись на минуту, этот раздел заслуживает чуть больше, чем просто чай. Я думаю, что печенье будет делать 😋

Хорошей практикой при работе с алгоритмами машинного обучения является возврат ваших данных. В этом случае мы будем предварительно обработать данные с помощью Minmax Scaler. Это будет масштабировать все функции, так что его значения попадают в данное диапазон, как правило, между 0 и 1. Затем мы будем использовать эти значения для подачи алгоритма кластеризации K-средних.

from sklearn.preprocessing import minmax_scale
from sklearn.cluster import KMeans

X = minmax_scale(np.array(summary))
kmeans = KMeans(n_clusters=3, random_state=0).fit(X)
summary['cluster'] = kmeans.labels_

Довольно быстро, не так ли? Ну, давайте посмотрим на результаты, прежде чем спешить с выводами. Я хотел бы построить выступление спортсмена с помощью наших 3 функций, показывая только группы, которые мы только что сделали.

import matplotlib.pyplot as plt
import seaborn as sns

_= plt.figure()
_= plt.subplots_adjust(hspace=0,wspace=0)
_= plt.subplot(221)
_= sns.scatterplot(x=summary.distance/1000,y='total_elevation_gain',data=summary,hue=summary.cluster,legend=False)
_= plt.subplot(223)
_= sns.scatterplot(x=summary.distance/1000,y='speed_kph',data=summary,hue=summary.cluster,legend=False)
_= plt.subplot(224)
plt.yticks([])
_= sns.scatterplot(x='total_elevation_gain',y='speed_kph',data=summary,hue=summary.cluster,legend=False)

Хороший сюжет Но я не совсем доволен этим. Конечно, нам удастся кластеризировать гонщиков в 3 группах или, скажем, 3 команды. Держите шампанское на данный момент, это хорошая новость, что у нас хорошо сгруппированы гонщики, которые они охватывают, но выглядя немного ближе, некоторые из этих команд довольно несбалансированы с точки зрения скорости 😓. Посмотрите на нижние сюжеты – расстояние в зависимости от скорости и общее усиление высоты в зависимости от скорости – теперь обратите внимание на синюю команду. Их диапазон в скорости огромный, и помните, что эта скорость – средняя скорость !! Я лично не хотел бы быть в синей команде, если вы лучшие гонщики, вы ничего не делаете, кроме как ждать остальных, и если вы самый медленный гонщик там … какой это кошмар !!

Нам нужна вторая попытка.

Мы хотели бы иметь меньший диапазон скорости на каждой группе, чтобы все гонщики могли легко идти в ногу с темпами группы. Это означает, что скорость функции должна иметь значение больше, чем остальные. Как вы реализуете эту концепцию? Ключ в масштабировании. Следуйте за Minmax Scaler, мы будем масштабировать скорость в 2 в 2 и оставить другие функции такими, какие они есть. Это поможет.

X_weighted = np.multiply(X, np.tile([1,1,2], (len(X), 1)))
kmeans = KMeans(n_clusters=3, random_state=0).fit(X_weighted)
summary['cluster2'] = kmeans.labels_

А теперь мы создаем ту же фигуру:

_= plt.figure()
_= plt.subplots_adjust(hspace=0,wspace=0)
_= plt.subplot(221)
_= sns.scatterplot(x=summary.distance/1000,y='total_elevation_gain',data=summary,hue=summary.cluster2,legend=False)
_= plt.subplot(223)
_= sns.scatterplot(x=summary.distance/1000,y='speed_kph',data=summary,hue=summary.cluster2,legend=False)
_= plt.subplot(224)
plt.yticks([])
_= sns.scatterplot(x='total_elevation_gain',y='speed_kph',data=summary,hue=summary.cluster2,legend=False)

Теперь это выглядит намного лучше, гонщики сгруппированы по количеству расстояния, которое они покрывают, как высоко они поднимаются и как быстро они ездят, следя за тем, чтобы распространение в средней скорости в группах сохранялось низким.

И вот и вы, как скрепить свои поездки в клуб Strava. Время открыть бутылку шампанского 🍾

Оригинал: “https://dev.to/xbasf/cluster-strava-club-riders-5cjd”