个人生活的量化分析(二):Apple健康数据分析
点击蓝字关注这个神奇的公众号~
林筱越:华东政法大学 社会学专业 R语言爱好者
往期回顾:
全文目录:
1. 前言
2. 数据准备
3. 数据导入
4. 数据预处理
5. 可视化部分
6. 结语
1. 前言
因为我是一个平时经常锻炼的人,因此基本上每次下载一个有关健康的app(例如Keep、悦跑圈等),我都会允许将数据导入iPhone的自带的健康app。
可能大家平时只对于健康app使用的比较少,但是它除了能提供有关健康数据的视图部分外,其实它还提供了「数据导出」的功能,这毫无疑问也为了解自己的锻炼状况提供了分析的可能。
本次使用的数据是通过iPhone导出后的健康数据,用以看看自己之前的一些锻炼状况。
2. 数据准备
进入健康app后点击右上角的用户图标:
之后进入之后点击最下面的「Export Data」,然后iPhone会将所有健康数据以一个名为「export.zip」的压缩包形式打包,接着通过iCloud或者微信传输到电脑:
得到压缩包后解压可以得到是「xml文件」,仍然不是我们需要的csv表格或xls格式,所以这时就需要进入健康数据转化的网站(http://ericwolter.com/projects/health-export.html),然后直接将解压后的xml文件拖入当中转化后便可以得到所有健康数据了:
由于篇幅有限,在导入数据之前我已经用Excel对所有csv格式的数据做了「分列」的预先处理,并删除一些无用的列,保留了基本的数据;但这并不代表数据就已经可以完全拿来使用了,因此还需要进一步进行清洗。
本次仅选取「体重记录」、「饮水量」、「卡路里消耗」、「每日步数记录」四部分数据来进行分析。
3. 数据导入
此次为大家展示一下通过与数据库交互,来导入数据将4个csv的数据文件导入进R;在此之前已经将4个文件导入MySQL:
library(RMySQL) #读取MySQL
library(tidyverse) #数据处理、可视化
# 1. 数据导入部分
## 调用MySQL
conn <- dbConnect(MySQL(), dbname='rdata',
username='root',
password='934804',
host='127.0.0.1',
port=3306)
## 读取表格
weight <- dbReadTable(conn, 'weight')
water <- dbReadTable(conn, 'water')
steps <- dbReadTable(conn, 'step_count')
calories <- dbReadTable(conn, 'calories')
• 其中conn部分最为重要:
– dbname:为数据库中的数据库名称
– username:一般个人的MySQL都为「root」
– password:是自己设定的密码(也可能为空)
– host:一般填「127.0.0.1」
– port:端口好一般为「3306」
• 建立好与MySQL连接的端口后,就可以使用RMySQL包中的dbReadTable函数来对数据库的表进行读取,第一个参数为端口,第二个参数为表格名称
• 不熟悉MySQL的朋友可以使用read_csv来读取,读取tidyverse包后就可以使用
4. 数据预处理
4.1 保留变量
由于这四部分数据存在着数据内容上的一致性,为了统一操作,这里其存入一个list中:
# 存入list
all_data <- list(weight, water, steps, calories)然后这时,我们用for循环来对这四个数据进行批量操作,即保留仅需要的列:
# 通过for循环保留列
keep_cols <- c('sourceName', 'creationDate', 'value')
for (i in c(1:4)) {
all_data[[i]] <- all_data[[i]][,keep_cols]
}
• 这里系统会提示说选择了没有定义列的警告信息,这里是因为有些数据没有sourceName这一列,但实际上并不影响列的提取,可以忽略
4.2 数据重组
由于没有使用read_csv函数导入,所以这里导入的所有数据都是字符串类型;但通过观察数据可以发现,所有数据基本都是包含时间对象和一个记录值。但时间对象我们仅需要提取「日期」即可,因此这里就需要所有数据时间对象进行日期提取。
在此之前我们先简单定义一个字符串列表分隔的函数:
# 定义提取日期函数
date_split_func <- function(name) {
date_split <- strsplit(name$creationDate, ' ')
date_get <- sapply(date_split, '[[', 1)
return(as.Date(date_get))
}
• 这里需要注意的就是sapply的用法:
– 第一个参数指定的是已经被strsplit函数分割的字符串列表
– 第二个参数是索引列表中的各部分(如果大家对此有疑问的话,可以查阅《R语言实战/第5章:高级数据管理》中学生成绩排名这一案例,当中就使用到了这一函数,很实用。算是一个骚操作:p)
– 第三个参数是提取时间对象的第1列(也就是日期)
接下来就是将四个数据进行筛选和重组:
# 数据筛选与重组
weight <- all_data[[1]]
weight_date <- date_split_func(weight)
weight <- weight %>%
select(value) %>%
cbind(weight_date)
calories <- all_data[[4]]
calories_date <- date_split_func(calories)
calories <- calories %>%
select(value, sourceName) %>%
cbind(calories_date)
• 这里为了方便大家阅读,就删去当中water和steps数据的重组过程,只需要将weight数据过程中的名称和索引进行替换即可。这三个数据的重组过程完全一致
• 然后calories数据与其他三个数据不同的是,多提取了sourceName一列
5. 可视化部分
5.1 体重记录
上个寒假放假的一段期间里,我每天都会早上测量自己的体重,打算看看在开学之前自己能胖多少斤:
weight %>%
ggplot(aes(x=weight_date, y=as.numeric(value))) +
geom_point() +
geom_line() +
# 添加平均线
# 设置颜色和线条:红色虚线,
# alpha:透明度减少50%
geom_hline(yintercept = mean(as.numeric(weight$value)),
color='red', linetype='dotdash', alpha=0.5)+
labs(x='日期(天)', y='重量(kg)', title='体重记录') +
scale_x_date(date_breaks = '3 day') +
# 在图中添加注释
# bolditalic为斜粗体
# parse=T时转化为数学公式样式
annotate(geom='text', x=weight_date[4], y=68,
label = 'paste(bolditalic(Mean), \' = 66.94(kg)\')', parse=T) +
# plot.title用于调整标题
theme_bw() +
theme(text = element_text(family = 'STKaiti'),
axis.text.x = element_text(angle = 45, vjust=1, hjust = 1),
plot.title = element_text(hjust = 0.5))
• 当中一些细节调整我已给出注释,大家对照着看应该很容易理解;后面我也不再一一重复
• 为了使图形简介,我使用scale_x_date函数稍微对x轴的日期进行分割
• 对于annotate函数中的选项,这里给大家稍微详解一下:
– x和y的坐标实际上是我个人指定图形中的空白区域部分,为了label注释部分能很好的展示
– label部分我使用了paste来将字符串进行黏贴,当中要注意一下「\」单斜杆的用法:用于对符号进行转化,以便系统视为「文本」的一部分
– parse=T时,label的内容会以数学公式的样式来呈现
在放假前,我的体重是63kg。从图形可以看出,一个假期我竟然一直在胖!
5.2 饮水量记录
为了贯彻每天8杯水的饮水理念(1杯水大概500ml?),我使用了一款名为「Water Reminder」的app来进行记录,就假定水杯为500ml,所以每次就喝一半就为250ml,因此就对应记录:
# 查看平均饮水量
water %>%
group_by(water_date) %>%
summarize(total_water = sum(as.numeric(value))) %>%
summarize(mean_drink = mean(total_water))
# 绘制
water_plot <- water %>%
group_by(water_date) %>%
summarise(total_water = sum(as.numeric(value))) %>%
ggplot(aes(x=water_date, y=total_water))+
geom_line() +
labs(x='日期(每3天)', y='饮水量(毫升)', title='饮水量') +
# 这里的yintercept=2802根据平均饮水量来输入
geom_hline(yintercept = 2802, color='red',
linetype='dotdash', alpha=0.5)+
annotate(geom='text', x=weight_date[1], y=1000,
label = 'paste(bolditalic(Mean), \' = 2802(ml)\')', parse=T) +
scale_x_date(date_breaks = '3 day') +
theme_bw() +
theme(text = element_text(family = 'STKaiti'),
axis.text.x = element_text(angle = 45, vjust=1,hjust = 1),
plot.title = element_text(hjust = 0.5))
从趋势可以看出,每天饮水量在不断下降(好吧,其实有时候会懒得记或者忘记记了);没想到的是,1月19号那天我竟然喝了5L的水!
不过平均来说我每天喝水量大概也接近3000ml了(查看了一下中位数与平均数差不多,这里就没有使用中位数),总的来说每天身体的水分还是相对维持在一个平衡的状态吧!(多喝水有益健康)
5.3 卡路里消耗记录
不同app的卡路里记录
成人每日消耗大约1000~1500千卡(百度的)。手机里的卡路里记录主要由日常行走和锻炼两部分组成,虽然可能会有部分重合,但是对于整体的影响并不大,还是能较为直观的反映出平日里的能量消耗。
这里我就首先看一看我在Keep和悦跑圈两个app上锻炼时候的记录:
# 提出分组好的数据集
diff_app <- calories %>%
group_by(sourceName) %>%
summarize(total_calories = sum(as.numeric(value)))
# 创建标签
calories_label <- paste(diff_app$total_calories,'Kcal')
# 绘制
diff_app %>%
ggplot(aes(x=sourceName, y=total_calories, fill=factor(sourceName))) +
geom_bar(stat='identity', show.legend = F, width = 0.3) +
labs(x='健康App',y='总消耗卡路里(Kcal)',title='不同app的卡路里记录') +
# 将标签映射到数据上
geom_text(aes(label=calories_label), vjust = -0.2) +
theme_bw() +
theme(text = element_text(family='STKaiti'),
panel.grid.major.x = element_blank(),
plot.title = element_text(hjust = 0.5))
• 悦跑圈功能相对简单,仅作为跑步使用,消耗了3505千卡
• Keep既提供锻炼健身动作的功能,也提供了跑步的功能,因此自己经常性使用;到目前为止也已经消耗了大约11142千卡!
2018年的卡路里记录
数据里包含了从2017年到至今的数据(期间还原过手机几次,所以更早的记录就丢失了),但这里仅选取2018年的数据:
# 建立上年记录的索引
# 提取本年度的记录
last_year <-seq.Date(from = as.Date('2017-01-01'),
to = as.Date('2017-12-31'),
by = 'day')
drop_index <- calories$calories_date %in% last_year
calories <- calories[which(!drop_index),]
# 查看中位数
median(as.numeric(calories$value))
# 绘制部分
calories %>%
group_by(calories_date) %>%
summarise(total_calories = sum(as.numeric(value))) %>%
ggplot(aes(x=calories_date, y=total_calories)) +
geom_point() +
geom_line(alpha = 0.5) +
geom_hline(yintercept = 83, linetype='dotdash',
color='red', alpha=0.5) +
labs(x='日期(月)',y='总卡路里(Kcal)', title='卡路里消耗图') +
scale_x_date(date_breaks = 'week') +
annotate(geom='text', x=calories$calories_date[15], y=300,
label = 'paste(bolditalic(Median),\' = 83(Kcal)\')', parse = T) +
theme_bw() +
theme(text = element_text(family = 'STKaiti'),
axis.text.x = element_text(angle = 45, vjust = 1, hjust =1),
plot.title = element_text(hjust = 0.5))
• 这里的83千卡其实并不准确,因为这仅是手机记录的运动时消耗的能量;还缺少日常行为所消耗的能量。
• 同时,为了避免受极端值的影响,我使用了中位数来进行衡量。
可以看到的,今年是平时活动时消耗的卡路里大约是83Kcal,主要是因为今年处于备考状态,所以在锻炼上所分配的精力就少了,这也就是为什么导致记录的数据不变偏低的原因。
5.4 每日步数记录
在大一大二喜欢跑步的时候,经常会沉迷于微信运动的排行榜,不过之后就没有再关注并且关闭了微信运动(因为点赞什么的太烦)。不过每日步数的记录,也能侧面反映出一个人每日活动的状况吧:
# 汇总数据
group_steps <- steps%>%
group_by(steps_date) %>%
summarize(total_steps = sum(as.numeric(value)))
# 查看均值
mean_steps <- mean(group_steps$total_steps)
# 绘制部分
steps_plot <- group_steps %>%
ggplot(aes(x=steps_date, y=total_steps)) +
geom_line() +
labs(x='日期(周)', y='每日总步数', title='行走步数') +
scale_x_date(date_breaks='week') +
geom_hline(yintercept = mean_steps, linetype='dotdash',
color='red', alpha=0.5) +
annotate(geom='text', x=group_steps$steps_date[120], y=18000,
label = 'paste(bolditalic(Mean),\' = 7947.88(steps)\')',
parse = T) +
theme_bw() +
theme(text = element_text(family = 'STKaiti'),
axis.text.x = element_text(angle=45, vjust=1, hjust=1),
plot.title = element_text(hjust = 0.5))
• 从图形可以看出,基本上2017年下半年,因为课程安排的还是比较多,所以几乎每天都保持超过8000步的行走记录
• 到了今年,行走的步数也越来越少了……因为基本上每天活动的范围仅限于寝室……
6. 结语
• 分析至此,我觉得我平日里的生活习惯还是蛮健康的,也正因如此也会渐渐养成了某些保持健康的习惯。
• iPhone的健康app记录了多种活动的数据(如果有佩戴Apple Watch可能还会记录心率之类的数据)并提供了数据导出的功能;当然,如果拥有更多健康测量的设备,那么获得的数据也许还会更多。
• 可以看出,几张可视化的分析(除了app对比的那张图外)基本上可以看作是时间序列对象的描述性分析;至于对时间序列进行分解与建模更深层次、更复杂的统计方法方面,暂时没有涉及,不过还是有深挖的可能。如果感兴趣对自己的日常行为有兴趣的朋友可以尝试去探索一番!
公众号后台回复关键字即可学习
回复 R R语言快速入门及数据挖掘
回复 Kaggle案例 Kaggle十大案例精讲(连载中)
回复 文本挖掘 手把手教你做文本挖掘
回复 可视化 R语言可视化在商务场景中的应用
回复 大数据 大数据系列免费视频教程
回复 量化投资 张丹教你如何用R语言量化投资
回复 用户画像 京东大数据,揭秘用户画像
回复 数据挖掘 常用数据挖掘算法原理解释与应用
回复 机器学习 人工智能系列之机器学习与实践
回复 爬虫 R语言爬虫实战案例分享