¿Zip o enumerar en R?

82

¿Cuáles son los equivalentes de R para estas comprensiones de listas de Python?

[(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

Ejemplo con salida

>>> [(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)]

Resolví este problema anteriormente con algún truco en R pero ya no puedo recordar, la primera idea fue itertools -pkg pero espero encontrar una forma más idiomática de hacer las cosas.

hhh
fuente
2
Si pudiera dar un pequeño ejemplo práctico para aquellos de nosotros que no estamos familiarizados con Python, podría aumentar la población de posibles respondedores. Supongo que el último esexpand.grid(i=10:20,j=10:20)
Ben Bolker
@BenBolker: agregó una salida, ¿ahora está claro? Podría ser más desafiante, pero la lógica es importante ...
hhh
1
Estoy de acuerdo con @DWin. No es razonable esperar un mapeo uno a uno entre las estructuras de datos en R y Python. Si desea buenas respuestas, debe especificar cómo desea que se vea el resultado en R, no en Python.
joran
Por cierto, aquí hay una manera ordenada de comprimir y aplanar las dos listas:as.vector(rbind(1:10, 11:20))
smci

Respuestas:

43

Respuesta para Python enumerate:

En R, se ordena una lista (consulte esta respuesta ). Por lo tanto, todo lo que necesita es indexar claves (usando names()[i]) o valores (usando [[i]]).

Usando seq_along(alternativamente puede hacer 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"

Respuesta para Python zip:

Vea una de las respuestas anteriores para imitar la lista de tuplas. Mi preferencia es hacia un marco de datos como se muestra en la respuesta de BondedDust:

> x <- 1:3
> y <- 4:6
> data.frame(x=x, y=y)
  x y
1 1 4
2 2 5
3 3 6
Theja
fuente
Para continuar con su primer ejemplo en la segunda,data.frame(names=labels(mylist),values=unlist(mylist),row.names = 1:length(mylist))
Josías Yoder
42

Ha habido algunas discusiones sobre la comprensión de listas para R, por ejemplo, aquí o allá . El paquete hash incluso ofrece una estructura similar a la de un diccionario. Sin embargo, como dijeron otros, es difícil intentar mapear las instalaciones de un lenguaje con otro (incluso si esto es lo que realmente ofrece la comparación de lenguajes de programación ) sin una comprensión clara de a qué se supone que está acostumbrado. Por ejemplo, puedo imitar Python zip()en R de la siguiente manera:

Pitón

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 

Como se puede ver, esto realmente depende de lo que quieras hacer con el resultado después.

chl
fuente
1
Creo que la pregunta es qué usa cuando usa zip en python. el uso típico es hacer una lista de comprensión con múltiples argumentos, por lo que mapply maneja esto directamente.
seanv507
6
El mapplyes lo que queremos para el análogo directa.
StephenBoesch
@javadba mapplycubre el caso de uso más común: zip, luego map.
Josiah Yoder
7

Otra opción que creará una lista de vectores es usar la función Mapa como la ve aquí @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
wphampton
fuente
En Python, un uso principal de zip es iteración sobre varios vectores / listas: for xi, yi in zip(x, y): .... +1 para la solución más elegante que he visto hasta ahora para hacer esto en R:for (xi.yi in Map(c, x, y)) { xi <- xi.yi[1]; yi <- xi.yi[2]; ... }
sgrubsmyon
6

Si esa es la representación impresa de Python de una matriz, entonces este código:

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

Todavía nos está dejando a aquellos de nosotros que no somos usuarios de Python en la oscuridad con respecto a la estructura de su salida deseada. Utiliza el término "lista" pero el resultado sugiere un conjunto ordenado de tuplas.

Dada la orientación de @ chi, también podríamos sugerir el uso de la estructura de 'marco de datos' muy centrada en R

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

... que tiene la flexibilidad de una lista en términos de tipos de columnas y las características de acceso de una matriz en términos de indexación de filas y columnas. O uno podría usar la solicitud de hhh y crear los valores indexados implícitamente del vector j 10:20, usando el rownamesvector que comienza en "1" por defecto, pero que podría modificarse para convertirse en un vector de caracteres que comienza en "0"

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

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

Desafortunadamente, los incautos encontrarán que dfrm [0,] no es una llamada feliz, devolviendo un vector de longitud 0.

IRTFM
fuente
+1 para una solución elegante. (No, esas no son matrices de Python, pero como ya adivinó, es una lista de tuplas .)
chl
4

Para utilizar listas por comprensión de estilo Python con enumeraciones, como listas enumeradas, una forma es instalar el paquete List-comprehension LC(desarrollado en 2018) y el paquete itertools (desarrollado en 2015).

Lista de comprensiones en R

Puedes encontrar el LCpaquete aquí .

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

Ejemplo

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

donde la sintaxis de programación aún no es tan limpia y pulida como en Python, pero funciona funcionalmente y sus esquemas de ayuda:

"La sintaxis es la siguiente: lc (expr, listas, predicados) donde expr es una expresión que se evaluará para todos los elementos de las listas, donde las listas son una o más listas con nombre, donde se especifican mediante un nombre y un nombre de expresión = list_expr, y donde los predicados son expresiones que deben evaluarse a un valor booleano. Por ejemplo, para obtener una lista de todos los números pares, al cuadrado, de una lista x podemos escribir lc (x ** 2, x = x, x% % 2 == 0). El resultado de una llamada a lc es una lista construida a partir de las expresiones en expr, para todos los elementos en las listas de entrada donde los predicados se evalúan como verdaderos ".

donde observe que puede dejar los predicados vacíos, por ejemplo, en el ejemplo anterior.

Itertools y enumeraciones de estilo Python

Puede usar las herramientas de iteración de R que son muy similares a las herramientas de iteración de Python, más adelante en Cran aquí

library(itertools)

donde se describe

"Varias herramientas para la creación de iteradores, muchas con patrones de funciones en el módulo itertools de Python, y otras con patrones de funciones en el paquete 'snow'".

Ejemplo. enumeración

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

Ejemplo. enumeración con 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"
hhh
fuente
3

zipy enumerateno son particularmente difíciles de implementar en R:

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

Enumerar es simple de definir en términos de zip:

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

Dado que estas son funciones adecuadas, podemos utilizarlas ...para hacerlas bastante flexibles y concisas, y aprovechar el comportamiento de mapply, como reciclar entradas y nombrar las salidas correctamente.

Neal Fultz
fuente
1
Estos se han agregado al stackoverflowpaquete, fwiw.
Neal Fultz
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
    })
}
Hoja
fuente
Proporcione una descripción sobre lo que hace este bloque de código.
Keivan Esbati
esta función hace exactamente lo mismo con "mapply (list, x, y, SIMPLIFY = F)" que @chl señaló
ibilgen
0

Esto se puede lograr usando dos declaraciones de pegado:

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

A la salida le gustará lo siguiente:

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