迭代器与生成器 (03)

项目快延期了, 一个人干一个项目, 真的是有点难受, 好在大头的数据处理宽表已经 用 sql 拼接出来了, 写了5个大视图拼接了一张物理宽表...也是第一次写过那么长的 sql 了, 心得就是硬怼出来的, 现都还谈不上优化, 能基本怼出来就已经很不错了, 对于目前的我而已. 其实更多的是已经有了的编程思维和sql 思维, 这两个完全不太一样的东西的碰撞吧, 多花点时间, 掌握窍门就会快很多.

现在用的是 mysql 和 IQ, 主要是 IQ, 我写的逻辑其实还是 mysql 风格的, 就很多嵌套, 和子查询, 表关联. 这些步骤都是必须要的, 其实更多是像在硬怼, 但最终还是能够整出来的. 跟面向过程编程的感觉是一样的, 我是觉得特别锻炼逻辑能力的.

然后也逐渐发现呢, 其实 sql 顺序, 会让你感到, 并不是严格按照 from , on, join, where, group by, having, select , distinct, group by, order by limit .. 这样的, 数据库引擎会自动做优化. 当然不同的商业公司的产品可能会不一样的.

简单来个小栗子, 还是以学生表为例.

select s_name as 姓名 from student where 姓名 = '星落'
  • 在 mysql 中, 这样写是不可以的, 因为 '姓名' 在 student 表中没有定义的
  • 在 IQ 中, 是可以的, 它在执行前, 会把 where 的 '姓名' 类似用以 s_name 替换, 说明引擎是有优化的

但是在 IQ 中, 如果 select 里面有 计算字段, 函数参与的字段, 就会找不到, 就让我很奇怪, 到底是优化还是没有

 select 
  s_id as 学号,
  avg(score) as 平均成绩
 from score 
 group by 学号
   having 平均成绩 > 70

学号     平均成绩
0001	89.6667
0003	80.0000

你会发现, 会给你一种感觉, 执行了 select 的部分, 再执行 group by 的. 也会有一种, 动态编程语言的感觉, 比如 Python 或者 JavaScript, 就执行的时候, 会自己去按一定顺序进行搜索. 具体是怎么实现的, sql 这块我目前也不清楚, 唯一能做的就是去写, 去尝试,如果不行就再再外面, 嵌套一层, 哈哈哈 .

扯远了, 本来是要在看一波迭代器的.

迭代器切片

需求

不通过把迭代器对象全部 list 等方式 "放出来" , 太消耗内存, 只想切片来取一部分.

方案

通过 itertools.islice () 来实现对迭代对象的切片. 不能用 [ ] 这种方式哦

def count(n):
    """从 n 开始往后计数, 步长为1"""
    while True:
        yield n
        n += 1
# test
g = count(0)
g[10:20]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-1-72a548c692de> in <module>
      6 
      7 g = count(0)
----> 8 g[10:20]

TypeError: 'generator' object is not subscriptable

生成器 generator 是不能直接来切片的, 用 itertools.islice( )

import itertools 

for i in itertools.islice(g, 1, 10):
    print(i)
1
2
3
4
5
6
7
8
9

其实很好理解, 生成器对象是不能用咱的标准切片的, 因为它的长度, 事先我们并不知道, 而且也没有实现索引.

函数 itertools.islice( ) 会返回一个可以生成指定元素的迭代器, 通过 遍历并丢弃 直到切片开始索引位置的所有元素, 然后才一个个地返回元素, 直到结束嘛.

需要注意的是, islice() 函数会消耗掉传入迭代器中的数据. 必须考虑到, 迭代器是一个不可逆的事实, 就像时间, 流逝了就永远不会再回来.

排列组合的迭代

需求

迭代遍历一个集合中, 元素的所有可能性 (排列或者组合)

方案

通过 itertools 内置模块的 permutations( ), combinations( ), combinations_with_replacement() 这三个已经造好的轮子来计算集合元素的排列组合.

我之前有自己实现过排列组合, 有点忘了是咋搞了, 当时也是参考网上的大佬, 就写起来蛮复杂其实, 留个小 todo, 我先学搬砖, 后面再看看怎么自己来实现一遍吧.

首先是 permutations () 排列嘛, 跟咱数学的东西是一样的. 它接收一个集合对象, 并产生一个元组序列, 是无序的.

从n个元素中取m个元素, 排列数有 : \(A_n^m = n(n-1)(n-2)...(n-m+1) = \frac {n!} {(n-m)!}\)

from itertools import permutations

lst = ['a', 'b', 'c'] 

# 是一个可迭代对象, 生成器 <itertools.permutations at 0x1f41a20da40>

for p in permutations(lst):
    print(p)
('a', 'b', 'c')
('a', 'c', 'b')
('b', 'a', 'c')
('b', 'c', 'a')
('c', 'a', 'b')
('c', 'b', 'a')

还可以指定长度的所有排列, 可以传参, 这就很厉害了. 反正暂时我是不会写, 搬砖我是专业的.

for p in permutations(lst, 2):
    print(p)
('a', 'b')
('a', 'c')
('b', 'a')
('b', 'c')
('c', 'a')
('c', 'b')

同样的, 组合 combinations() 也是类似的用法, 它返回一个集合元素中的所有组合.

从 n 个 中 取 m 个元素, 组合数有: \(C_n^m = \frac {n!}{m!(n-m)!}\) 推导过程是地推的, 这里不讲了, 留到概率论部分的笔记吧.

lst = ['a', 'b', 'c']

for c in itertools.combinations(lst, 3):
    print(c)
    
print() 

print("从3个中取2个, 一共有: ")
for c in itertools.combinations(lst, 2):
    print(c)
('a', 'b', 'c')

从3个中取2个, 一共有: 
('a', 'b')
('a', 'c')
('b', 'c')

最后这个 combinations_with_replacement( ) 就咱说的,可放回抽样.

对于 combinations 来说, 元素的顺序其实无所谓的, 都只是一个. 因此在计算组合的时候, 一旦元素被选取, 就会从候选中给剔除掉. 而如果是要实现可放回抽样, 就要这样玩:

for c in itertools.combinations_with_replacement(lst, 3):
    print(c)
('a', 'a', 'a')
('a', 'a', 'b')
('a', 'a', 'c')
('a', 'b', 'b')
('a', 'b', 'c')
('a', 'c', 'c')
('b', 'b', 'b')
('b', 'b', 'c')
('b', 'c', 'c')
('c', 'c', 'c')

小结

  • 迭代器对象的切片用 itertools.islice () 方法来实现, 偶尔是会用的
  • 排列组合的迭代对象 也用 itertools 里面的 permutations( ), combinations( ), combinations_with_replacement()
  • itertools 这个内置模块很香, 值得搬砖, 同时也抽空自己来写一遍排列组合原理, 和数学推导
posted @ 2020-06-15 23:59  致于数据科学家的小陈  阅读(179)  评论(0编辑  收藏  举报