因为书里关于代码的解释部分一直都写得比较详细, 但是到了这一小段后突然说得很省略, 并且引用了几个之前未使用过的函数, 例如zip(), *zip(), lambda, groupby等. 自己也是花了不少时间才把这个代码搞懂, 故记录下来, 既是加深记忆, 也希望能帮到同样有疑惑的同学.
这里面主要是zip()和*zip()函数比较让人费解, 为了避免重复, 大家可先看我的这篇文章, 详细了解这两个函数(其实是同一个函数), 然后再看代码就比较容易懂了.
Python中的zip(), zip()与zip(zip(a,b))
接下来先上代码:
from itertools import groupby
def draw_line(x_data, y_data, title, y_legend):
xy_map = []
for x, y in groupby(sorted(zip(x_data, y_data)), key=lambda _:_[0]):
y_list = [v for _, v in y]
xy_map.append([x, sum(y_list) / len(y_list)])
x_unique, y_mean = [*zip(*xy_map)]
line_chart = pygal.Line()
line_chart.title = title
line_chart.x_labels = x_unique
line_chart.add(y_lengend, y_mean)
line_chart.render_to_file(title+'.svg')
return line_chart
逐行解释:
第1行: 引入迭代工具groupby, 从itertools中引出出来的函数都是迭代器, 想获得其中的数据, 需要用for循环或者其余访问方式获取, 所以下面的draw_line中, 使用了for循环获取其中的数据.
第3行: 定义了draw_line方法, 其中4个参数分别为, 月份, 该月收盘价, 图表标题, 图例. 因为该表用到的数据是若干年的数据, 所以每个月会有不定数量的该月数据, 如10个3月的数据.
第4行: 创建了一个空列表
第5~7行: 重中之重, 下面按嵌套顺序解释:
zip(x_data, y_data): 把月份和该月的收盘价结合成一个二元元素(一个含有2个值的元素). 例如: x_data = [1,2,3,1], y_data =[1200,1400,1300,1450]. 由于在数据文件中, 他们的元素序列都是一一对应的, 使用zip后, 就变成了(1,1200), (2,1400), (3,1300), (1,1450). (假设比较小的数字便于讲解, 真实文件里的数据不为如此)
sorted的算法是先根据第一个元素进行排序, 第一个元素相同则根据第二个元素排序, 所以排列后的数字为(1,1200), (1,1450), (2,1400), (3,1300). sorted后的数据变成了按照月份的升序排列, 把他称为sorted_object
接下来进行groupby分组函数, 分组的依据由key决定, 而key则使用了lambda函数, lambda是个匿名函数, 其相当于
def functionA(_):
return _[0] # 也就是无论你给他什么参数, 他都给你返还这个参数索引为0的数.**
而且, key具体函数的参数取自于可迭代对象中,指定可迭代对象中的一个元素进行排序. 也就是把可迭代对象sorted_object作为参数, 传递给lambda函数, 然后进行分组, 分组的依据为第0号索引的元素, 即元素中的第一个数, 如(1,1200)中的1.
排序后进行分组, 其数据形式会变成这样: (1, ((1,1200),(1, 1450))), (2, (2,1400)), (3, (3,1300)), 也就是元素第一个数是组号, 第二个数是多个二元元素. 所以此时for循环里x代表的是组号, y代表的是后面的多个二元元素集合
代码举例:
A = (1,400), (3,600), (5,700), (1,900), (1,500), (3,800)
print(*groupby(sorted(A),key=lambda _:_[0]))
for x,y in groupby(sorted(A),key=lambda _:_[0]):
for x1, y1 in y:
print(x1,y1)
---
输出结果:
(1, <itertools._grouper object at 0x01D704F0>) (3, <itertools._grouper object at 0x01E6D0E8>) (5, <itertools._grouper object at 0x01E6D268>)
1 400
1 500
1 900
3 600
3 800
5 700
所以图形代码第6行里, [v for _,v in y]中, (_和v)是一个整体,分别代表第一个数和第二个数, 但是y_list只取第二个数收盘价用于运算, 使用列表解析取出了同一个月的所有收盘价数据
第7行: sum(y_list) / len(y_list), 总和除以数据个数求出均值, 再和x组合成元素, 此时的x虽是组号, 但是因为我们把1月分成第一组, 2月分成第二组, 所以该组号也是月份数, 于是xy_map列表由各月月数和该月平均收盘价组成. 如:[(1, 1203), (2, 1340), (3, 1245)]等
第8行: [*zip(*xy_map)], 逐层来看, *xy_map, 把上面的[(1, 1203), (2, 1340), (3, 1245)]变成了(1, 1203), (2, 1340), (3, 1245)的多个二元元素, *号的作用就是把列表的[ ]去掉了(详看开头的第一篇文章), 然后对这些元素进行一次zip()操作, 就会得到(1, 2, 3), (1203, 1340, 1245), 但是由于此时的zip是迭代对象, 他的值不能直接取出, 所以需要再在前面加一个*号, 并且用[ ]将其包裹起来, 让两个多元元素分别作为列表的0号和1号元素赋给x_unique和y_mean. 此时x_unique是月份数, y_mean是各月的收盘均值, 下面就可以进行绘图了
第9~14行: 都是比较简单的代码, 大略提一下, 分别为: 创建折线图, 添加标题, 设置x轴刻度, 添加折线图数据, 保存图像, 将折线图作为方法返回值.
考虑到学这本书的应该都是和我一样刚接触python的新手, 所以写得比较详细. 希望能帮助到大家, 如果我的文章中有什么错误或者不准确的地方, 也欢迎大家勘误, 大家共同学习.