前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >左手用R右手Python系列之——迭代器与迭代对象

左手用R右手Python系列之——迭代器与迭代对象

作者头像
数据小磨坊
发布2018-04-12 10:15:41
1.3K0
发布2018-04-12 10:15:41
举报
文章被收录于专栏:数据小魔方数据小魔方

接触过Python的小伙伴儿肯定都知道,Python中关于迭代器和可迭代对象运用的很广泛。迭代器可以以一种非常友好的方式使用在循环中,不仅节省内存,还能优化代码。

在R语言中,其实也有迭代的概念,但是需要借助第三方包的辅助。

今天要介绍的包是iterators和itertools,这两个包在最新开发的软件包工具中使用的非常频繁。迭代器作为一种特殊的容器,生成之后,只能按照顺序迭代完内部对象之后,便失效了,要想重新迭代就必须重新生成一个迭代器。

而我们在普通场景下构造的循环,一般都利用R语言内部的现有的数据结构(列表、向量、数据框等),这些数据结构是可见的迭代对象,而且迭代完一次之后,可以重复使用,这一点是迭代器与普通对象最大的区别。

代码语言:javascript
复制
library("iterators")
library("itertools")

iter函数可以创建一个迭代器对象,迭代器可以从所有R语言数据结构对象中创建,包括向量、矩阵、列表、数据框。

代码语言:javascript
复制
iter(obj, ...)
# a vector iterator

i1 <- iter(1:5)
> i1
$state
<environment: 0x0000000005df86c8>

$length
[1] 5

$checkFuncfunction (...) 
TRUE
<environment: 0x0000000005dfb860>

$recycle
[1] FALSE

attr(,"class")
[1] "containeriter" "iter"

nextElem可以逐次迭代迭代器内的单个对象,当迭代次数用完了之后,便会抛出错误。

代码语言:javascript
复制
> nextElem(i1)
[1] 1

> nextElem(i1)
[1] 2

> nextElem(i1)
[1] 3

> nextElem(i1)
[1] 4

> nextElem(i1)
[1] 5

> nextElem(i1)
Error: StopIteration

迭代数据框:

代码语言:javascript
复制
# a data frame iterator by column
i2 <- iter(data.frame(x=1:3, y=10, z=c('a', 'b', 'c')))
$state
<environment: 0x0000000016a84200>

$by
[1] "column"

$length
[1] 3

$checkFuncfunction (...) 
TRUE
<environment: 0x0000000016a85f20>

$recycle
[1] FALSE

attr(,"class")
[1] "dataframeiter" "iter"
代码语言:javascript
复制
nextElem(i2)
[1] 1 2 3

> nextElem(i2)
[1] 10 10 10

> nextElem(i2)
[1] a b c
Levels: a b c

对于数据框而言,默认按列迭代,最大迭代次数为列数,迭代完为止。

代码语言:javascript
复制
i2 <- iter(data.frame(x=1:3, y=10, z=c('a', 'b', 'c')),by="row")
> nextElem(i2)
  x  y z
1 1 10 a
> nextElem(i2)
  x  y z
2 2 10 b
> nextElem(i2)
  x  y z
3 3 10 c

设置迭代依据参数by可以控制迭代方式,这里将by设为row迭代即为按行迭代。

迭代器可以通过as.list函数进行还原。

代码语言:javascript
复制
iter1<-iter(LETTERS[1:10])
as.list(iter1)
[[1]]
[1] "A"

[[2]]
[1] "B"

[[3]]
[1] "C"

[[4]]
[1] "D"

[[5]]
[1] "E"

[[6]]
[1] "F"

[[7]]
[1] "G"

[[8]]
[1] "H"

[[9]]
[1] "I"

[[10]]
[1] "J"

转换之后,迭代器便失效了,nextElem(iter1)会报错。

enumerate函数可以将列表或者向量进行键值对形式的迭代(Python中就有同名的函数,这并不奇怪,因为以上两个包中的所有函数都是参照Python中的迭代器包设计的)。

代码语言:javascript
复制
myvector<-LETTERS[1:5]
iter2 <- enumerate(myvector)
nextElem(iter2)

$index
[1] 1

$value
[1] "A"

> nextElem(iter2)
$index
[1] 2

$value
[1] "B"

> nextElem(iter2)
$index
[1] 3

$value
[1] "C"

> nextElem(iter2)
$index
[1] 4

$value
[1] "D"

> nextElem(iter2)
$index
[1] 5

$value
[1] "E"

hasNext函数可以将一个迭代器内的每一次迭代行为添加是否可迭代的标记,当迭代次数没有达到最大值时,显示可继续迭代,否则显示剩余可迭代次数为零。

代码语言:javascript
复制
it <- ihasNext(LETTERS[1:5])
while (hasNext(it))
    cat(nextElem(it),"\n")
A 
B 
C 
D 
E

使用hasNext函数作为迭代器判定条件,当迭代次数完成之后,越界的迭代就会立即停止。

除了以上函数之外,还有很多定制的迭代函数,用于迭代不同场景下的数据结构。

代码语言:javascript
复制
x <- array(1:24, c(2,3,4))
, , 1

     [,1] [,2] [,3]
