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

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

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

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

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

library("iterators")
library("itertools")

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

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可以逐次迭代迭代器内的单个对象,当迭代次数用完了之后,便会抛出错误。

> nextElem(i1)
[1] 1

> nextElem(i1)
[1] 2

> nextElem(i1)
[1] 3

> nextElem(i1)
[1] 4

> nextElem(i1)
[1] 5

> nextElem(i1)
Error: StopIteration

迭代数据框:

# 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"
nextElem(i2)
[1] 1 2 3

> nextElem(i2)
[1] 10 10 10

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

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

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函数进行还原。

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中的迭代器包设计的)。

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

it <- ihasNext(LETTERS[1:5])
while (hasNext(it))
    cat(nextElem(it),"\n")
A 
B 
C 
D 
E

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

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

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)) #迭代各维度子数据块儿的行

[[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)) #迭代各维度子数据块儿的列

[[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中的迭代器理念设计的,所以理念和用法都差不多。

import string,random
iter1 = random.sample(string.ascii_letters[26:],5)
['J', 'E', 'F', 'W', 'Y']
I=iter(iter1)

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

<list_iterator at 0x2b8c927b1d0>

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

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函数可以检测一个对象是否是迭代器。

from collections import Iterator, Iterable

isinstance(iter1, Iterable)
True

isinstance(iter1, Iterator)
False

isinstance(I, Iterable)
True

isinstance(I, Iterator)
True

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

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

I=iter(iter1)
for i in I:
    print(i,"\n")
J 

E 

F 

W 

Y

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

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

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

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循环直接访问并遍历其中对象。

for i in gen1:
    print(i,"\n")
t 

p 

f 

n 

h

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

def odd(gen):
    for i in gen:
        yield i

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

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

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

原文发布于微信公众号 - 数据小魔方(datamofang)

原文发表时间:2017-11-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏菩提树下的杨过

python:函数的高级特性

2203
来自专栏HTML5学堂

提取数字——字符串、正则面试题

提取数字——字符串、正则面试题 HTML5学堂:正则、数组、字符串,是JavaScript语言中让人头痛的一些知识,今天这篇文章我们使用数组字符串、正则两种方法...

2986
来自专栏书山有路勤为径

1.C与C++

使用c++中的标准库类型vector可以很轻松的完成任务。 不需要管理内存分配,对不同的类型都可以处理

1324
来自专栏我的博客

sizeof用法

sizeof()功能:计算数据空间的字节数 1.与strlen()比较 strlen()计算字符数组的字符数,以”\0″为结束判断,不计算为’\...

2924
来自专栏GIS讲堂

postgis常用函数介绍(二)

通过函数st_isempty(geom)可以判断geometry是否为空,返回是布尔型的true或者false,具体使用如下:

1783
来自专栏python学习之旅

Python笔记(十七):生成器

Python生成器是创建迭代器的简单方法。简单来说,生成器是一个函数,它返回一个我们可以迭代的对象(迭代器)(一次一个值)。

831
来自专栏Jed的技术阶梯

算法小细节之数组某部分的中间位置的索引

给定一个数组的某个部分,这部分起始索引为L,结束索引为R,求这部分中间位置的索引。

1012
来自专栏韦弦的偶尔分享

Swift 移动零 - LeetCode

思路:增加标志位(j)记录从头开始的非零元素后面的位置,循环数组,当元素非零时,交换nums[i]和nums[j],即将找到的非零元素移动到当前非零元素串后面。

932
来自专栏Phoenix的Android之旅

你不知道的HashMap

面试中经常会问到常用数据结构,比如HashMap。 相信你平时几乎每天都会用到HashMap,但是你知道它是的实现原理是怎样的吗? 这里先提几个问题:HashM...

993
来自专栏肖洒的博客

Python3学习集合

1673

扫码关注云+社区

领取腾讯云代金券