我的生活统计:我追踪了一年的习惯,这是我学到的东西

我的生活纪实:一年来跟踪习惯,我学到的奇妙秘诀

我连续332天测量了我在日常活动中所花费的时间(学习、做运动、社交、睡觉…)

为什么?为什么我要这样做?

这可能是我一生中做过的最长、最耗时的实验。此外,它在科学上几乎没有意义——样本只有一个人,而且高度主观(完全依赖于我的记忆和时间感知)。

那为什么要这样做呢?例行公事,就像任何其他自我问责的方法一样,以许多不同的方式帮助我。我在生活中的低谷时开始了这个实验,试图研究自己以及不同的习惯如何影响我的情绪和心理健康。目的是能够“黑客入侵”自己的大脑:如果我知道——从统计上——什么让我长期快乐和健康(以及相反的情况!),我就能改善自己的生活,可能给那些经历困境的人提供建议或帮助。

为什么这对你有意义?

我认为这种自省的练习是数据科学可以应用于任何事物的一个很好的例子。当然,它不必是这种类型的追踪和日志记录。你可以研究生活中任何你认为有价值的事物:跟踪你宠物的行为,你所在城镇的天气,当地公共交通系统的延误率… 有很多个人分析可以进行:只要有数据集,你就可以研究它!幸运的是,数据无处不在——你只需要找对位置并追踪它。

方法——我做了什么,以及如何做到的?

我每天留出一些时间来做个人笔记,记录我所做的事情,并追踪不同活动和类别所花费的时间(以小时为单位)。

我测量的变量在这一年中有些变化:有一些新的出现,有些消失,还有一些合并在一起。最后的变量,也是我所有时间记录中有数据的变量,包括:睡眠、写作、学习、运动、音乐、卫生、语言、阅读、社交和心情——总共十个变量,涵盖我认为是我生活中最重要的方面。

数据的初步探索

我首先查看了四个变量的个体时间序列:睡眠、学习、社交和心情。我使用Microsoft Excel快速绘制了一些图表。它们表示每天所花时间的数量(蓝色)和五天移动平均值(红色),我认为这是一个适合我情况的很好的度量标准。心情变量从10(最高!)到0(糟糕!)进行评分。

关于每个图表中包含的数据脚注:总和是系列值的总和,平均值是系列的算术平均值,STD是标准差,相对偏差是标准差除以平均值。

总和:2361小时。平均值:7.1小时。标准差:1.1小时。相对偏差:15.5%(图片由作者提供)

综上所述,我在睡眠方面表现得不错。我也有艰难的日子,就像其他人一样,但我认为趋势相当稳定。实际上,这是我研究中变化最小的一个方面。

总和:589.1小时。平均值:1.8小时。标准差:2.2。相对偏差:122%(图片由作者提供)

这些是我为我的学术生涯投入的时间。它经常波动很大,找到工作和学习之间的平衡通常意味着必须在周末加班,但是我仍然对此感到满意。

总计:1440.9小时。平均值:4.3小时。标准差:4.7小时。相对偏差:107%(作者提供的图片)

关于这个表格,我只能说我感到惊讶。总计大于我预期,考虑到我是一个内向的人。当然,与同事们一起在学校度过的时间也应该算在内。在变化性方面,标准差非常高,这是有道理的,因为与社交活动相关的日常例行工作非常困难。

平均值:8.0小时。标准差:0.9小时。相对偏差:11.3%(作者提供的图片)

这是最不变化的系列之一 —— 相对偏差在我研究的变量中最低。从先验来看,我对观察到的趋势感到满意。我认为保持一个相当稳定的情绪是积极的 —— 如果是良好的情绪,则更好。

相关研究

在查看主要变量的趋势后,我决定深入研究它们之间的潜在相关性²。由于我的目标是能够对“心情”进行数学建模和预测(或至少解释),所以相关性是一个重要的指标需要考虑。借助相关性,我可以提取出一些关系,比如:“我学习最多的那些天是我睡眠最少的时候”,“我通常同时学习语言和音乐”,等等。

在我们做任何其他事情之前,让我们打开一个Python文件并从时序分析中导入一些关键库。我通常为它们使用别名,因为这是一种常见的做法,可以在实际代码中减少冗长。

import pandas as pd               #1.4.4import numpy as np                #1.22.4import seaborn as sns             #0.12.0import matplotlib.pyplot as plt   #3.5.2from pmdarima import arima        #2.0.4

