欲直接下载代码文件,关注我们的公众号哦!查看历史消息即可!
0. 前言
在机器学习里,我们对时间序列数据做预处理的时候,经常会碰到一个问题:有多个时间序列存在多个表里,每个表的的时间轴不完全相同,要如何把这些表在时间轴上进行对齐,从而合并成一个表呢?尤其是当这些表都存在数据库里,而且超级超级大的时候,怎样才能更高效地处理呢?
在上一篇文章中,已经介绍过了如何在Python中创建数据库连接以及对数据库进行增删改查、分组聚合以及批量读取和处理等操作。
今天就以上面的问题为导向,手把手教你如何用Python一步步实现相应的功能。讲解的内容主要有:
-
如何实现两个有序序列的合并;
-
延伸到两个时间序列数据的对齐;
-
从数据库中自动循环分批读取数据。
需要掌握的主要编程技巧包括:
-
用函数实现特定功能
-
用类对功能进行封装
-
实现基本的迭代器
使用的工具及版本:Python3.7,MySQL8.0, Jupyter Notebook
1. 有序序列的合并
本节主要介绍如何实现将2个有序(默认从小到大排序)序列合并成一个序列,同时介绍Python中基本的循环结构。
其实在Python中固然有相应的方法可以很容易地做到(例如集合的set.union()方法),这里之所以要自己实现,主要是要理解这种思想,为后文的功能实现做铺垫。
1.1 Python知识点之条件测试
if 语句的语法结构为:
if boolean_expression1: #如果满足条件1,则执行suite1代码块
suite1
elif boolean_expression2: #如果满足条件2,则执行suite2代码块
suite2
else: #否则执行else_suite代码块
else_suite
其中elif
和else
为可选。
1.2 Python知识点之循环控制
1.2.1 while循环
(1) 循环机制及应用场景
-
用于编写通用迭代结构
-
顶端测试为真时执行循环体,并会重复多次测试直到为假后结束循环
(2) 语法格式
while boolean_expression: #如果测试为真,则执行while_suite代码块(循环执行)
while_suite
else: #直到测试为假,则执行一遍else_suite代码块之后结束循环
else_suit
其中else
为可选。
1.2.2 for 循环
(1)循环机制及应用场景
-
通用的序列迭代器,用于遍历任何有序的序列对象内的元素
-
可用于字符串、元组、列表和其它的内置可迭代对象,以及通过类所创建的新对象
(2)语法格式
for expression in iterable:
for_suite
else:
else_suite
其中else
为可选。
?tips1: for循环比while循环执行速度快的多,能用for的尽量使用for
1.3 Python知识点之函数
函数是python为了代码最大程度地重复利用和最小化冗余而提供的基本程序结构。
它能够将整块代码巧妙地隔离成易管理的一小块,把重复代码放在函数中,而不是进行大块的复制,这是一个程序员应该具备的基本技能。
1.3.1 创建函数
使用def
语句定义函数,并且函数都会有一个返回值,默认为None,也可以用return
语句明确指定返回值。
语法格式:
def funtionName(parameters): #定义函数名,设置函数的参数
suite #函数体
return something
1.3.2 调用函数
在Python中,函数是一个可调用对象,它有一个内置的方法,叫call
。
我们在写程序的时候,会碰到一类错误:"xxx" object is not callable
,这就表示这个对象是不可调用的。
调用函数的方法也很简单,在函数名后面加小括号(),有参数的时候在括号中传入参数即可:funtionName(par1,..)
?tips2:python中定义函数名的时候,通常第一个单词均小写,第二个单词开始通常首字母大写,例如,printName,calculateSum
?tips3:写函数的时候,尽量写得简单,功能尽可能单一,不要写得又长又复杂
1.4 手动实现有序序列的合并
注:在 Python 中,list(列表)是最常用、最核心的数据结构之一,它是一种序列类型,可以接收各种类型的元素,也可以同时接收不同类型的元素。此外,list 还是一个可迭代对象。本文的演示多采用 list 结构组织数据。
- 解题思路
假设有两个序列:a = [1,3,7,9,11], b = [3,4,7,8],怎么合并成一个序列?
思路:用第3个序列 c 记录结果,同时对 a、b 进行遍历,按一定的顺序依次将 a、b 中的元素添加到 c 中;遍历的方法是用指针进行索引。
-
a[0]=1, b[0]=3, a[0]<b[0]
→ 将 a[0] 添加到 c ,idx_a=idx_a+1
→ 此时 c=[1], idx_a=1, idx_b=0 -
a[1]=3, b[0]=3, a[0]=b[0]
→ 将 a[1]或b[0] 添加到 c ,idx_a=idx_a+1, idx_b=idx_b+1
→ 此时 c=[1,3], idx_a=2, idx_b=1; -
a[2]=7, b[1]=4, a[0]>b[1]
→ 将b[1] 添加到 c , idx_b=idx_b+1
→ 此时 c=[1,3,4], idx_a=2, idx_b=2; -
a[2]=7, b[2]=7, a[2]=b[2]
→ 将a[2]或b[2] 添加到 c , idx_a=idx_a+1, idx_b=idx_b+1
→ 此时 c=[1,3,4,7], idx_a=3, idx_b=3; -
a[3]=9, b[3]=8, a[3]>b[3]
→ 将b[3] 添加到 c , idx_b=idx_b+1
→ 此时 c=[1,3,4,7,8], idx_a=3, idx_b=4; -
idx_b=4超出了b的索引范围,及idx_b=len(b),但此时idx_a<len(a),所以将 a[idx_a:] 直接添加到 c
→ 此时c=[1,3,4,7,8,9,11],结束,输出结果c 。
- 实现代码
def orderedListUnion(a, b):
'''
合并两个按从小到大排好序的序列a,b
'''
# 设置循环初始值
idx_a = 0
idx_b = 0
c = []
# 声明变量len_a,len_b,指向序列a,b的长度,用来控制循环条件
len_a = len(a)
len_b = len(b)
while (idx_a < len_a) and (idx_b < len_b):
#若两个元素相等,则将该元素添加到c,且两个idx同时右移:
if a[idx_a] == b[idx_b]:
c.append(a[idx_a])
idx_a += 1
idx_b += 1
#若不相等,取较小的元素,且较小元素的idx右移
elif a[idx_a] < b[idx_b]:
c.append(a[idx_a])
idx_a += 1
else:
c.append(b[idx_b])
idx_b += 1
# 当一个序列遍历结束后,跳出循环,将未遍历完的序列的剩余元素添加到c
if idx_a == len_a:
c = c + b[idx_b:]
if idx_b == len_b:
c = c + a[idx_a:]
return c
# 测试
a = [1,3,7,9,11]
b = [3,4,7,8]
print(orderedListUnion(a,b))
输出结果:
2、时间序列的对齐
2.1 问题场景
前面的练习仅仅作为热身,现在回到文章开头的问题,假设一个更具体场景:
在医院的ICU里,需要持续观察病人的各项生命指标。这些指标的采集频率往往是不同的(例如有些指标隔几秒采集一个,有些几个小时采集一个,有些一天采集一个),而且有些是定期的,有些是不定期的,或者由于某些原因某些指标在某段时间上是缺失的,所以不同生命指标的时间序列数据在时间轴上的表现往往是不对齐的。
所以现在的问题是:
如何将存储在不同数据表里,且时间轴不同的两个时间序列进行合并,对齐到同一个时间轴上?
举例说明:
假设现在有2个数据表,分别记录了某个病人某一天当中某些时刻的一些生命体征指标:
表1: