STRAVAアプリで計測したライドデータとFitbitが計測したライドデータの比較 その1
こんにちは、ライドログ保存が趣味のTakum!です。
以前も書きかけて深追いできずにいた、気になる問題。
今回こそ、その真相に迫ろうと思います。
対象のライドは、2019/1/31の自宅から市内の駐輪場までの約11km。
STRAVAを起動したスマホをポケットに入れ、自転車に跨り、Fitbitのbikeをスタートさせて11km走り、駐輪場に自転車を停め、歩きながらFitbitの記録を止め、STRAVAの記録を止めました。(なので、歩いている距離分、STRAVAが多少大きく出るはずです。)
ライドの結果は、
a. STRAVAアプリ:距離11.03km タイム34:45 高度121m
b. Fitbitアプリ:距離9.39km タイム42:01 高度53m
となっており、その距離の誤差はちょっと看過できません。
ちなみに、STRAVAアプリは自動停止がON(停止中はログ保存しない)となっているため、タイムは信号待ち等で停止していた時間を除いた移動中の時間ということになります。FitbitからSTRAVAにデータ連携した後のデータがどうにか加工されているかもしれないので、連携されたデータをSTRAVAでエクスポートしたファイル(c)も含めて、検証してみたいと思います。
まずはエクスポートされるXMLデータのフォーマットを見てみましょう。
a. STRAVAからエクスポートされるのは、GPX形式で、
tail()なのは、head()だと自宅の座標が出てしまうからです(-_-;)
各値は数値に見えますが、df.dtypes を見ると、object型になっているので、それぞれ変換していきます。時刻もこの時点で変換してしまいましょう。
インデックスを、str_日時に変更して、str_timeは削除してしまいます。
次は、Fitbitから直接ダウンロードしたTCXファイルをDataFrameに変換していきます。
以前も書きかけて深追いできずにいた、気になる問題。
a.STRAVAアプリで計測したデータ と
b.Fitbitで計測し、STRAVAに連携されたデータ
の距離がずいぶんと異なることです。本当は、心拍数も記録されたFitbitのデータを保存したいところなのですが、移動距離が短く出てしまうため、STRAVAのデータの方を保存しています。(Fitbitデータは、二重カウントにならないように、削除してます。)今回こそ、その真相に迫ろうと思います。
分析するデータ3つ
対象のライドは、2019/1/31の自宅から市内の駐輪場までの約11km。
STRAVAを起動したスマホをポケットに入れ、自転車に跨り、Fitbitのbikeをスタートさせて11km走り、駐輪場に自転車を停め、歩きながらFitbitの記録を止め、STRAVAの記録を止めました。(なので、歩いている距離分、STRAVAが多少大きく出るはずです。)
ライドの結果は、
a. STRAVAアプリ:距離11.03km タイム34:45 高度121m
b. Fitbitアプリ:距離9.39km タイム42:01 高度53m
となっており、その距離の誤差はちょっと看過できません。
ちなみに、STRAVAアプリは自動停止がON(停止中はログ保存しない)となっているため、タイムは信号待ち等で停止していた時間を除いた移動中の時間ということになります。FitbitからSTRAVAにデータ連携した後のデータがどうにか加工されているかもしれないので、連携されたデータをSTRAVAでエクスポートしたファイル(c)も含めて、検証してみたいと思います。
データの形式
まずはエクスポートされるXMLデータのフォーマットを見てみましょう。
a. STRAVAからエクスポートされるのは、GPX形式で、
<trkpt lat="緯度" lon="経度"> <ele>標高</ele> <time>2019-01-30T23:22:43Z</time> </trkpt>
のようになっています。時間は協定世界時の形式です。日本の時間に直すには、+9時間の補正が必要になります。
b. Fitbitからエクスポートされるのは、TCX形式で、
<Trackpoint> <Time>2019-01-31T08:22:39.000+09:00</Time> <Position> <LatitudeDegrees>緯度</LatitudeDegrees> <LongitudeDegrees>経度</LongitudeDegrees> </Position> <AltitudeMeters>標高</AltitudeMeters> <DistanceMeters>移動距離累積値</DistanceMeters> <HeartRateBpm> <Value>心拍数</Value> </HeartRateBpm> </Trackpoint>
のようになっています。こちらは日本時間で記録されていますね。心拍数と、移動距離の累積値が付いています。これで何かわかりそうです。
GPXファイルをDataFrameに変換
それでは、これらのデータ、XMLでは扱いにくいので、Pythonに取り込んでみたいと思います。JupyterNotebookにて作業をしていきます。
import pandas as pd import numpy as np from bs4 import BeautifulSoup import datetime with open('Stravaで記録したGPX.gpx', 'r', encoding='utf-8') as xml_file: soup = BeautifulSoup(xml_file, 'xml') trackpoints = soup.find_all('trkpt') len(trackpoints)
1732行のデータであることが分かりました。
ここから、DataFrameに変換していきます。
points = [] for trackpoint in trackpoints: timestamp = trackpoint.find('time').text latitude = trackpoint.attrs['lat'] longitude = trackpoint.attrs['lon'] altitude = trackpoint.find('ele').text point = { 'str_time': timestamp, 'str_lat': latitude, 'str_lng': longitude, 'str_alt': altitude, } points.append(point) df = pd.DataFrame(points) df.tail()
str_alt | str_lat | str_lng | str_time | |
---|---|---|---|---|
1727 | 52.8 | 38.2599200 | 140.8757030 | 2019-01-30T23:22:38Z |
1728 | 52.8 | 38.2599450 | 140.8757280 | 2019-01-30T23:22:39Z |
1729 | 53.0 | 38.2599800 | 140.8757660 | 2019-01-30T23:22:41Z |
1730 | 53.0 | 38.2600030 | 140.8757930 | 2019-01-30T23:22:43Z |
1731 | 53.1 | 38.2600330 | 140.8758000 | 2019-01-30T23:22:48Z |
tail()なのは、head()だと自宅の座標が出てしまうからです(-_-;)
各値は数値に見えますが、df.dtypes を見ると、object型になっているので、それぞれ変換していきます。時刻もこの時点で変換してしまいましょう。
df.loc[:, 'str_日時'] = df.loc[:, 'str_time'].apply(pd.to_datetime)+datetime.timedelta(hours=9) df.loc[:, "str_alt"] = df.loc[:, "str_alt"].astype(np.float32) df.loc[:, "str_lat"] = df.loc[:, "str_lat"].astype(np.float32) df.loc[:, "str_lng"] = df.loc[:, "str_lng"].astype(np.float32)
str_alt | str_lat | str_lng | str_time | str_日時 | |
---|---|---|---|---|---|
1727 | 52.799999 | 38.259918 | 140.875702 | 2019-01-30T23:22:38Z | 2019-01-31 08:22:38 |
1728 | 52.799999 | 38.259945 | 140.875732 | 2019-01-30T23:22:39Z | 2019-01-31 08:22:39 |
1729 | 53.000000 | 38.259979 | 140.875763 | 2019-01-30T23:22:41Z | 2019-01-31 08:22:41 |
1730 | 53.000000 | 38.260002 | 140.875793 | 2019-01-30T23:22:43Z | 2019-01-31 08:22:43 |
1731 | 53.099998 | 38.260033 | 140.875793 | 2019-01-30T23:22:48Z | 2019-01-31 08:22:48 |
インデックスを、str_日時に変更して、str_timeは削除してしまいます。
df = df.set_index('str_日時') df = df.drop('str_time', axis=1) df.tail()
str_alt | str_lat | str_lng | |
---|---|---|---|
str_日時 | |||
2019-01-31 08:22:38 | 52.799999 | 38.259918 | 140.875702 |
2019-01-31 08:22:39 | 52.799999 | 38.259945 | 140.875732 |
2019-01-31 08:22:41 | 53.000000 | 38.259979 | 140.875763 |
2019-01-31 08:22:43 | 53.000000 | 38.260002 | 140.875793 |
2019-01-31 08:22:48 | 53.099998 | 38.260033 | 140.875793 |
一旦完成です。pickleファイルとして保存します。
df.to_pickle('strava.pickle')
同様に、fitbitから自動連携してSTRAVAにアップされたGPXファイルもDataFrameに変換し、pickleとして保存します。
※心拍も連携されていますので、gpxtpx:hrによって値が取れます。
TCXファイルからDataFrameに変換
次は、Fitbitから直接ダウンロードしたTCXファイルをDataFrameに変換していきます。
import pandas as pd import numpy as np from bs4 import BeautifulSoup import datetime with open('fitbitで記録したTCX.tcx', 'r', encoding='utf-8') as xml_file: soup = BeautifulSoup(xml_file, 'xml') trackpoints = soup.find_all('Trackpoint') points = [] for trackpoint in trackpoints: timestamp = trackpoint.find('Time').text latitude = trackpoint.find('LatitudeDegrees').text longitude = trackpoint.find('LongitudeDegrees').text altitude = trackpoint.find('AltitudeMeters').text distance = trackpoint.find('DistanceMeters').text heartrate = trackpoint.find('Value').text point = { 'fit_time': timestamp, 'fit_lat': latitude, 'fit_lng': longitude, 'fit_alt': altitude, 'fit_dist': distance, 'fit_hr': heartrate } points.append(point) df = pd.DataFrame(points) df.tail()
fit_alt | fit_dist | fit_hr | fit_lat | fit_lng | fit_time | |
---|---|---|---|---|---|---|
2391 | 57.5 | 9388.570000000002 | 117 | 38.25991249084473 | 140.87570250034332 | 2019-01-31T08:22:35.000+09:00 |
2392 | 58.0 | 9389.86 | 117 | 38.25991106033325 | 140.8756968975067 | 2019-01-31T08:22:36.000+09:00 |
2393 | 58.4 | 9391.220000000001 | 116 | 38.25992226600647 | 140.8757063150406 | 2019-01-31T08:22:37.000+09:00 |
2394 | 58.55 | 9392.67 | 115 | 38.25993084907532 | 140.87571513652802 | 2019-01-31T08:22:38.000+09:00 |
2395 | 58.7 | 9394.23 | 115 | 38.25993740558624 | 140.8757222890854 | 2019-01-31T08:22:39.000+09:00 |
同様に、データ型を変換し、日時をインデックスに変換します。
df.loc[:, 'fit_日時'] = df.loc[:, 'fit_time'].apply(pd.to_datetime)+datetime.timedelta(hours=9) df.loc[:, "fit_alt"] = df.loc[:, "fit_alt"].astype(np.float32) df.loc[:, "fit_lat"] = df.loc[:, "fit_lat"].astype(np.float32) df.loc[:, "fit_lng"] = df.loc[:, "fit_lng"].astype(np.float32) df.loc[:, "fit_dist"] = df.loc[:, "fit_dist"].astype(np.float32) df.loc[:, "fit_hr"] = df.loc[:, "fit_hr"].astype(np.int) df = df.set_index('fit_日時') df = df.drop('fit_time', axis=1) df.tail()
fit_alt | fit_dist | fit_hr | fit_lat | fit_lng | |
---|---|---|---|---|---|
fit_日時 | |||||
2019-01-31 08:22:35 | 57.500000 | 9388.570312 | 117 | 38.259911 | 140.875702 |
2019-01-31 08:22:36 | 58.000000 | 9389.860352 | 117 | 38.259911 | 140.875702 |
2019-01-31 08:22:37 | 58.400002 | 9391.219727 | 116 | 38.259922 | 140.875702 |
2019-01-31 08:22:38 | 58.549999 | 9392.669922 | 115 | 38.259930 | 140.875717 |
2019-01-31 08:22:39 | 58.700001 | 9394.230469 | 115 | 38.259937 | 140.875717 |
df.to_pickle('fitbit.pickle')
これで、分析する3つのpickleがそろいました。
コメント
コメントを投稿
コメントをどうぞ。