我们将进行两种不同的相关性研究。我们将研究Person相关系数³(用于变量之间的线性关系)和Spearman相关系数⁴(研究变量之间的单调关系)。我们将使用pandas中的相关实现⁵。

Pearson相关矩阵

两个变量XY之间的Person相关系数计算如下:

其中cov为协方差,sigma X为std(X),sigma Y为std(Y)

我们可以快速计算一个相关矩阵,其中计算了每对变量之间的可能相关性。

#读取、选择和归一化数据raw = pd.read_csv("final_stats.csv", sep=";")numerics = raw.select_dtypes('number')#计算相关矩阵corr = numerics.corr(method='pearson')#生成热力图sns.heatmap(corr, annot=True)#绘制图形plt.show()

这是从我的数据中获得的原始Pearson相关矩阵。

我的变量的Pearson相关矩阵(作者提供的图片)

而这些是显著的值⁶,也就是以95%的置信度与零不同的值。我们使用以下公式进行 t-检验⁷。对于每个相关值 rho,如果满足以下条件,我们就会丢弃它:

其中 n 是样本大小。我们可以重复使用之前的代码,并添加这个过滤器。

#常数N=332 #样本数STEST = 2/np.sqrt(N)def significance_pearson(val):    if np.abs(val)<STEST:        return True    return False#读取数据raw = pd.read_csv("final_stats.csv", sep=";")numerics = raw.select_dtypes('number')#计算相关性corr = numerics.corr(method='pearson')#准备掩码mask = corr.copy().applymap(significance_pearson)mask2 = np.triu(np.ones_like(corr, dtype=bool)) #移除上三角mask_comb = np.logical_or(mask, mask2)c = sns.heatmap(corr, annot=True, mask=mask_comb)c.set_xticklabels(c.get_xticklabels(), rotation=-45)plt.show()

那些被丢弃的值可能只是噪音,并错误地表示趋势或关系。无论如何,假设一个真实关系是无意义的,总比考虑一个没有意义的关系更好(我们指的是错误类型 II 被优先于错误类型 I)。在主观测量相对较多的研究中尤其如此。

筛选后的 Pearson 相关矩阵。已经筛除了非显著值(以及上三角)。 (作者提供的图像)

斯皮尔曼相关系数

斯皮尔曼相关系数可以计算如下:

其中 R 表示排名变量⁸ — 其余变量与 Pearson 系数描述的一样。

与之前一样,我们可以快速计算相关矩阵:

#读取、选择和归一化数据raw = pd.read_csv("final_stats.csv", sep=";")numerics = raw.select_dtypes('number')#计算相关矩阵corr = numerics.corr(method='spearman') #注意这个改变!#生成热力图sns.heatmap(corr, annot=True)#绘制图形plt.show()

这是我从我的数据中获取的原始斯皮尔曼等级相关矩阵:

我变量的斯皮尔曼相关矩阵(作者提供的图像)

让我们看看哪些值实际上是显著的。检查显著性的公式如下:

其中 r 是斯皮尔曼系数。这里,t 遵循自由度为 n-2 的 t-学生分布。

在这里,我们将过滤掉所有绝对值大于1.96的 t 值。同样,我们丢弃的原因是我们不能确定它们是噪音(随机机会)还是实际趋势。让我们来编写代码:

#constantsN=332 #样本数量TTEST = 1.96def significance_spearman(val):    if val==1:        return True    t = val * np.sqrt((N-2)/(1-val*val))        if np.abs(t)<1.96:        return True    return False#读取数据raw = pd.read_csv("final_stats.csv", sep=";")numerics = raw.select_dtypes('number')#计算相关性corr = numerics.corr(method='spearman')#准备掩码mask = corr.copy().applymap(significance_spearman)mask2 = np.triu(np.ones_like(corr, dtype=bool)) #删除上三角形mask_comb = np.logical_or(mask, mask2)#绘制结果c = sns.heatmap(corr, annot=True, mask=mask_comb)c.set_xticklabels(c.get_xticklabels(), rotation=-45)plt.show()

这些是显著值。

具有显著值的相关矩阵(作者提供的图片)

我认为这张图更好地解释了变量之间的表面关系,因为它的标准更“自然”(它考虑了单调⁹关系,而不仅仅是线性关系)。它不像另一个图那样受到异常值的影响(与某个变量相关的一些非常糟糕的日子不会影响整体的相关系数)。

不过,我会将两张图都留给读者来判断和得出自己的结论。

