¿Cómo Utilizar Correctamente las Listas en R?


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

  • String (una secuencia compuesta de caracteres)

  • List (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, la primera dos se implementan como character y vector, respectivamente.

Cuando empecé a aprender R, dos cosas eran obvias casi desde el principio: list es el tipo de datos más importante en R (porque es la clase padre para el R data.frame), y segundo, 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 tipo de datos de R list era una implementación directa del mapa ADT (dictionary en Python, NSMutableDictionary en Objective C, hash en Perl y Ruby, object literal en Javascript, y así sucesivamente).

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

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

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

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 R list s también son a diferencia de otros ADT tipo mapa (de entre los idiomas que he aprendido de todos modos). Mi conjetura es que esto es una consecuencia de la especificación inicial para S, es decir, una intención de diseñar un DSL de datos/estadísticas [lenguaje específico de dominio] desde cero.

tres diferencias significativas entre R lists y tipos de mapeo en otros idiomas de uso generalizado (por ejemplo,. Python, Perl, JavaScript):

primero, lists en R son un ordenado colección, al igual que los vectores, a pesar de que los valores están codificados (es decir, las claves pueden ser cualquier valor hashable no solo números enteros secuenciales). Casi siempre, el tipo de datos de mapeo en otros lenguajes es desordenado.

segundo, lists puede devolverse desde funciones aunque nunca haya pasado un list cuando llamó a la función, y aunque la función que devolvió el list no contiene un constructor (explícito) list (Por supuesto, puede tratar con esto en la práctica 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

A tercera característica peculiar de los R list s: no parece que puedan ser miembros de otro ADT, y si intentas hacer eso, entonces el contenedor primario es coaccionado a un list. Por ejemplo,

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 la estructura de datos list o cómo se comporta. Todo lo que busco es corregir es mi comprensión de cómo funcionan para que pueda usarlos correctamente en mi código.

Estas son las cosas que me gustaría entender mejor:{[44]]}

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

  • Si no asigno nombres explícitamente a un list (por ejemplo, list(10,20,30,40)) ¿los nombres predeterminados son enteros secuenciales que comienzan con 1? (Supongo, pero estoy lejos de estar seguro que la respuesta es sí, de lo contrario no seríamos capaces de coaccionar este tipo de list a un vector w/ 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 lo mismo resultado?

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

    x2 = list(1:4)

Por favor, no me apunte a la documentación de R (?list, R-intro)--Lo he leído cuidadosamente y no me ayuda a responder el tipo de preguntas que recité justo arriba.

(por último, recientemente me enteré y comencé a usar un paquete R (disponible en CRAN) llamado hash que implementa comportamiento convencional tipo mapa a través de una clase S4; sin duda puedo recomendar esto Paquete.)

Author: Gregor, 2010-01-12

11 answers

Solo para abordar la última parte de su pregunta, ya que eso realmente señala la diferencia entre a list y vector en 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. Así que puede tener una lista donde el primer elemento es un vector de caracteres, el segundo es un marco de datos, etc. En este caso, ha creado dos listas diferentes. x tiene cuatro vectores, cada uno de longitud 1. x2 tiene 1 vector de longitud 4:

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

Así que estas son listas completamente diferentes.

Las listas R son muy similares a una estructura de datos de hash map en la que cada valor de índice puede asociarse con cualquier objeto. Este es un ejemplo simple de una lista que contiene 3 clases diferentes (incluyendo 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 llamarlo así:

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

Como final comentario sobre esto: debe tenerse en cuenta que a data.frame es realmente una lista (de la documentación data.frame):

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

Es por eso que las columnas en un data.frame pueden tener diferentes tipos de datos, mientras que las columnas en una matriz no pueden. Como ejemplo, aquí trato de 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 cómo no puedo cambiar el tipo de datos en la primera columna a numérico porque la segunda columna tiene caracteres:

> d[,1] <- as.numeric(d[,1])
> class(d[,1])
[1] "character"
 132
Author: Shane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-17 08:50:55

Con respecto a sus preguntas, permítanme abordarlas en orden y dar algunos ejemplos:

1) Se devuelve una lista si y cuando la instrucción return agrega una. Considere

 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 se establecen:

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

3) No devuelven lo mismo. Su 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 el mismo que x. Cada escalar es un vector de longitud uno. Por otro lado x[[1]] devuelve el primer elemento de la lista.

4) Por último, los dos son diferentes entre ellos crean, respectivamente, una lista que contiene cuatro escalares y una lista con un solo elemento (que pasa a ser un vector de cuatro elementos).

 57
Author: Dirk Eddelbuettel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-12 20:02:30