[1,]    1    3    5

[2,]    2    4    6

, , 2

     [,1] [,2] [,3]
[1,]    7    9   11

[2,]    8   10   12

, , 3

     [,1] [,2] [,3]
[1,]   13   15   17

[2,]   14   16   18

, , 4

     [,1] [,2] [,3]
[1,]   19   21   23

[2,]   20   22   24

as.list(iarray(x, 1)) #迭代各维度子数据块儿的行

代码语言:javascript
复制
[[1]]
     [,1] [,2] [,3] [,4]
[1,]    1    7   13   19

[2,]    3    9   15   21

[3,]    5   11   17   23

[[2]]
     [,1] [,2] [,3] [,4]
[1,]    2    8   14   20

[2,]    4   10   16   22

[3,]    6   12   18   24

as.list(iarray(x, 2)) #迭代各维度子数据块儿的列

代码语言:javascript
复制
[[1]]
     [,1] [,2] [,3] [,4]
[1,]    1    7   13   19

[2,]    2    8   14   20

[[2]]
     [,1] [,2] [,3] [,4]
[1,]    3    9   15   21

[2,]    4   10   16   22

[[3]]
     [,1] [,2] [,3] [,4]
[1,]    5   11   17   23

[2,]    6   12   18   24

更多的迭代器函数,可以参考itertools、itertor包的文档,迭代器的工作虽然也可以通过基础数据对象来完成,但是其简洁性、内存有好、容易设置循环中的判断条件,给以给数据处理过程带来很大便利。

https://github.com/ramhiser/itertools2 https://github.com/cran/iterators

Python

之前讲解R语言中迭代器概念的时候曾说过,R语言iterator包、itertools包是参照Python中的迭代器理念设计的,所以理念和用法都差不多。

代码语言:javascript
复制
import string,random
iter1 = random.sample(string.ascii_letters[26:],5)
['J', 'E', 'F', 'W', 'Y']
I=iter(iter1)

使用iter函数可以将一个可迭代对象(可以是列表、字典、元组、集合等)转换为一个迭代器。

代码语言:javascript
复制
<list_iterator at 0x2b8c927b1d0>

直接打印迭代器将会返回以上结果,迭代器将所有列表元素封装成一个可迭代的容器,迭代的最大次数即为转换前可迭代对象的长度。使用next()函数可以单次迭代一个迭代器,直至迭代到最大次数,迭代器失效,再次迭代将会抛出错误。

代码语言:javascript
复制
next(I)
'J'

next(I)
'E'

next(I)
'F'

next(I)
'W'

next(I)
'Y'

Traceback (most recent call last):

  File "<ipython-input-24-032af8264890>", line 1, in <module>
    next(I)

StopIteration

使用isinstance函数可以检测一个对象是否是迭代器。

代码语言:javascript
复制
from collections import Iterator, Iterable

isinstance(iter1, Iterable)
True

isinstance(iter1, Iterator)
False

isinstance(I, Iterable)
True

isinstance(I, Iterator)
True

可以看到,转换前的列表仅仅是可迭代对象,而不是迭代器,而转换后的迭代器对象,即是可迭代对象,也是迭代器。

一个迭代器可以被for循环直接访问(在R中好像不允许)。

代码语言:javascript
复制
I=iter(iter1)
for i in I:
    print(i,"\n")
J 

E 

F 

W 

Y

for循环访问迭代器,迭代至最大次数之后,迭代器失效,循环停止并自动跳出,无需设置跳出条件。

在Python中与迭代器经常一起被提起的就是生成器了(关于生成器目前在R语言中还没有看到很好的实例)。

使用各种推导式函数可以很方便的改造成生成器。

代码语言:javascript
复制
import string,random
gen = random.sample(string.ascii_letters[26:],5)
gen1 = (i.lower() for i in gen)
type(gen1)
Out[41]: generator
<generator object <genexpr> at 0x000002B8C9285D00>

一个典型的生成器格式如上所示。

生成器也可以直接被for循环直接访问并遍历其中对象。

代码语言:javascript
复制
for i in gen1:
    print(i,"\n")
t 

p 

f 

n 

h

而另一种生成器的产生方式是使用yield函数。

代码语言:javascript
复制
def odd(gen):
    for i in gen:
        yield i

mygen = odd(gen)
<generator object odd at 0x000002B8C92B5E08>

迭代器和生成器可以通过list函数转换为普通的list对象,两者与内建数据对象的最大区别是,迭代器和生成的元素是不可见的,只有在使用循环访问或者使用next函数调用的时候才能输出内部元素。迭代器和生成器迭代到最大次数之后便失效了。

代码语言:javascript
复制
list(mygen)
Out[48]: ['T', 'P', 'F', 'N', 'H']
list(iter(['T', 'P', 'F', 'N', 'H']))
Out[50]:['T', 'P', 'F', 'N', 'H']

迭代器和生成器是函数型编程的重要技巧,这些技巧用在代码中,不但能够减少内存使用,还能够提高代码可读性。

往期案例数据请移步本人GitHub: https://github.com/ljtyduyu/DataWarehouse/tree/master/File

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-11-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据小魔方 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档