时间序列研究-ARIMA模型

我们可以将这些数据视为时间序列。当解释变量时,时间可能是一个重要因素:其中一些变量可能定期波动,甚至存在自相关¹⁰关系。例如,一晚上睡不好可能会让我感到困倦,并导致我第二天睡过头-那将是一种时间上的相关。在本节中,我将只关注最初探索的变量。

让我们探索ARIMA模型并找到适合我们数据的好模型。ARIMA¹¹模型是自回归模型(AR¹²)和移动平均的组合,因此称为自回归综合移动平均模型(Auto Regressive Integrated Moving Average)。在这种情况下,我们将使用pmdarimaauto_arima方法,这是受到R语言“forecast::autoarima”函数启发的函数,用于确定我们模型的系数。

for v in ['Sleep','Studying','Socializing','Mood']:    arima.auto_arima(numerics[v], trace=True) #trace=True 以查看结果

结果已总结在以下表格中:

ARIMA(p,d,q)模型及其相应的AIC(作者提供的图片)

令人惊讶的是,睡眠不是自回归的,但情绪似乎是!正如我们所见,一个简单的ARIMA(1,0,0) -一个AR(1) -相当好地表示了情绪。这意味着D天的情绪由D-1天的情绪解释,或前一天的情绪,和一些符合正态分布的噪声。

尽管看起来很小,但这个结果已经足够有趣。学习也是自回归的,但遵循ARIMA(1,0,2) -这意味着它不直接遵循趋势,但它的移动平均值确实遵循趋势。然而,这个模型的AIC¹³要高得多,所以模型可能过于复杂化了观察行为的解释。

FFT — 快速傅里叶变换

我们可以使用离散傅里叶变换¹⁴来分析我们的数据。通过这个方法,我们可以发现与季节性相关的任何模式。傅里叶变换是一种数据变换操作,可以将一系列分解为其基本组成部分。您可以通过下面的图片更好地理解:

傅里叶变换的过程(作者提供的图像)

下面是另一个例子:我们有一个由两个正弦函数组成的信号,频率分别为1和10。在应用傅里叶变换后,我们可以看到:

正如我们所看到的,快速傅里叶变换将信号分解为频率分量(来自维基百科共享资源的图像)

结果是一个具有两个峰值的图形,一个位于x=1处,另一个位于x=10处。傅里叶变换已经找到了我们信号的基本组成部分!

让我们将其转换为代码:

for v in ['Sleep','Studying','Socializing','Mood']:    t = np.arange(0,N,1)    x = numerics[v]    X = np.fft.fft(x)    n = np.arange(0,len(X),1)    T = N    freq = n/T     plt.figure(figsize = (8, 4))    plt.subplot(121)    plt.plot(t, x, 'r')    plt.xlabel('时间(天)')    plt.ylabel(v)    plt.subplot(122)    plt.stem(n, np.abs(X), 'b', markerfmt=" ", basefmt="-b")    plt.xlabel('频率(1/天)')    plt.ylabel('FFT |X(freq)|')    plt.xlim(0, 30)    plt.ylim(0, 500)    plt.tight_layout()    plt.show()

回到我们的案例研究中,这是我们的代码输出的结果:

从左到右,从上到下:Sleep,Studying,Socializing和Mood的图表(作者提供的图像)

我们可以观察到Sleep在频率1处有一个显著值-这意味着数据遵循一种1天的循环,这并没有太大的帮助。Studying也显示出了一些有趣的值:前五个或者更多的值明显高于其他值。不幸的是,噪音占据了它们以及每个其他图表的主导地位-无法确信地得出结论。

为了消除噪音,我们使用移动平均法进行滤波。我们再次尝试应用MA(5)并研究FFT。除了移动平均法,代码几乎相同。

def moving_average(x, w):    return np.convolve(x, np.ones(w), 'valid') / wk = 5for v in ['Sleep','Studying','Socializing','Mood']:    t = np.arange(0,N-k+1,1)    x = moving_average(numerics[v], k)    X = np.fft.fft(x)    n = np.arange(0,len(X),1)    T = N-k+1    freq = n/T    plt.figure(figsize = (8, 4))    plt.subplot(121)    plt.plot(t, x, 'r')    plt.xlabel('时间(天)')    plt.ylabel(v)    plt.subplot(122)    plt.stem(n, np.abs(X), 'b', markerfmt=" ", basefmt="-b")    plt.xlabel('频率(1/天)')    plt.ylabel('FFT |X(freq)|')    plt.xlim(0, 30)    plt.ylim(0, 500)    plt.tight_layout()    plt.show()

