用 R 表示拉链还是枚举?

这些 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-pkg,但是我希望找到一种更加惯用的方法。

63559 次浏览

If that is the Python print representation of a matrix, then this code:

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

You are still leaving those of us who are not Python users in the dark regarding the structure of your desired output. You use the term "list" but the output suggests an ordered set of tuples.

Given @chi's guidance we might also suggest using the very R-centric 'dataframe' structure

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

... which has the flexibility of a list in terms of column types and the access features of a matrix in terms of row and column indexing. Or one could use hhh's request and create the implicitly indexed values of the j-vector, 10:20, using the rownames vector that starts at "1" by default, but which could be altered to become a character vector starting at "0"

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


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

Unfortunately, the unwary will find that dfrm[0, ] is not a happy call, returning vector of length 0.

There have been some discussions around list comprehension for R, e.g. here or there. The hash package even offers dictionary-like structure. However, as others said, it is difficult to try to map one language facilities onto another (even if this is what Comparison of programming languages actually offers) without a clear understanding of what it is supposed to be used to. For example, I can mimic Python zip() in R as follows:

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

> 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

As can be seen, this really depends on what you want to do with the result afterwards.

Answer for python enumerate:

In R, a list is ordered (see this answer). Thus, all you need is to index either keys (using names()[i]) or values (using [[i]]).

Using seq_along (alternatively can do 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"

Answer for python zip:

See one of the above answers to mimic the list of tuples. My preference is towards a data frame as shown in BondedDust's answer:

> x <- 1:3
> y <- 4:6
> data.frame(x=x, y=y)
x y
1 1 4
2 2 5
3 3 6
# 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
})
}

In order to use Python style list comprehensions with enumerations, such as enumerated lists, one way is to install List-comprehension package LC (developed 2018) and itertools package (developed 2015).

List comprehensions in R

You can find the LC package here.

install.packages("devtools")
devtools::install_github("mailund/lc")

Example

> 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"

where the programming syntax is not yet as clean and polished as in Python but functionally working and its help outlines:

"The syntax is as follows: lc(expr, lists, predicates) where expr is some expression to be evaluated for all elements in the lists, where lists are one or more named lists, where these are specified by a name and an expression name = list_expr, and where predicates are expressions that should evaluated to a boolean value. For example, to get a list of all even numbers, squared, from a list x we can write lc(x ** 2, x = x, x %% 2 == 0). The result of a call to lc is a list constructed from the expressions in expr, for all elements in the input lists where the predicates evaluate to true."

where notice that you can leave the predicates empty for example in the above example.

Python-style itertools and enumerations

You can use R's itertools that is very similar to Python's itertools, further in Cran here

library(itertools)

where described

"Various tools for creating iterators, many patterned after functions in the Python itertools module, and others patterned after functions in the 'snow' package."

Example. enumeration

> 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"

Example. enumeration with 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"

Another option which will create a list of vectors is to use the Map function as seen here by @peterhurford: https://rdrr.io/github/peterhurford/funtools/src/R/zippers.R

> x <- 1:3
> y <- 4:6
> z <- 7:9
> Map(c, x, y, z)
[[1]]
[1] 1 4 7


[[2]]
[1] 2 5 8


[[3]]
[1] 3 6 9

zip and enumerate are not particularly difficult to implement in R:

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

Enumerate is simple to define in terms of zip:

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

Since these are proper functions, we can use ... to make them fairly flexible and terse, and take advantage of mapply's behavior, such as recycling inputs and naming output correctly.

This can be achieved using two paste statements:

str1 <- paste(1:11, 10:20, sep=",", collapse='), (')
paste("(", str1, ")", sep = "")

Output will like following:

'(1,10), (2,11), (3,12), (4,13), (5,14), (6,15), (7,16), (8,17), (9,18), (10,19), (11,20)'

For python, 'enumerate' equivalent in R. Storing the vectors in the list and iterating over them with an index should work fine.

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))
}

Output:

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

R 'list' is a nice bank for depositing the vectors and retain with an index.