Garmin fit形式ファイルの解析手順

## はじめに
Garminなどで計測したデータをフルに活用するには、fitファイルとして出力されたデータを解析する必要があります。hiking時のデータを事例にfit形式ファイルを解析する方法をまとめました。

## 1. fitparseライブラリをインストールする

```python
!pip install fitparse
```

https://github.com/dtcooper/python-fitparse

```python
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import os
import fitparse
```

# 2. fit形式ファイルを読み込む

```python
fitfile = fitparse.FitFile("./xxxxxx/xxxxxx_ACTIVITY.fit")
fitdata=[]
for record in fitfile.get_messages("record"):
    for data in record:
        if data.units:
            fitdata+=[[data.name, data.value, data.units]]
        else:
            fitdata+=[[data.name, data.value, 'NaN']]
```

## 3. ファイルの中身を確認する

```python
df1=pd.DataFrame(fitdata)
df1.columns=['item','value','unit']
df1
```

表示例

<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>item</th>
      <th>value</th>
      <th>unit</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>altitude</td>
      <td>328.2</td>
      <td>m</td>
    </tr>
    <tr>
      <th>1</th>
      <td>cadence</td>
      <td>0</td>
      <td>rpm</td>
    </tr>
    <tr>
      <th>2</th>
      <td>distance</td>
      <td>0.0</td>
      <td>m</td>
    </tr>
    <tr>
      <th>3</th>
      <td>enhanced_altitude</td>
      <td>328.2</td>
      <td>m</td>
    </tr>
    <tr>
      <th>4</th>
      <td>enhanced_speed</td>
      <td>0.0</td>
      <td>m/s</td>
    </tr>
    <tr>
      <th>5</th>
      <td>fractional_cadence</td>
      <td>0.0</td>
      <td>rpm</td>
    </tr>
    <tr>
      <th>6</th>
      <td>heart_rate</td>
      <td>92</td>
      <td>bpm</td>
    </tr>
    <tr>
      <th>7</th>
      <td>position_lat</td>
      <td>None</td>
      <td>semicircles</td>
    </tr>
    <tr>
      <th>8</th>
      <td>position_long</td>
      <td>None</td>
      <td>semicircles</td>
    </tr>
    <tr>
      <th>9</th>
      <td>speed</td>
      <td>0.0</td>
      <td>m/s</td>
    </tr>
    <tr>
      <th>10</th>
      <td>temperature</td>
      <td>30</td>
      <td>C</td>
    </tr>
    <tr>
      <th>11</th>
      <td>timestamp</td>
      <td>2021-07-28 06:03:02</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>12</th>
      <td>unknown_87</td>
      <td>0</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>13</th>
      <td>unknown_88</td>
      <td>100</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>14</th>
      <td>altitude</td>
      <td>328.2</td>
      <td>m</td>
    </tr>
  </tbody>
</table>
</div>


## 4. 不要な項目を削除する

```python
df1['item'].value_counts()
```

    temperature           1015
    unknown_88            1015
    fractional_cadence    1015
    altitude              1015
    enhanced_speed        1015
    heart_rate            1015
    cadence               1015
    position_lat          1015
    distance              1015
    timestamp             1015
    speed                 1015
    unknown_87            1015
    position_long         1015
    enhanced_altitude     1015
    unknown_90             865
    Name: item, dtype: int64


```python
drop=[]
drop+=df1[df1['item']=='unknown_90'].index.tolist()
drop+=df1[df1['item']=='unknown_88'].index.tolist()
drop+=df1[df1['item']=='unknown_87'].index.tolist()
drop2=sorted(set(drop))
df2=df1.drop(index=df1.index[drop2]).reset_index(drop=True)
```

## 5. 項目名をカラム名にしたテーブルに変換する

```python
items2=df2['item'].unique().tolist()
df3=pd.DataFrame(columns=items2,index=range(len(df2)//len(items2)))
for item in items2:
    df3[item]=df2[df2['item']==item]['value'].tolist()
```

表示例
![qqqq.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311614/4c372f64-9825-9e85-8ef9-615837aa41df.png)

Semicircle単位の緯度、経度を度に直すには、以下の式で変換します。地図上にルート表示する場合などに使用します。

```python
df3['latitude']=df3['position_lat']*(180/2**31)
df3['longitude']=df3['position_long']*(180/2**31)
df4=df3[['timestamp','latitude','longitude','altitude','speed','heart_rate']]
```

## 6. グラフ化する

```python
fig=make_subplots(specs=[[{"secondary_y":False}]])
fig.add_trace(go.Scatter(x=df4['timestamp'],y=df4['altitude'],name='Altitude'),secondary_y=False,)
fig.update_layout(autosize=False,width=700,height=500,title_text="Altitude")
fig.update_xaxes(title_text="timestamp")
fig.update_yaxes(title_text="Altitude(m)",secondary_y=False)
fig.show()
```

![newplot (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311614/0628d435-6d27-cfec-1cae-9e7b562ccba1.png)


```python
fig=make_subplots(specs=[[{"secondary_y":False}]])
fig.add_trace(go.Scatter(x=df4['timestamp'],y=df4['speed'],name='speed'),secondary_y=False,)
fig.update_layout(autosize=False,width=700,height=500,title_text="Speed")
fig.update_xaxes(title_text="timestamp")
fig.update_yaxes(title_text="speed",secondary_y=False)
fig.show()
```

![newplot (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311614/0ab2c86e-96c1-8cbb-77f8-ee5c1d0a2f5a.png)


```python
fig=make_subplots(specs=[[{"secondary_y":False}]])
fig.add_trace(go.Scatter(x=df4['timestamp'],y=df4['heart_rate'],name='heart_rate'),secondary_y=False,)
fig.update_layout(autosize=False,width=700,height=500,title_text="Heart Rate")
fig.update_xaxes(title_text="timestamp")
fig.update_yaxes(title_text="heart_rate",secondary_y=False)
fig.show()
```

![newplot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311614/bae089f2-36d2-cc23-12b4-9338463fede7.png)