Solo para tomar un subconjunto de sus preguntas:

Este artículo sobre indizació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 solo entero, pero x[[1]] devuelve un solo 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
 34
Author: JD Long,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-12 17:35:20

Una razón por la 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, lo que los vectores no hacen. Las listas se reutilizan para una variedad de propósitos en R, incluyendo formar 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 añadir a la respuesta de @Shane, si quieres obtener el mismo resultado, try:

x3 = as.list(1:4)

Que coacciona el vector 1:4 en una lista.

 12
Author: Alex Brown,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-12 18:19:46

Solo para añadir un punto más a esto:

R tiene una estructura de datos equivalente al dict de Python en el paquete hash . Puedes leer sobre ello en esta entrada del blog del Grupo de Datos Abiertos. He aquí un ejemplo sencillo:

> library(hash)
> h <- hash( keys=c('foo','bar','baz'), values=1:3 )
> h[c('foo','bar')]
<hash> containing 2 key-value pairs.
  bar : 2
  foo : 1

En términos de usabilidad, la clase hash es muy similar a una lista. Pero el rendimiento es mejor para grandes conjuntos de datos.

 11
Author: Shane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-17 19:14:40

Usted dice:

Para otro, las listas pueden ser devueltas de funciones a pesar de que nunca pasado en una Lista cuando llamó a la función, y aunque la función no contiene un constructor de lista, por ejemplo,

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

Y supongo que usted sugiere que esto es un problema (?). Estoy aquí para decirte por qué no es un problema : -). Su ejemplo es un poco simple, ya que cuando hace la división de cadenas, tiene una lista con elementos que tienen 1 elemento de longitud, por lo que sabe eso x[[1]] es lo mismo que unlist(x)[1]. Pero qué pasa si el resultado de strsplit devuelve resultados de diferente longitud en cada bin. Simplemente devolver un vector (frente a la lista) no.

Por ejemplo:

stuff <- c("You, me, and dupree",  "You me, and dupree",
           "He ran away, but not very far, and not very fast")
x <- strsplit(stuff, ",")
xx <- unlist(strsplit(stuff, ","))

En el primer caso (x : que devuelve una lista), puedes decir cuál era la 2da "parte" de la 3ra cadena, por ejemplo: x[[3]][2]. ¿Cómo podrías hacer lo mismo usando xx ahora que los resultados han sido "desenredados" (unlist-ed)?

 8
Author: Steve Lianoglou,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-27 17:33:13
x = list(1, 2, 3, 4)
x2 = list(1:4)
all.equal(x,x2)

No es lo mismo porque 1: 4 es lo mismo que c (1,2,3,4). Si quieres que sean los mismos entonces:

x = list(c(1,2,3,4))
x2 = list(1:4)
all.equal(x,x2)
 5
Author: JeremyS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-13 05:28:34

Respecto a los vectores y el concepto hash/array de otros lenguajes:

  1. Los vectores son los átomos de R. Eg, rpois(1e4,5) (5 números aleatorios), numeric(55) (longitud-55 vector cero sobre dobles), y character(12) (12 cadenas vacías), son todos "básicos".

  2. Cualquiera de las listas o vectores pueden tener names.

    > n = numeric(10)
    > n
     [1] 0 0 0 0 0 0 0 0 0 0
    > names(n)
    NULL
    > names(n) = LETTERS[1:10]
    > n
    A B C D E F G H I J 
    0 0 0 0 0 0 0 0 0 0
    
  3. Los vectores requieren que todo sea del mismo tipo de datos. Mira esto:

    > i = integer(5)
    > v = c(n,i)
    > v
    A B C D E F G H I J           
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
    > class(v)
    [1] "numeric"
    > i = complex(5)
    > v = c(n,i)
    > class(v)
    [1] "complex"
    > v
       A    B    C    D    E    F    G    H    I    J                          
    0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i 0+0i
    
  4. Las listas pueden contener diferentes tipos de datos, como se ve en otras respuestas y la propia pregunta de la OP.

He visto lenguajes (ruby, javascript) en los que "arrays" pueden contener tipos de datos variables, pero por ejemplo en C++ "arrays" deben ser todos del mismo tipo de datos. Creo que esto es una cosa de velocidad / eficiencia: si tienes un numeric(1e6) sabes su tamaño y la ubicación de cada elemento a priori ; si la cosa podría contener "Flying Purple People Eaters" en algún segmento desconocido, entonces tienes que analizar realmente las cosas para conocer los hechos básicos sobre se.

