Published on

Using ChatGPT and Python to Make Better Looking Apple Health Data

Authors
  • avatar
    Name
    Chris Fitzgerald
    Twitter

Turning blah into hoorah

Apple Health is a great app that allows users to track their physical activity, heart rate, and other health-related data. However, the app has limited visualization capabilities. For instance, the Heart Rate data mainly shows your heart rate on a single range bar. I am no data scientist, but I think the visualization capabilities for the heart rate data are honestly boring. Fortunately, Apple is kind enough to share this data, allowing us to get creative. In this post, I will share my experience using Python and OpenAI’s ChatGPT to quickly create code that extracts my Heart Rate data and plots it using the Python libraries Pandas and matplotlib.

apple Heart Rate

Exporting Apple Health Data

The first step in this process is to export your Apple Health data. This can be done by following these steps:

  1. Open the Health app on your iPhone.
  2. Click on your profile picture in the top-right corner.
  3. Scroll down and click on "Export Health Data."
  4. Enter your password and click on "Export."
  5. The data will be exported as a zip file.

Once you have exported your data, you will need to extract it from the zip file. The data is stored in an XML file, which contains all of the health-related data that the app has collected. If you are following along, you’ll want to place the “export.xml” file in the same folder as the Python file we will be creating in subsequent steps. I would also call out that my Apple Health data has only been recording for roughly two weeks. I assume this file could be quite large for others where adding a condition to pull only recent dates would be beneficial.

Using ChatGPT to Generate a Script that Extracts Heart Rate Data

I am a forever learner and enjoy messing around with projects that are already working (boilerplate, cloning repos, etc..). So using an AI chatbot to jumpstart an idea I have off the ground is thrilling for me. It not only fills out the blank page, but it exposes me to new techniques and libraries that I may not have otherwise been exposed to. Here is what ChatGPT told me to do when I prompted it: We import the xml.etree.ElementTree module which appears to be a powerful tool for parsing XML data. ChatGPT decides to import Pandas to create a DataFrame and matplotlib to chart our extracted data:

import xml.etree.ElementTree as ET
import pandas as pd
import matplotlib.pyplot as plt

# Open the XML file and parse its contents
xml_file = 'export.xml'
root = ET.parse(xml_file).getroot()

# Extract the data we're interested in (in this case, heart rate data)
data = []
for record in root.iter('Record'):
    if record.get('type') == 'HKQuantityTypeIdentifierHeartRate':
        data.append({
            'date': record.get('startDate'),
            'heart_rate': float(record.get('value')),
        })

# Convert the data to a DataFrame
df = pd.DataFrame(data)

# Convert the date column to a datetime format and set it as the index
df['date'] = pd.to_datetime(df['date'])
df.set_index('date', inplace=True)

# Plot the data
df.plot()

# Display the chart
plt.show()

When this code is run we will create a pretty vanilla looking line graph like the one below:

Default Chart

Okay, nice.. we are getting somewhere. With literally no experience using .xml formatted metadata or matplotlib, this was a pretty awesome result to see after one prompt in chatGPT (a total of 10 minutes). I feel like I have super powers.

Using More MatPLotlib features

I wanted to take this a step further and prompted chatGPT to create a toggle feature where we can switch between a line graph and scatter points. I have also asked to ensure we are including some options to modify colors and formatting to allow us to get a bit more creative with the data. Here’s what it spat out:

import xml.etree.ElementTree as ET
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import tkinter as tk

# Open the XML file and parse its contents
xml_file = 'export.xml'
root = ET.parse(xml_file).getroot()

# Extract the data we're interested in (in this case, heart rate data)
data = []
for record in root.iter('Record'):
    if record.get('type') == 'HKQuantityTypeIdentifierHeartRate':
        timestamp = pd.to_datetime(record.get('startDate'))
        data.append({
            'date': timestamp,
            'heart_rate': float(record.get('value')),
        })

# Convert the data to a DataFrame
df = pd.DataFrame(data)

# Set the date column as the index
df.set_index('date', inplace=True)

# Create a new figure and plot the data as a line graph
fig, ax = plt.subplots(figsize=(10, 6))
line, = ax.plot(df.index, df['heart_rate'], '-')
ax.set_ylabel('Heart rate (bpm)')

# Set the background color of the figure and axes
fig.set_facecolor('#404040')
ax.set_facecolor('#404040')

# Set the color of the tick labels
ax.tick_params(axis='both', colors='white')

# Set the color of the x and y axis labels
ax.set_xlabel('Date', color='white')
ax.set_ylabel('Heart Rate (bpm)', color='white')

# Set the color of the title
ax.set_title('Heart Rate over Time', color='white')

# Set the background color of the plot area
ax.set_xlim(df.index.min(), df.index.max())
ax.set_ylim(df['heart_rate'].min() - 10, df['heart_rate'].max() + 10)
ax.patch.set_alpha(0)
ax.grid(color='white', alpha=0.2)

# Initialize a global variable for the scatter plot points
points = None

# Define a function to update the plot to show the data as scatter points
def show_points():
    global points
    line.set_visible(False)
    cmap = plt.get_cmap('cool')
    norm = plt.Normalize(df['heart_rate'].min(), df['heart_rate'].max())
    colors = cmap(norm(df['heart_rate']))
    points = ax.scatter(df.index, df['heart_rate'], s=20, c=colors, alpha=0.8)
    canvas.draw()

# Define a function to update the plot to show the data as a line graph
def show_line():
    global points
    if points is not None:
        points.set_visible(False)
    line.set_visible(True)
    cmap = plt.get_cmap('cool')
    canvas.draw()

# Create a GUI window and add a button to switch between the line graph and scatter plot
root = tk.Tk()
root.title('Heart Rate Data')
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
toolbar = tk.Frame(master=root)
toolbar.pack(side=tk.BOTTOM, pady=5)
button = tk.Button(master=toolbar, text='Show points', command=show_points)
button.pack(side=tk.LEFT, padx=5)
button = tk.Button(master=toolbar, text='Show line', command=show_line)
button.pack(side=tk.LEFT, padx=5)

# Start the GUI loop
tk.mainloop()

When run, we now have an easier to look at chart with not only a line graph that plots my data, but also the ability to switch to scatter plots which I believe works well to express what this data represents. I have also dug into matplotlib’s documentation and found a built-in gradient called ‘cool’ which we can define in the line cmap = plt.get_cmap('cool') to show the Y-axis with heightening intensity as my heart rate increases during exercise.

Okay Chart
Cool Chart

What the Health?

We have seen how to export Apple Health data and utilize ChatGPT to help create some code extract the data from the XML data file. We have familiarized ourselves with a couple of Python’s most powerful libraries such as Matplotlib and Pandas to create visualizations of this data. My next steps with this would be to overlay more data points from my health data the show when I am actively working out or asleep. I would also like to make the charts a bit more interactive so we can change the date range on the x-axis. Hopefully you enjoy reading about this project of mine and now have some fundamentals to build something of your own.