劉畊宏男孩女孩看過來!運動數據分析挖掘!⛵

💡 作者:韓信子@ShowMeAI
📘數據分析 ◉ 技能提升系列//www.showmeai.tech/tutorials/33
📘AI 面試題庫系列//www.showmeai.tech/tutorials/48
📘本文地址//www.showmeai.tech/article-detail/301
📢 聲明:版權所有,轉載請聯繫平台與作者並註明出處
📢 收藏ShowMeAI查看更多精彩內容

因為疫情,2年多的時間裏,大家多了很多居家的經歷,但是運動健康並不能因為居家而停止,健身隨時隨處可以進行!健身環大冒險等大熱,而前陣子的劉畊宏跳操,帶火了一大票畊宏男孩女孩,可穿戴設備市場大漲,而這些設備也記錄了大量的運動數據。

現在有非常多的運動穿戴設備,比如簡單的小米手環,到fitbit,到apple watch,而數據科學領域的從業者們,我們眾多的數據分析師和數據科學家,可以輕鬆分析健身設備上收集的數據,把數據科學和醫療保健結合起來。

在本篇內容中,ShowMeAI就基於 fitbit 手環記錄的一部分數據,講解如何進行有效的數據分析。本次使用的數據集可以在 Kaggle 平台 上免費下載。大家也可以通過ShowMeAI的網盤直接下載。

🏆 實戰數據集下載(百度網盤):公眾號『ShowMeAI研究中心』回復『實戰』,或者點擊 這裡 獲取本文 [12] 運動手環的數據分析挖掘與建模案例Fitabase 運動佩戴設備數據集

ShowMeAI官方GitHub//github.com/ShowMeAI-Hub

本篇內容涉及的工具庫,大家可以參考ShowMeAI製作的工具庫速查表和教程進行學習和快速使用。

💡 數據導入

我們先導入所需工具庫,並讀取數據:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import plotly.express as px
import plotly.graph_objects as go

# 讀取數據
data = pd.read_csv('dailyActivity_merged.csv')
print(data.head())

該數據集由2016年12月3日-2016年12月5日期間通過亞馬遜Mechanical Turk進行的分佈式調查的受訪者產生。30名符合條件的Fitbit用戶同意提交個人追蹤器數據,包括身體活動、心率和睡眠監測的分鐘級輸出。個人報告可以通過輸出會話ID(A列)或時間戳(B列)進行解析。輸出結果之間的差異代表了不同類型的Fitbit追蹤器的使用和個人追蹤行為/偏好。

💡 初步分析

我們先看看這個數據集的缺失值情況:

data.isnull().sum()

我們可以通過info和describe查看數據基本信息。

data.info()

我們在數據中看到記錄時間的字段ActivityDate,我們把它轉換為時間型,以便進行後續進一步分析。

# 更改 ActivityDate 的數據類型。 
data["ActivityDate"] = pd.to_datetime(data["ActivityDate"], format="%m/%d/%Y")

我們從字段名稱中可以看到,有記錄『非常活躍』、『相當活躍』、『輕度活躍』和『久坐』的時間信息,分別是VeryActiveMinutes、FairlyActiveMinutes、LightlyActiveMinutes、SedentaryMinutes,我們對所有時間做一個匯總。

data["TotalMinutes"] = data["VeryActiveMinutes"] + data["FairlyActiveMinutes"] + data["LightlyActiveMinutes"] + data["SedentaryMinutes"]
data["TotalMinutes"].sample(5)

我們通過describe函數查看一下數據集的描述性統計數據。

data.describe()

💡 EDA 探索性數據分析

數據集中的「卡路里」列記錄了每天燃燒多少卡路里,我們基於它做一點分析。

# 研究一下每日總步數和消耗的卡路里之間的聯繫。 
figure = px.scatter(data_frame = data, x="Calories",
                    y="TotalSteps", size="VeryActiveMinutes", 
                    trendline="ols", 
                    title="總步數和消耗的卡路里的關係")
figure.show()

從上圖可以看出,每日熱量消耗與所採取的總步數之間存在直接關聯。 同樣的思路我們分析一下總路程和消耗卡路里的關係:

# 研究一下每日總路程和消耗的卡路里之間的聯繫。 
figure = px.scatter(data_frame = data.dropna(), x="Calories",
                    y="TotalDistance", size="VeryActiveMinutes", 
                    trendline="lowess", color='TotalSteps',
                    title="總路程和消耗的卡路里的關係")
figure.show()

上圖可以看到,總路程和卡路里之間也是正相關的關係。下面讓我們分析一下一天中的平均總活躍分鐘數。

label = ["Very Active Minutes", "Fairly Active Minutes", "Lightly Active Minutes", "Inactive Minutes"]
counts = data[["VeryActiveMinutes", "FairlyActiveMinutes", "LightlyActiveMinutes", "SedentaryMinutes"]].mean()
colors = ["gold","lightgreen", "pink", "blue"]

fig = go.Figure(data=[go.Pie(labels=label, values=counts)])
fig.update_layout(title_text="總活動時間")
fig.update_traces(hoverinfo="label+percent", textinfo="value", textfont_size=24, marker=dict(colors=colors, line=dict(color="black", width=3)))
fig.show()

一些觀察結論

  • 81.3% 的非活動分鐘數
  • 15.8% 的輕度活動分鐘數
  • 平均21 分鐘(1.74%)非常活躍
  • 評價13 分鐘(1.11%)的相當活躍的分鐘數

下面我們展開做一點更詳細的分析,我們先抽取更細化的信息,我們添加一個新字段「Day」記錄星期幾。

data["Day"] = data["ActivityDate"].dt.day_name()
data["Day"].head()

下面我們可視化對比一下一周中每一天的『非常活躍』、『相當活躍』和『輕度活躍』的分鐘數。

