在R中使用Zip还是Enumerate?

103

以下是 Python 列表推导式的 R 语言等价物:

[(i,j) for i,j in zip(index, Values)]
[(i,j) for i,j in enumerate(Values)]
[(i,j) for i,j in enumerate(range(10,20))]   %MWE, indexing or enumerating to 
                                            %keep up with the index, there may 
                                            %be some parameter to look this up

输出示例

>>> [(i,j) for i,j in enumerate(range(10,20))]
[(0, 10), (1, 11), (2, 12), (3, 13), (4, 14), (5, 15), (6, 16), (7, 17), (8, 18), (9, 19)]

我之前用R解决过这个问题,但现在不能记起来了。当时的第一个想法是使用itertools软件包,但我希望找到更符合习惯的方法。


2
如果你能给那些对Python不熟悉的人提供一个小的实例,可能会增加潜在回答者的数量。我猜最后一个是expand.grid(i=10:20,j=10:20) - Ben Bolker
@BenBolker:添加了一个输出--现在清楚了吗?这可能会更具挑战性,但逻辑很重要... - hhh
1
我同意@DWin的观点。期望在R和Python之间存在一对一的数据结构映射是不合理的。如果您想得到好的答案,您应该明确指定您希望结果在R中的样子,而不是在Python中。 - joran
顺便说一下,这是一种漂亮的方法来压缩和展平这两个列表:as.vector(rbind(1:10, 11:20)) - smci
9个回答

63

关于Python的enumerate的回答:

R语言中,列表是有序的(详见这个解答)。因此,你只需要通过索引键值(使用names()[i])或值(使用[[i]])来获取相应元素。

可以使用seq_along(也可以使用for(i in 1:length(mylist)){...}):

> mylist <- list('a'=10,'b'=20,'c'=30)
> for (i in seq_along(mylist)){
+   print(paste(i,names(mylist)[i],mylist[[i]]))
+ }
[1] "1 a 10"
[1] "2 b 20"
[1] "3 c 30"

Python中zip的答案:

查看上面的回答之一以模仿元组列表。我的偏好是使用数据框,如BondedDust的答案所示:

> x <- 1:3
> y <- 4:6
> data.frame(x=x, y=y)
  x y
1 1 4
2 2 5
3 3 6

1
将你的第一个示例延续到第二个示例中,使用以下代码:data.frame(names=labels(mylist),values=unlist(mylist),row.names = 1:length(mylist)) - Josiah Yoder
一个有关性能的问题:调用names(mylist)[i]每次循环都需要进行操作吗,还是这是一个微不足道的操作?我在想在循环之前将其分配给name_list是否更好。 - markgalassi
2
R语法非常丑陋,如果我们想要做一些超出正常语法范围的事情,这种语言在某些方面非常不灵活,但在其他方面非常好(例如NSE和SE)。 - Qbik

44

关于R语言的列表推导式已经有一些讨论,例如这里那里。甚至hash包提供了类似字典的结构。但是,正如其他人所说的,试图将一种语言的特性映射到另一种语言中是困难的(即使这就是编程语言比较实际上提供的),没有明确理解它应该用于什么。例如,我可以在R中模仿Python的zip()函数:

Python代码:

In [1]: x = [1,2,3]
In [2]: y = [4,5,6]
In [3]: zip(x, y)
Out[3]: [(1, 4), (2, 5), (3, 6)]

R


这是一个带有加粗字母 R 的 HTML 段落。
> x <- 1:3
> y <- 4:6
> list(x, y)                     # gives a simple list
> as.list(paste(x, y))           # three tuples, as a list of characters
> mapply(list, x, y, SIMPLIFY=F) # gives a list of 3 tuples
> rbind(x, y)                    # gives a 2x3 matrix 

可以看出来,这实际上取决于您之后想要如何处理结果。


1
我认为问题在于,当你想在Python中使用zip时,应该使用什么。典型的用法是使用多个参数进行列表推导,所以mapply可以直接处理这种情况。 - seanv507
6
"Mapply"是我们所需的直接类比。 - WestCoastProjects
@mengjavadba mapply覆盖了最常见的用例:先压缩,然后映射。 - Josiah Yoder

19

zipenumerate在R中实现起来并不特别困难:

#' zip(1:5,1:10)
zip <- function(...) {
  mapply(list, ..., SIMPLIFY = FALSE)
}

使用 zip 很容易定义枚举:

#' enumerate(l=LETTERS)
enumerate <- function(...) {
  zip(ix=seq_along(..1), ...)
}

由于这些是正确的函数,我们可以使用...使它们相当灵活简洁,并利用mapply的行为,例如回收输入和正确命名输出。


1
这些已经被添加到 stackoverflow 包中了,如果需要的话。 - Neal Fultz
还有什么方法可以让它与数据框一起工作吗?默认情况下,它会给我数据框的列,但我更愿意枚举行。 目前我使用 enumerate(v = split(g, seq(nrow(g)))) 其中 g = data.frame(a = c(1, 2, 3), b = c(4, 5, 6)) - Frank
1
在cran构建中,它被命名为zip2,以避免与基本R发生冲突。 - Neal Fultz
在以下用例中,您经常会想要添加[-1,],从而使其更像Python: d <- tibble(x = c(1, 4, 2, NA, 2), y = c(NA, NA, NA, NA, NA), z = c(3, 1, NA, 12, NA), w = c(NA, NA, 6, NA, 9)) 在这里,d %>% replace_na(mapply(list, colnames(.), 0)[-1,])等同于d %>% replace_na(list(x = 0, y = 0, z = 0, w = 0)) - CubicInfinity
1
多个向量元素的组合可以通过 expand.grid()purrr::cross() 实现。 - ivan866
显示剩余2条评论