Ciertas operaciones R estándar también tienen más sentido cuando el tipo está garantizado. Por ejemplo, cumsum(1:9) tiene sentido, mientras que cumsum(list(1,2,3,4,5,'a',6,7,8,9)) no, sin que se garantice que el tipo sea doble.


En cuanto a su segunda pregunta:

Las listas se pueden devolver desde funciones aunque nunca haya pasado una Lista cuando llamó a la función{[16]]}

Las funciones devuelven diferentes tipos de datos de los que se introducen todo el tiempo. plot devuelve un gráfico aunque no toma una trama como entrada. Arg devuelve un numeric aunque aceptó un complex. Sucesivamente.

(Y en cuanto a strsplit: el código fuente es aquí.)

 1
Author: isomorphismes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-07 16:38:48

Si ayuda, tiendo a concebir "listas" en R como "registros"en otros lenguajes pre-OO:

  • no hacen ninguna suposición sobre un tipo global (o más bien el tipo de todos los registros posibles de cualquier arity y nombres de campo está disponible).
  • sus campos pueden ser anónimos (luego se accede a ellos por estricto orden de definición).

El nombre "registro" chocaría con el significado estándar de" registros " (también conocido como filas) en el lenguaje de la base de datos, y puede ser que esta sea la razón por la que su nombre se sugiere: como listas (de campos).

 1
Author: Francisco J. Valverde Albacete,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-03 09:39:24

Aunque esta es una pregunta bastante antigua, debo decir que está tocando exactamente el conocimiento que me faltaba durante mis primeros pasos en R, es decir, cómo expresar datos en mi mano como objeto en R o cómo seleccionar entre objetos existentes. No es fácil para un novato de R pensar" en una caja de R " desde el principio.

Así que yo mismo comencé a usar muletas debajo, lo que me ayudó mucho a averiguar qué objeto usar para qué datos, y básicamente a imaginar el uso en el mundo real.

Aunque no dar respuestas exactas a la pregunta el texto corto a continuación podría ayudar al lector que acaba de comenzar con R y está haciendo preguntas simmilares.

  • Vector atómico ... Lo llamé "secuencia" para mí, sin dirección, solo secuencia de los mismos tipos. [ subconjuntos.
  • Vector ... secuencia con una dirección a partir de subconjuntos 2D, [.
  • Matriz ... montón de vectores con la misma longitud formando filas o columnas, [ subconjuntos por filas y columnas, o por secuencia.
  • Matrices ... matrices en capas que forman 3D
  • Dataframe ... una tabla 2D como en Excel, donde puedo ordenar, agregar o eliminar filas o columnas o hacer arit. operaciones con ellos, solo después de algún tiempo realmente reconocí que dataframe es una implementación inteligente de list donde puedo subconjunto usando [ por filas y columnas, pero incluso usando [[.
  • List ... para ayudarme a mí mismo pensé en la lista como de tree structure donde [i] selecciona y devuelve ramas enteras y [[i]] devuelve elemento de la rama. Y porque si es tree like structure, incluso puede usar un index sequence para abordar cada hoja en un list muy complejo usando su [[index_vector]]. Las listas pueden ser simples o muy complejas y pueden mezclar varios tipos de objetos en uno.

Así que para lists puede terminar con más formas de cómo seleccionar un leaf dependiendo de la situación como en el siguiente ejemplo.

l <- list("aaa",5,list(1:3),LETTERS[1:4],matrix(1:9,3,3))
l[[c(5,4)]] # selects 4 from matrix using [[index_vector]] in list
l[[5]][4] # selects 4 from matrix using sequential index in matrix
l[[5]][1,2] # selects 4 from matrix using row and column in matrix

Esta forma de pensar me ayudó mucho.

 1
Author: Petr Matousu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-20 14:54:23

¿por Qué estos dos operadores diferentes, [ ], y [[ ]], devuelven el mismo resultado?

x = list(1, 2, 3, 4)
  1. [ ] proporciona una operación de subajuste. En general subconjunto de cualquier objeto tendrá el mismo tipo que el objeto original. Por lo tanto, x[1] proporciona una lista. Del mismo modo x[1:2] es un subconjunto de la lista original, por lo tanto, es una lista. Ex.

    x[1:2]
    
    [[1]] [1] 1
    
    [[2]] [1] 2
    
  2. [[ ]] es para extraer un elemento de la lista. x[[1]] es válido y extraer el primer elemento de la lista. x[[1:2]] no es válido como [[ ]] no proporciona una configuración secundaria como [ ].

     x[[2]] [1] 2 
    
    > x[[2:3]] Error in x[[2:3]] : subscript out of bounds
    
 1
Author: HariG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-17 08:57:05