¿Cómo utilizar correctamente las listas?

Resuelto doug asked hace 54 años • 13 respuestas

Breve historia: Muchos (¿la mayoría?) lenguajes de programación contemporáneos de uso generalizado tienen al menos un puñado de ADT [tipos de datos abstractos] en común, en particular,

  • cadena (una secuencia compuesta de caracteres)

  • lista (una colección ordenada de valores), y

  • tipo basado en mapas (una matriz desordenada que asigna claves a valores)

En el lenguaje de programación R, los dos primeros se implementan como charactery vector, respectivamente.

Cuando comencé a aprender R, dos cosas eran obvias casi desde el principio: listes el tipo de datos más importante en R (porque es la clase principal de R data.frame), y en segundo lugar, simplemente no podía entender cómo funcionaban, al menos no lo suficientemente bien como para usarlos correctamente en mi código.

Por un lado, me pareció que el listtipo de datos de R era una implementación sencilla del mapa ADT ( dictionaryen Python, NSMutableDictionaryen Objective C, hashen Perl y Ruby, object literalen Javascript, etc.).

Por ejemplo, los creas tal como lo harías con un diccionario de Python, pasando pares clave-valor a un constructor (que en Python dictno lo es list):

x = list("ev1"=10, "ev2"=15, "rv"="Group 1")

Y accede a los elementos de una Lista R tal como lo haría con los de un diccionario de Python, por ejemplo, x['ev1']. Del mismo modo, puede recuperar solo las 'claves' o solo los 'valores' de la siguiente manera:

names(x)    # fetch just the 'keys' of an R list
# [1] "ev1" "ev2" "rv"

unlist(x)   # fetch just the 'values' of an R list
#   ev1       ev2        rv 
#  "10"      "15" "Group 1" 

x = list("a"=6, "b"=9, "c"=3)  

sum(unlist(x))
# [1] 18

pero los R lists también son diferentes a otros ADT de tipo mapa (de todos modos, entre los lenguajes que he aprendido). Supongo que esto es una consecuencia de la especificación inicial de S, es decir, la intención de diseñar un DSL [lenguaje específico de dominio] de datos/estadísticas desde cero.

Tres diferencias significativas entre R listy los tipos de mapeo en otros lenguajes de uso generalizado (por ejemplo, Python, Perl, JavaScript):

Primero , lists en R son una colección ordenada , al igual que los vectores, aunque los valores tengan clave (es decir, las claves pueden ser cualquier valor hash, no solo números enteros secuenciales). Casi siempre, el tipo de datos de mapeo en otros idiomas está desordenado .

segundo , lists puede ser devuelto desde funciones incluso aunque nunca hayas pasado a listcuando llamaste a la función, y aunque la función que devolvió listno contenga un listconstructor (explícito) (por supuesto, puedes lidiar con esto en la práctica al envolviendo el resultado devuelto en una llamada a unlist):

x = strsplit(LETTERS[1:10], "")     # passing in an object of type 'character'

class(x)                            # returns 'list', not a vector of length 2
# [1] list

Una tercera característica peculiar de listlos s de R: no parece que puedan ser miembros de otro ADT, y si intentas hacerlo, el contenedor principal se ve obligado a convertirse en un list. P.ej,

x = c(0.5, 0.8, 0.23, list(0.5, 0.2, 0.9), recursive=TRUE)

class(x)
# [1] list

Mi intención aquí no es criticar el lenguaje o cómo está documentado; Del mismo modo, no estoy sugiriendo que haya nada malo con ellist estructura de datos o con su comportamiento. Lo único que busco es corregir mi comprensión de cómo funcionan para poder usarlos correctamente en mi código.

Estos son los tipos de cosas que me gustaría entender mejor:

  • ¿ Cuáles son las reglas que determinan cuándo una llamada a una función devolverá un list(por ejemplo, strsplitla expresión mencionada anteriormente)?

  • Si no asigno explícitamente nombres a list(por ejemplo, list(10,20,30,40)), ¿los nombres predeterminados son solo números enteros secuenciales que comienzan con 1? (Supongo, pero estoy lejos de estar seguro de que la respuesta sea sí; de lo contrario, no podríamos forzar este tipo de lista un vector con una llamada a unlist).

  • ¿Por qué estos dos operadores diferentes, []y [[]], devuelven el mismo resultado?

    x = list(1, 2, 3, 4)

    ambas expresiones devuelven "1":

    x[1]

    x[[1]]

  • ¿Por qué estas dos expresiones no devuelven el mismo resultado?

    x = list(1, 2, 3, 4)

    x2 = list(1:4)

Por favor, no me indique la documentación de R ( ?list,R-intro ). La he leído detenidamente y no me ayuda a responder el tipo de preguntas que mencioné anteriormente.

(Por último, recientemente me enteré y comencé a usar un paquete R (disponible en CRAN) llamado hashque implementa un comportamiento de tipo mapa convencional a través de una clase S4; ciertamente puedo recomendar este paquete).