10

在Python中,zip的主要用途是迭代多个向量/列表:for xi, yi in zip(x, y): ...。对于我目前看到的最优雅的R语言解决方案,加1分:for (xi.yi in Map(c, x, y)) { xi <- xi.yi[1]; yi <- xi.yi[2]; ... } - sgrubsmyon

6
如果这是一个矩阵的Python打印表示,那么这段代码如下:
j <- 10:20
matrix(c(seq_along(j), j), ncol=2)
#------------
      [,1] [,2]
 [1,]    1   10
 [2,]    2   11
 [3,]    3   12
 [4,]    4   13
 [5,]    5   14
 [6,]    6   15
 [7,]    7   16
 [8,]    8   17
 [9,]    9   18
[10,]   10   19
[11,]   11   20

你仍然让我们这些不使用Python的人对你所需输出的结构一无所知。你使用了术语“列表”,但输出表明它是一组有序的元组。
鉴于@chi的指导,我们也可以建议使用非常适合R的“数据框”结构。
x <- 1:3
y <- 4:6
dfrm <- data.frame(x=x, y=y)

这个东西在列类型的灵活性方面类似于列表,而在行和列索引方面则类似于矩阵。也可以使用hhh的请求,使用默认从“1”开始的rownames向量创建j-向量的隐式索引值10:20,但是该向量可以更改为以“0”开头的字符向量。

dfrm <- data.frame(j=10:20)
dfrm[3, ]
#[1] 12

 rownames(dfrm) <- 0:10
 dfrm["0",]
# [1] 10

不幸的是,不小心的人会发现dfrm [0,]不是一个愉快的调用,返回长度为0的向量。


+1 对于优雅的解决方案。(不,那些不是 Python 矩阵,但正如您已经猜到的那样,它们是元组列表。) - chl

4
为了在Python中使用带有枚举的列表推导式,例如枚举列表,一种方法是安装List-comprehension包LC(2018年开发)和itertools包(2015年开发)。
R中的列表推导式
您可以在此处找到LC这里
install.packages("devtools")
devtools::install_github("mailund/lc")

例子

> library(itertools); library(lc)
> lc(paste(x$index, x$value), x=as.list(enumerate(rnorm(5))), )
[[1]]
[1] "1 -0.715651978438808"

[[2]]
[1] "2 -1.35430822605807"

[[3]]
[1] "3 -0.162872340884235"

[[4]]
[1] "4 1.42909760816254"

[[5]]
[1] "5 -0.880755983937781"

其中编程语法还不如Python干净、流畅,但功能上是可以正常工作的,它的帮助概述如下: “语法如下:lc(expr, lists, predicates),其中expr是要对列表中所有元素进行评估的某些表达式,lists是一个或多个命名列表,这些列表由名称和表达式name=list_expr指定,predicates是应该被评估为布尔值的表达式。例如,要从列表x中获取所有偶数的平方列表,我们可以写成lc(x ** 2, x = x, x %% 2 == 0)。调用lc的结果是从expr中构造的列表,对于输入列表中谓词评估为true的所有元素。” 在上面的例子中,注意你可以将谓词留空。 Python风格的itertools和枚举 您可以使用R的itertools,它非常类似于Python的itertools,在Cran here中进一步了解。
library(itertools)

需要描述的地方

"有各种用于创建迭代器的工具,其中许多模仿 Python itertools 模块中的函数,其他模仿 'snow' 包中的函数。"

示例:枚举

> for (a in as.list(enumerate(rnorm(5)))) { print(paste(a$index, "index:", a$value))}
[1] "1 index: 1.63314811372568"
[1] "2 index: -0.983865948988314"
[1] "3 index: -1.27096072277818"
[1] "4 index: 0.313193212706331"
[1] "5 index: 1.25226639725357"

示例。使用ZIP进行枚举

> for (h in as.list(izip(a=1:5, b=letters[1:5]))) { print(paste(h$a, "index:", h$b))}
[1] "1 index: a"
[1] "2 index: b"
[1] "3 index: c"
[1] "4 index: d"
[1] "5 index: e"

2
这可以通过两个粘贴语句来实现:
str1 <- paste(1:11, 10:20, sep=",", collapse='), (')
paste("(", str1, ")", sep = "")

输出结果将如下所示:
'(1,10), (2,11), (3,12), (4,13), (5,14), (6,15), (7,16), (8,17), (9,18), (10,19), (11,20)'

1
在R中,与Python中的'enumerate'等价的是将向量存储在列表中,并使用索引迭代它们应该可以正常工作。
vect1 <- c('A', 'B', 'C')
vect2 <- c('a', 'b', 'c')

# eqiv to zip values:
idx_list <- list(vect1, vect2)
idx_vect <- c(1:length(idx_list[[1]]))

for(i in idx_vect){
    x <- idx_list[[1]][i]
    j <- idx_list[[2]][i]
    print(c(i, x, j))
}

输出:

[1] "1" "A" "a"
[1] "2" "B" "b"
[1] "3" "C" "c"

R中的'list'是一个很好的银行,可以存储向量并保留索引。


0
# similar to python. return a list of list. Short sequences get recycled.
zip <- function(...){ 
    all.list <- list(...)
    ele.names <- names(all.list)
    max.length <- max(sapply(all.list, length))
    lapply(0:(max.length - 1), function(i) {
        res <- lapply(all.list, function(l) l[i %% length(l) + 1]) 
        names(res) <- ele.names
        res
    })
}

请提供关于这段代码的描述。 - Keivan Esbati
这个函数与@chl指出的“mapply(list, x, y, SIMPLIFY=F)”完全相同。 - ibilgen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接