fig = go.Figure()

fig.add_trace(go.Bar(
                         x=data["Day"],
                         y=data["VeryActiveMinutes"],
                         name="Very Active",
                         marker_color="purple"
                        ))

fig.add_trace(go.Bar(
                         x=data["Day"],
                         y=data["FairlyActiveMinutes"],
                         name="Fairly Active",
                         marker_color="green"
                        ))

fig.add_trace(go.Bar(
                         x=data["Day"],
                         y=data["LightlyActiveMinutes"],
                         name="Lightly Active",
                         marker_color="pink"
                        ))

fig.update_layout(barmode="group", xaxis_tickangle=-45)

fig.show()

讓我們看看一周中每一天的非活動分鐘數。

day = data["Day"].value_counts()
label = day.index
counts = data["SedentaryMinutes"]

colors = ['gold','lightgreen', "pink", "blue", "skyblue", "cyan", "orange"]
fig = go.Figure(data=[go.Pie(labels=label, values=counts)])

fig.update_layout(title_text='Inactive Minutes Daily')
fig.update_traces(hoverinfo='label+percent', textinfo='value', textfont_size=30,
                  marker=dict(colors=colors, line=dict(color='black', width=3)))
fig.show()

從這份數據看來,星期四是大家最不活躍的一天。 下面我們來看看一周中每一天燃燒的卡路里數。

calories = data["Day"].value_counts()
label = calories.index
counts = data["Calories"]

colors = ['gold','lightgreen', "pink", "blue", "skyblue", "cyan", "orange"]
fig = go.Figure(data=[go.Pie(labels=label, values=counts)])

fig.update_layout(title_text='Calories Burned Daily')
fig.update_traces(hoverinfo='label+percent', textinfo='value', textfont_size=30, marker=dict(colors=colors, line=dict(color='black', width=3)))
fig.show()

從上圖可以看出,星期二是這份數據集中的用戶最活躍的日子之一,這一天燃燒的卡路里最多。 下面我們分析一下每日步數:

import seaborn as sns
sns.set(rc={'figure.figsize':(8,6)})
activity_by_week_day = sns.barplot(x="Day", y="TotalSteps", data=data, 
                                   order=['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'], 
                                   capsize=.2)

💡 每日消耗卡路里預估

下面我們搭建一個模型,對於每日消耗的卡路里進行建模預估。

本部分涉及到的模型知識與建模操作方法,參見ShowMeAI以下部分教程:

我們剔除ID類特徵和日期特徵,把『Calories』作為目標,把其他字段作為特徵,注意其中的『星期幾/Day』字段是類別型,我們要單獨編碼一下。

features = ['TotalSteps', 'TotalDistance', 'TrackerDistance', 'LoggedActivitiesDistance', 'VeryActiveDistance', 'ModeratelyActiveDistance', 'LightActiveDistance', 'SedentaryActiveDistance', 'VeryActiveMinutes', 'FairlyActiveMinutes', 'LightlyActiveMinutes', 'SedentaryMinutes', 'TotalMinutes', 'Day']
target = 'Calories'

# 數據切分
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(data[features], data[target], test_size=0.2, random_state=0)

# 使用lightgbm訓練
from lightgbm import LGBMRegressor
lgbm = LGBMRegressor(n_estimators=1000, learning_rate=0.05, random_state=0)

# 「星期幾」字段編碼
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
X_train['Day'] = le.fit_transform(X_train['Day'])
X_test['Day'] = le.transform(X_test['Day'])

# 擬合模型
lgbm.fit(X_train, y_train)

# 測試集預估
predictions = lgbm.predict(X_test)

# 計算測試集RMSE
from sklearn.metrics import mean_squared_error
rmse = np.sqrt(mean_squared_error(y_test, predictions))
print("RMSE: %f" % (rmse))

運行得到結果為RMSE: 373.128953

為了更準確地進行建模和評估,我們使用網格搜索交叉驗證進行超參數優化:

# 使用網格搜索對lightgbm模型進行超參數調優
from sklearn.model_selection import GridSearchCV
parameters = {
                'learning_rate': [0.02, 0.05, 0.08, 0.1],
                'max_depth': [5, 7, 10],
                'feature_fraction': [0.6, 0.8, 0.9],
                'subsample': [0.6, 0.8, 0.9],
                'n_estimators': [100, 200, 500, 1000]}

# 網格搜索
grid_search = GridSearchCV(lgbm, parameters, cv=5, n_jobs=-1, verbose=1)

# 最佳模型
grid_search.fit(X_train, y_train)
best_lgbm = grid_search.best_estimator_
# 輸出最佳超參數
print(grid_search.best_params_)
# 測試集預估
predictions = best_lgbm.predict(X_test)
# 計算RMSE
from sklearn.metrics import mean_squared_error
rmse = np.sqrt(mean_squared_error(y_test, predictions))
print("RMSE: %f" % (rmse))

最終結果輸出

{'feature_fraction': 0.6, 'learning_rate': 0.05, 'max_depth': 5, 'n_estimators': 1000, 'subsample': 0.6}
RMSE: 352.782209

我們可以看到,調參後的模型在測試集上表現更優。最後我們輸出一下特徵重要度,看看那些因素對於卡路里消耗更加重要:

#繪製特徵重要度
import matplotlib.pyplot as plt
plt.figure(figsize=(20,10))
importance = best_lgbm.feature_importances_
feature_importance = pd.DataFrame({'feature': features, 'importance': importance})
feature_importance = feature_importance.sort_values('importance', ascending=True)
feature_importance.plot.barh(x='feature', y='importance', figsize=(20,10))

可以看到,每日總步數對結果影響最大,大家要多多抬腿多多運動!

參考資料