3 минут чтения
3 марта 2021 г.
Экспорт исторических данных Apple Health в Google Sheets
Для устройств на базе iOS и watchOS существует приложение Health, которое ежедневно записывает все данные о здоровье носителя и синхронизирует их со сторонними приложениями. Все эти данные в любой момент можно получить прямо из приложения в виде XML-документа. Сегодня мы выгрузим исторические данные о здоровье из приложения Apple Health, обработаем их и отправим в Google Sheets для анализа и визуализации в будущем.
Экспорт архива из приложения
Зайдите в приложение Health на iPhone. Нажмите на аватарку своего профиля в верхнем правом углу – откроется меню приложения.
Внизу нажмите на кнопку «Экспортировать медданные». Через некоторое время откроется меню экспорта — отправьте архив себе на компьютер любым способом, можно по AirDrop или даже по почте в письме самому себе. Из архива нужен только один файл — «экспорт.xml». Достаньте его и положите в папку с ноутбуком jupyter.
Парсер XML в DataFrame
При помощи библиотеки XML составляем дерево на основе документа из Health. Собирать в словарь будем следующие атрибуты: тип, единица измерения, дата создания, дата начала, дата конца, значение. Проходим по всему дереву и отправляем полученные значения атрибутов в records_dict.
from xml.etree import ElementTree
import pandas as pd
import datetime
tree = ElementTree.parse(‘экспорт.xml’)
root = tree.getroot()
records = root.findall(‘Record’)
records_dict = {
‘type’:[],
‘unit’:[],
‘creationDate’:[],
‘startDate’:[],
‘endDate’:[],
‘value’:[]
}
for record in records:
for attribute in records_dict.keys():
attribute_value = record.get(attribute)
records_dict[attribute].append(attribute_value)
События записаны в нечитабельном виде — для перевода составим специальный словарь с нужными типами, где ключ — старое название, а значение — новое. Мы возьмём только 11 событий: минуты осознанности, дистанция на велосипеде, дистанция заплыва, дистанция ходьбы и бега, пройдено пролётов, пульс, пульс в покое, шаги, активная энергия, энергия покоя и средний пульс при ходьбе.
types_dict = {
‘HKCategoryTypeIdentifierMindfulSession’: ‘Mindful Session’,
‘HKQuantityTypeIdentifierDistanceCycling’: ‘Cycling Distance’,
‘HKQuantityTypeIdentifierDistanceSwimming’: ‘Swimming Distance’,
‘HKQuantityTypeIdentifierDistanceWalkingRunning’: ‘Walking + Running Distance’,
‘HKQuantityTypeIdentifierFlightsClimbed’: ‘Flights Climbed’,
‘HKQuantityTypeIdentifierHeartRate’: ‘Heart Rate’,
‘HKQuantityTypeIdentifierRestingHeartRate’: ‘Resting Heart Rate’,
‘HKQuantityTypeIdentifierStepCount’: ‘Steps’,
‘HKQuantityTypeIdentifierActiveEnergyBurned’: ‘Active Calories’,
‘HKQuantityTypeIdentifierBasalEnergyBurned’: ‘Resting Calories’,
‘HKQuantityTypeIdentifierWalkingHeartRateAverage’: ‘Walking Heart Rate Average’
}
Для минут осознанности в поле значения записей нет — мы сами посчитаем позже это поле как разницу даты окончания и начала события. Разница будет представлена как timedelta, поэтому напишем функцию перевода timedelta в минуты:
def td_to_m(td):
seconds = td.seconds + td.days * 24 * 60 * 60
return seconds // 60
Из словаря создаём DataFrame и задаём названия колонок. Оставляем только те 11 событий, которые есть в словаре types_dict и приводим все колонки к нужным типам данных:
df = pd.DataFrame(records_dict)
df.columns = [‘type’, ‘unit’, ‘date’, ‘start’, ‘end’, ‘value’]
df = df[df[‘type’].isin(types_dict.keys())]
df[‘value’] = df[‘value’].astype(float)
df[‘date’] = df[‘date’].astype(‘datetime64’)
df[‘date’] = df[‘date’].dt.date
df[‘start’] = df[‘start’].astype(‘datetime64’)
df[‘end’] = df[‘end’].astype(‘datetime64’)
df[‘unit’] = df[‘unit’].astype(str)
Данные Health при экспорте никак не группируются — мы сделаем это самостоятельно. DataFrame можно поделить на три: в первом будут события, у которых единица измерения «количество в минуту» — для таких событий нужно искать среднее значение. В другой группе будут минуты осознанности — считаем число минут в каждой записи и суммируем. В последней группе находятся все остальные записи, связанные с количественными событиями — шаги, дистанция ходьбы и бега и так далее. Их тоже суммируем.
df_1 = df[df[‘unit’] == ‘count/min’]
df_1 = df_1.groupby(by=[‘date’, ‘type’, ‘unit’], as_index=False).agg({‘start’:’min’,
‘end’:’max’,
‘value’:’mean’})
df_2 = df[df[‘type’] == ‘HKCategoryTypeIdentifierMindfulSession’]
df_2[‘value’] = df_2[‘end’] — df_2[‘start’]
df_2[‘value’] = df_2[‘value’].map(td_to_m)
df_2 = df_2.groupby(by=[‘date’, ‘type’, ‘unit’], as_index=False).agg({‘start’:’min’,
‘end’:’max’,
‘value’:’sum’})
df_3 = df[(df[‘unit’] != ‘count/min’) & (df[‘type’] != ‘HKCategoryTypeIdentifierMindfulSession’)]
df_3 = df_3.groupby(by=[‘date’, ‘type’, ‘unit’], as_index=False).agg({‘start’:’min’,
‘end’:’max’,
‘value’:’sum’})
df = pd.concat([df_1, df_2, df_3])
Дату создания записи переводим в строковый тип. Все наименования типов событий заменяем согласно словарю types_dict. В переменную dates записываем все уникальные даты.
df[‘date’] = df[‘date’].astype(str)
df[‘type’] = df[‘type’].apply(lambda x: types_dict[x])
dates = df[‘date’].unique()
В результате нужен словарь с колонкой даты и отдельной колонкой под каждое из 11 событий:
result = {
‘date’: [],
‘Steps’: [],
‘Walking + Running Distance’: [],
‘Swimming Distance’: [],
‘Cycling Distance’: [],
‘Resting Calories’: [],
‘Active Calories’: [],
‘Flights Climbed’: [],
‘Heart Rate’: [],
‘Resting Heart Rate’: [],
‘Walking Heart Rate Average’: [],
‘Mindful Session’: []
}
Проходим по каждой дате и получаем кусок DataFrame за эту дату. Добавляем её в словарь и проходим по каждому ключу, пробуя добавить значение:
for date in dates:
part = df[df[‘date’] == date]
result[‘date’].append(date)
for key in result.keys():
if key == ‘date’:
continue
else:
field = ‘value’
try:
result[key].append(part[part[‘type’] == key][field].values[0])
except IndexError:
result[key].append(None)
Из полученного словаря создаём DataFrame, округляем всё до двух знаков после запятой и сортируем по дате:
result_df = pd.DataFrame(result)
result_df = result_df.round(2)
result_df = result_df.sort_values(by=’date’)
В результате получается такая таблица с историческими данными по 11 событиям:
Экспорт DataFrame в Google Sheets
Для экспорта в Google Docs необходим сервисный аккаунт и json-файл с ключом. О том, как его получить, мы писали в материале «Собираем данные по рекламным кампаниям ВКонтакте»
Создайте новый документ в Google Sheets. Весь DataFrame можно вставить одним действием при помощи методов библиотеки gspread. Импортируйте её, а также укажите идентификатор документа и json-файл с ключом. В методе get_worksheet указывается порядковый номер листа в файле начиная с нуля.
import pandas as pd
import gspread
from gspread_dataframe import set_with_dataframe
gc = gspread.service_account(filename=’serviceAccount.json’)
sh = gc.open_by_key(‘1osKA63LQkUC0FC0eIZ63jEJwn1TeIkUvqCV6ur’)
worksheet = sh.get_worksheet(0)
В итоге в Google Spreadsheets появится такая таблица:
А в следующем материале посмотрим, как наладить ежедневный экспорт данных Здоровья в эту таблицу при помощи шорткатов и Google AppScript!
[ Рекомендации ]
Читайте также
[ Связаться ]
Давайте раскроем потенциал вашего бизнеса вместе
Заполните форму на бесплатную консультацию