doug avatar Jan 01 '70 08:01 doug
Aceptado

Sólo para abordar la última parte de su pregunta, ya que eso realmente señala la diferencia entre a listy vectoren R:

¿Por qué estas dos expresiones no devuelven el mismo resultado?

x = lista(1, 2, 3, 4); x2 = lista(1:4)

Una lista puede contener cualquier otra clase como cada elemento. Entonces puedes tener una lista donde el primer elemento es un vector de caracteres, el segundo es un marco de datos, etc. En este caso, has creado dos listas diferentes. xtiene cuatro vectores, cada uno de longitud 1. x2tiene 1 vector de longitud 4:

> length(x[[1]])
[1] 1
> length(x2[[1]])
[1] 4

Entonces estas son listas completamente diferentes.

Las listas R se parecen mucho a una estructura de datos de mapa hash en el sentido de que cada valor de índice se puede asociar con cualquier objeto. Aquí hay un ejemplo simple de una lista que contiene 3 clases diferentes (incluida una función):

> complicated.list <- list("a"=1:4, "b"=1:3, "c"=matrix(1:4, nrow=2), "d"=search)
> lapply(complicated.list, class)
$a
[1] "integer"
$b
[1] "integer"
$c
[1] "matrix"
$d
[1] "function"

Dado que el último elemento es la función de búsqueda, puedo llamarla así:

> complicated.list[["d"]]()
[1] ".GlobalEnv" ...

Como comentario final sobre esto: cabe señalar que a data.framees realmente una lista (de la data.framedocumentación):

Un marco de datos es una lista de variables del mismo número de filas con nombres de fila únicos, dada la clase '"data.frame"'

Es por eso que las columnas de una matriz data.framepueden tener diferentes tipos de datos, mientras que las columnas de una matriz no. A modo de ejemplo, aquí intento crear una matriz con números y caracteres:

> a <- 1:4
> class(a)
[1] "integer"
> b <- c("a","b","c","d")
> d <- cbind(a, b)
> d
 a   b  
[1,] "1" "a"
[2,] "2" "b"
[3,] "3" "c"
[4,] "4" "d"
> class(d[,1])
[1] "character"

Tenga en cuenta que no puedo cambiar el tipo de datos de la primera columna a numérico porque la segunda columna tiene caracteres:

> d[,1] <- as.numeric(d[,1])
> class(d[,1])
[1] "character"
Shane avatar Jan 12 '2010 18:01 Shane

En cuanto a tus preguntas, déjame abordarlas en orden y darte algunos ejemplos:

1 ) Se devuelve una lista si la declaración de devolución agrega una. Considerar

 R> retList <- function() return(list(1,2,3,4)); class(retList())
 [1] "list"
 R> notList <- function() return(c(1,2,3,4)); class(notList())
 [1] "numeric"
 R> 

2 ) Los nombres simplemente no están establecidos:

R> retList <- function() return(list(1,2,3,4)); names(retList())
NULL
R> 

3 ) No devuelven lo mismo. Tu ejemplo da

R> x <- list(1,2,3,4)
R> x[1]
[[1]]
[1] 1
R> x[[1]]
[1] 1

donde x[1]devuelve el primer elemento de x-- que es lo mismo que x. Todo escalar es un vector de longitud uno. Por otro lado x[[1]]devuelve el primer elemento de la lista.

4 ) Por último, los dos se diferencian entre sí: crean, respectivamente, una lista que contiene cuatro escalares y una lista con un solo elemento (que resulta ser un vector de cuatro elementos).

Dirk is no longer here avatar Jan 12 '2010 17:01 Dirk is no longer here

Solo para tomar un subconjunto de sus preguntas:

Este artículo sobre indexación aborda la cuestión de la diferencia entre []y [[]].

En resumen, [[]] selecciona un solo elemento de una lista y []devuelve una lista de los elementos seleccionados. En su ejemplo, x = list(1, 2, 3, 4)'el elemento 1 es un número entero único pero x[[1]]devuelve un único 1 y x[1]devuelve una lista con un solo valor.

> x = list(1, 2, 3, 4)
> x[1]
[[1]]
[1] 1

> x[[1]]
[1] 1
JD Long avatar Jan 12 '2010 17:01 JD Long

Una de las razones por las que las listas funcionan como lo hacen (ordenadas) es para abordar la necesidad de un contenedor ordenado que pueda contener cualquier tipo en cualquier nodo, algo que los vectores no hacen. Las listas se reutilizan para una variedad de propósitos en R, incluida la formación de la base de a data.frame, que es una lista de vectores de tipo arbitrario (pero de la misma longitud).

¿Por qué estas dos expresiones no devuelven el mismo resultado?

x = list(1, 2, 3, 4); x2 = list(1:4)

Para agregar a la respuesta de @Shane, si desea obtener el mismo resultado, intente:

x3 = as.list(1:4)

Lo que obliga al vector 1:4a formar una lista.

Alex Brown avatar Jan 12 '2010 18:01 Alex Brown