恋爱 5 年的消息看起来是什么样**

386 阅读9分钟

恋爱 5 年的消息看起来是什么样

或者,用 Python 来处理时间序列数据(还有表情)的一些很酷的方式 c9RbqJ.jpg出自: Pexels (图中不是我女朋友)

在政府实施封闭政策后的第三周,我的女朋友随口一说:“我打赌我们发送的消息是有史以来最少的……” 作为一个被困在家中的数据分析科学家,这是我不能忽视的评论。所以我打开了 Telegram 的桌面程序,然后下载我们所有的消息记录。消息记录是以 HTML 文件的形式导出的,每个文件包含 500 条消息数据(一共有 160 个这样的文件)。幸运的是,我们可以使用一般的网络抓取技术 - 特别是类似 Beautifulsoup4 的方法 - 能够自动完成数据框的搭建,其中每一行代表一条消息。

c9Rf5q.png

(我忘记我们当时讨论的是哪个美国人了……)

注意——Timestamp 这列是关键的一列。在我们稍后使用的 Pandas 的方法中,这是重要的 ‘datetime’ 类型,而不是简单的字符串。用 Pandas 自带的 to_datetime 方法易于实现时间格式的转换:

df[‘Timestamp’] = pd.to_datetime(df[‘Timestamp’], dayfirst=True)
df[‘Timestamp’] = pd.to_datetime(df[‘Timestamp’], dayfirst=True)

Telegram 导出的HTML文件用dd/mm/yyyy 的格式给出时间戳。 因此,我们将 ‘dayfirst’ 参数传入,以确保程序按这种格式读取字符串(即,5/4/2020 应该读作 4 月 5 日,而不是 5 月 4 日)。 那么接下类我们能够用这个数据集做谢什么呢?我们当然可以尝试一些 NLP 的工作(这可能是我未来一个博客的主题——关于我们在一起的时间里,我们的词汇是如何汇聚到一起的,我有一些假设......)现在,我们简单地思考下如何可视化我们之间的时间序列的数据,我们可以简单地考虑如何来可视化我们之间的时间序列数据,以及如何最好地呈现这种可视化效果,便于观点的提取。


我们首先通过创建每天发送的消息的折线图了解下我们的消息数量在过去的五年是怎样变化的。Pandas 自带的一个方法可通过给定的时间间隔聚合数据(例如,获取每天的消息数)。

df.set_index('Timestamp').groupby(pd.Grouper(freq ='D')).count()
df.set_index('Timestamp').groupby(pd.Grouper(freq ='D')).count()

这儿有几种链式方法——让我们拆开来看:

  • .set_index(‘Timestamp’) 首先, 我们需要日期时间的特征作为索引。
  • .groupby(pd.Grouper()) 这个操作类似 标准的 pandas 分组, 但它并不是对列中的唯一元素创建索引,而是对样本中“最小”日期和“最大”日期之间的天数创建索引。重要的是,这还会创建数据集中未考虑的行(例如,一些天中我们没有发送消息)。
  • freq=‘D’** 这个频率不一定是‘天’ —— 我们可以设置成想要的任何时间间隔(M 代表月等)**
  • .count()** 类似一个标准的分组操作,我们需要调用一个聚合方法来创建实际的数据框。** 所以用标准折线图中呈现是什么样子呢? c9R5GV.png

大多数时候是一团糟。 如果我们用每天总字数来替代每天消息数,情况并不会有任何改善(也就是我们的数据并没有因为我把句子中的每个子句作为单独的信息发送而受到影响)。 c9R4P0.png

然而,这儿一些东西是我们可以从这些图表得到的:

  • 我们每日发送消息的上限通常保持上限在大约 150 条消息,或是 600 字;
  • 在我们彼此分开的两个圣诞节中我们发了很多消息(两个峰值分别在 2015 年和 2016 年),而不是我们一起过的圣诞节(2017, 2018 和 2019)
  • 我们互相发送消息的总量并不像是随着时间增长变化了很多 让我们更深入地探究最后一点。图表显示,即使在过去的五年里,每天的消息量有一个非常稳定的上限,但是每天都在剧烈波动(我们今天互发 500 字,明天就不发消息了)。 我们可以通过使用移动平均值来整理下 —— 换句话说,以过去 x 天的平均值作为某一天的数值。这就表明我们上面锯齿状的图,在理论上,应当是平滑的(特别是当我们增加窗口宽度 x 时).这应该揭示了任何潜在的趋势。 Pandas 对此有一个便利的方法:
df[‘WordCount’].rolling(window = x).mean()
  • 我们可以改变x设置窗口宽度
  • 我们不需要使用平均 —— 可以滚动求和等。
  • 这将创建一个与原始序列相同长度的新序列,但是第一个x-1元素是NaN值 现在让我们看看每天交换的总字数,划分发送人,依次增加窗口宽度(1 个星期,4 个星期和 8 个星期)。同时加上两条红色的垂线来表示下面的两个时间点:
  1. 我们同居时(2017 年 4 月)
  2. 我们搬到城市,我开始在家里学习时(2019 年 5 月) c9RI2T.png

c9RoxU.png

c9R7MF.png

56天的移动平均开始让一些事情变得更清楚了:

  • 在我们同居之前(第一条红线的左边)我们每天发送更多的文字
  • 我的女朋友每天发送的文字一直比我发送的多
  • 然而,当我们搬到城市并且她开始忙于一个客户要求很高的项目时,她的消息发送量降到和我一样的水平了 ——她没有更多的时间去发送正常水平的消息量,但是当我发送信息时,她显然还是足够体贴地回复了
  • 对时间序列最右边的封锁确实导致了每天互发消息量的急速减少……

当然,现代消息不仅仅只是你发送的文字(这儿有个缘由 2015 年牛津词典年度词汇是一个表情符号)。令人惊讶的是,Telegram HTML 的初始版本实际保留了最原始的表情符号。

c9RHr4.png

原始的 Python 并不支持这些字符,然而有一个小的第三方库(可以称为“ emoji”)支持处理识别、计算和解码字符串中的表情符号。 如果我们研究“非文本”消息,可以发现表情符号对于我女朋友是一种非常重要的沟通方式。另一方面,我通过分享照片、链接(通常是 Reddit 的)和[贴纸]。

c9RLZ9.png(Telegram 较早推出的一个功能)来增加消息量。

我们可以尝试用同样的移动平均的技巧弄清这些非文本消息量是如何随着时间变化的。然而,除了 2018 年早期使用贴纸出现的高峰(当我们发现可以下载自定义的集合时——可真是一个转折性的时刻),并没有很多可辨别的模式。

c9ROaR.png

相反,让我们创建一个图,按类型显示这些非文本消息的累积情况。为此,我们可以用 NumPy 的 cumsum() 方法,这个方法将会计算一个序列的累加和。假设我们有一个数据框,其索引是一个日期范围,并且每列描述了那天发送的每种消息类型的数量:

#创建一个消息类型的列表
required_cols = ['Emoji','Photo','Sticker','Link','Voice message','Animation']

#为这些类型创建一个新的数据框架
df_types_cum = df_types[required_cols]

#通过迭代每列
#并用累加和替代数值
for i in required_cols:
    df_types_cum[i] = np.cumsum(df_types_cum[i])
    
#使用Pandas自带的绘图方法显示
df_types_cum.plot.area(figsize=(15,6), lw=0)

这就生成了下面的图表。 c9RXI1.png

我们可以再次看见贴纸(琥珀色部分)的引进和使用,也可以看到自 2018 年起表情符号的使用增加。


我们前面的分析没有涉及到的是一天里我们互发消息(只是每天的数量)的时间。这种情况的可视化可能很快就会变得非常混乱。一天已经有 24 小时了 —— 如果我们还想知道基于其他标准(例如,一周中的哪一天、一年中的哪一天等)是否会存在差异,那我们需要在一个图里传递大量的可视化数据。 热图让我们能够做得相当整洁。在这里,我已经创建了一个数据框,它显示了我们开始同居前后每天每小时发送的文字总量(利用 Pandas 的 pivot_tabl e方法):

df_times = pd.pivot_table(df,fill_value=0,
               index=['LivingTogether','Day'],
               columns='Hour',
               aggfunc='sum')['WordCount'].T
               
#注意 —— 代码结尾的 .T 转置了数据帧

然后,通过将数据帧的两部分除以同居前后适当的周数,我可以得到每小时的平均单词量。接下来,我们就可以使用 Seaborn 的热图方法可视化数据帧了。

sns.heatmap(df_times)

c9RvPx.png

在加上一些垂直线和水平线划分工作时间和周末后,我们可以轻松地从热图得到视觉信息。在同居之前,我们一整天都在发消息,尤其在睡前(周五和周六的灰白色正方形,表明这些夜晚我们更可能在对方的住处过夜)。 在同居后,可以看到我们发消息受到了工作时间的限制。有趣的是,看起来一天中这个时间段的消息总量实际上增加了。也可以发现我们的睡眠时间在这段时间显著增加了 —— 发信息到凌晨 1 点的日子早已一去不复返了。

感谢阅读完了这则博客!我很乐意听取关于上述分析的任何意见,或者是这则博客涉及的任何概念。请在下方留言,或者通过领英联系我。

如果发现译文存在错误或其他需要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 本文永久链接 即为本文在 GitHub 上的 MarkDown 链接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