这些是我们的代码生成的图表:

从左到右,从上到下:Sleep,Studying,Socializing和Mood的图表(作者提供的图像)

应用MA(Moving Average)后,噪声有所减少。然而,似乎从中得不出任何结论 —— 我们无法找到任何显著的、清晰的频率值。

结论

通过进行不同的统计研究,我们可以得出预期的结论:人类行为非常复杂 —— 当然比Excel表格和几个数学模型所能解释的要复杂得多。然而,无论是方法性数据收集还是从中产生的分析机会,都有其价值。让我们快速回顾一下我们所做的:

  • 原始数据和趋势线概述。
  • Pearson和Spearman相关分析和显著性测试。
  • ARIMA模型拟合。
  • 快速/离散傅里叶变换分解。

通过进行这些分析,我们能够对我们的数据及不同变量之间的相关性有一些洞见。以下是我们发现的摘要:

  • 在相对偏差(变异性)方面,心情和睡眠分别是最低的(分别为11.3%、15.5%),而学习和社交都超过了100%。
  • 社交与几乎所有的爱好都呈负相关,但与我的心情呈正相关(无论是在Pearson相关性还是Spearman相关性方面)。这可能是因为当我与朋友或家人见面时,我必须把我的爱好放在一边,但整体上我比独自一人时更快乐。
  • 心情与写作呈负相关(Spearman),这可能是因为我有时会通过短篇小说或写日记来抱怨我的问题。
  • 通过ARIMA拟合研究发现,心情和学习呈自回归趋势,这意味着特定一天的值可以由前一天的值来解释。
  • 通过离散傅里叶变换找不到明确的分解,尽管某些频率组可能会高于其他频率。

值得注意的是,我们得到了有趣的“全局”统计数据,虽然它们可能不具备科学意义,但了解它们还是有趣的。

从个人角度来看,我认为这个实验对我非常有帮助。即使最终结果并不具有决定性,我相信它帮助我应对了困难时期,并跟踪了美好时期。同样地,我认为进行一些自省,更好地了解自己,这总是积极的。

最后,这是一个累积图表 —— 再次由MS Excel制作 —— 展示了所有可累积的变量(除了心情和卫生,它们不是以小时计算的,而是以某个特定的排名计算;以及睡眠)。我决定将其绘制为对数图表,因为即使累积变量是线性的,它们的变化斜率使观察者很难看到数据。就是这样!享受吧!

每个系列的累积总和,对数Y轴。(作者提供的图片)

同样,请务必留下您可能有的任何想法或疑问的评论。

代码和数据在我的GitHub上。

GitHub – Nerocraft4/habittracker

通过创建GitHub帐号贡献给Nerocraft4/habittracker。

github.com

参考资料

[1] 维基百科。移动平均。 https://en.wikipedia.org/wiki/Moving_average

[2] 维基百科。相关性。 https://en.wikipedia.org/wiki/Correlation

[3] 维基百科。皮尔逊相关系数。 https://en.wikipedia.org/wiki/Pearson_correlation_coefficient

[4] 维基百科。斯皮尔曼等级相关系数。 https://en.wikipedia.org/wiki/Spearman%27s_rank_correlation_coefficient

[5] Pandas文档。pandas.DataFrame.corr。 https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.corr.html

[6] 维基百科。统计显著性。 https://en.wikipedia.org/wiki/Statistical_significance

[7] 维基百科。学生t检验。 https://en.wikipedia.org/wiki/Student%27s_t-test

[8] 维基百科。等级相关。 https://en.wikipedia.org/wiki/Rank_correlation

[9] Wolfram MathWorld。单调函数。 https://mathworld.wolfram.com/MonotonicFunction.html

[10] 维基百科。自相关。 https://en.wikipedia.org/wiki/Autocorrelation

[11] 维基百科。自回归移动平均。 https://en.wikipedia.org/wiki/Autoregressive_integrated_moving_average

[12] 维基百科。自回归模型。 https://en.wikipedia.org/wiki/Autoregressive_model

[13] Science Direct. 赤池信息准则。 https://www.sciencedirect.com/topics/social-sciences/akaike-information-criterion

[14] 维基百科。离散傅立叶变换。 https://en.wikipedia.org/wiki/Discrete_Fourier_transform