Breve reseña: 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 character
y vector
, respectivamente.
Cuando comencé 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 principal para 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 list
tipo de datos de R era una implementación directa del mapa ADT ( dictionary
en Python, NSMutableDictionary
en el Objetivo C, hash
en Perl y Ruby, object literal
en Javascript, etc.).
Por ejemplo, los crea como si fuera un diccionario de Python, pasando pares clave-valor a un constructor (que en Python dict
no lo es list
):
x = list("ev1"=10, "ev2"=15, "rv"="Group 1")
Y se accede a los elementos de una lista de R al igual que lo haría con las de un diccionario de Python, por ejemplo, x['ev1']
. Del mismo modo, puede recuperar solo las 'claves' o solo los 'valores' al:
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 list
también son diferentes a otros ADT de tipo mapa (de todos 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 list
sy tipos de mapeo en otros idiomas en uso generalizado (p. ej., Python, Perl, JavaScript):
primero , list
s en R son una colección ordenada , 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 enteros secuenciales). Casi siempre, el tipo de datos de mapeo en otros idiomas no está ordenado .
segundo , list
s puede ser devuelto desde funciones aunque nunca haya pasado a list
cuando llamó a la función, y aunque la función que devolvió el list
no contiene un list
constructor ( explícito) (Por supuesto, puede tratar esto en la práctica mediante 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 las R list
: no parece que puedan ser miembros de otro ADT, y si intenta hacerlo, el contenedor primario se convierte en a 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 algo mal con la list
estructura de datos o cómo se comporta. Todo lo que busco es corregir mi comprensión de cómo funcionan para poder usarlos correctamente en mi código.
Estas son las cosas que me gustaría entender mejor:
¿Cuáles son las reglas que determinan cuándo una llamada de función devolverá un
list
(p. Ej.,strsplit
Expresión mencionada anteriormente)?Si no asigno explícitamente nombres a
list
(p. Ej.list(10,20,30,40)
) , ¿ Son los nombres predeterminados solo enteros secuenciales que comienzan con 1? (Asumo, pero no estoy seguro de que la respuesta sea sí, de lo contrario no podríamos obligar a este tipolist
a un vector con una llamada aunlist
).¿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 señale la Documentación R ( ?list
, R-intro
): la he leído detenidamente y no me ayuda a responder el tipo de preguntas que recité anteriormente.
(por último, recientemente me enteré y comencé a usar un paquete R (disponible en CRAN) llamado hash
que implementa el comportamiento de tipo de mapa convencional a través de una clase S4; ciertamente puedo recomendar este paquete).
x = list(1, 2, 3, 4)
, ambos NO devuelven el mismo resultado:x[1]
yx[[1]]
. El primero devuelve una lista y el segundo devuelve un vector numérico. Al desplazarme a continuación, me parece que Dirk fue el único encuestado que respondió esta pregunta correctamente.list
en que R no es como un hash. Tengo uno más que creo que es digno de mención.list
en R puede tener dos miembros con el mismo nombre de referencia. Considere queobj <- c(list(a=1),list(a=2))
es válido y devuelve una lista con dos valores con nombre de 'a'. En este caso, una solicitudobj["a"]
solo devolverá el primer elemento de la lista coincidente. Puede obtener un comportamiento similar (tal vez idéntico) a un hash con solo un elemento por cada nombre de referencia utilizando entornos en R. ej .x <- new.env(); x[["a"]] <- 1; x[["a"]] <- 2; x[["a"]]
Respuestas:
Sólo para hacer frente a la última parte de su pregunta, ya que realmente señala la diferencia entre una
list
yvector
en I:Una lista puede contener cualquier otra clase como cada elemento. Entonces 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:Entonces estas son listas completamente diferentes.
Las listas R son muy parecidas a una estructura de datos de mapa hash en que cada valor de índice puede asociarse con cualquier objeto. Aquí hay un ejemplo simple de una lista que contiene 3 clases diferentes (incluida una función):
Dado que el último elemento es la función de búsqueda, puedo llamarlo así:
Como comentario final sobre esto: debe tenerse en cuenta que a
data.frame
es realmente una lista (de ladata.frame
documentación):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:Tenga en cuenta que no puedo cambiar el tipo de datos en la primera columna a numérico porque la segunda columna tiene caracteres:
fuente
switch
función útil en R que se puede usar para ese propósito (verhelp(switch)
).Con respecto a sus preguntas, permítame abordarlas en orden y dar algunos ejemplos:
1 ) Se devuelve una lista si y cuando la instrucción return agrega uno. Considerar
2 ) Los nombres simplemente no se establecen:
3 ) No devuelven lo mismo. Tu ejemplo da
donde
x[1]
devuelve el primer elemento dex
, que es lo mismo quex
. 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 resulta ser un vector de cuatro elementos).
fuente
Value
. Como en?strsplit
: "Una lista de la misma longitud que x". Pero debe considerar que puede haber una función que devuelva diferentes valores dependiendo de los argumentos (por ejemplo, sapply puede devolver una lista o un vector).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, elx = list(1, 2, 3, 4)'
elemento 1 es un entero único, perox[[1]]
devuelve un solo 1 yx[1]
devuelve una lista con un solo valor.fuente
A = array( 11:16, c(2,3) ); A[5]
es 15, en la matriz plana ?!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, 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?
Para agregar a la respuesta de @ Shane, si desea obtener el mismo resultado, intente:
Lo que obliga al vector a
1:4
formar una lista.fuente
Solo para agregar un punto más a esto:
R tiene una estructura de datos equivalente al dict de Python en el
hash
paquete . Puede leer sobre esto en esta publicación de blog del Grupo de datos abiertos . Aquí hay un ejemplo simple:En términos de usabilidad, la
hash
clase es muy similar a una lista. Pero el rendimiento es mejor para grandes conjuntos de datos.fuente
Tu dices:
Y supongo que sugieres que esto es un problema (?). Estoy aquí para decirte por qué no es un problema :-). Su ejemplo es un poco simple, ya que cuando realiza la división de cadenas, tiene una lista con elementos que tienen 1 elemento de largo, por lo que sabe que
x[[1]]
es lo mismo queunlist(x)[1]
. Pero, ¿y si el resultado destrsplit
resultados devueltos de diferente longitud en cada bin. Simplemente devolver un vector (frente a una lista) no funcionará en absoluto.Por ejemplo:
En el primer caso (
x
: que devuelve una lista), se puede decir lo que la segunda "parte" de la tercera cadena era, por ejemplo:x[[3]][2]
. ¿Cómo podría hacer lo mismo usandoxx
ahora que los resultados han sido "desentrañados" (unlist
-ed)?fuente
no es lo mismo porque 1: 4 es lo mismo que c (1,2,3,4). Si quieres que sean iguales, entonces:
fuente
Esta es una pregunta muy antigua, pero creo que una nueva respuesta podría agregar algo de valor ya que, en mi opinión, nadie abordó directamente algunas de las preocupaciones en el PO.
A pesar de lo que sugieren las respuestas aceptadas, los
list
objetos en R no son mapas hash. Si quiere hacer un paralelo con python,list
es más, supongo, pythonlist
s (otuple
s en realidad).Es mejor describir cómo la mayoría de los objetos R se almacenan internamente (el tipo C de un objeto R es
SEXP
). Se componen básicamente de tres partes:NULL
si el objeto no tiene atributos).Desde un punto de vista interno, hay poca diferencia entre a
list
y unnumeric
vector, por ejemplo. Los valores que almacenan son simplemente diferentes. Dividamos dos objetos en el paradigma que describimos antes:Para
x
:numeric
(REALSXP
en el lado C), la longitud es 10 y otras cosas.double
valores.NULL
, ya que el objeto no tiene ninguno.Para
y
:list
(VECSXP
en el lado C), la longitud es 2 y otras cosas.runif(10)
yrunif(3)
respectivamente.NULL
, como parax
.Entonces, la única diferencia entre un
numeric
vector y un alist
es que lanumeric
parte de datos está hecha dedouble
valores, mientras que para lalist
parte de datos es una matriz de punteros a otros objetos R.¿Qué pasa con los nombres? Bueno, los nombres son solo algunos de los atributos que puede asignar a un objeto. Veamos el objeto a continuación:
list
(VECSXP
en el lado C), la longitud es 2 y otras cosas.1:3
yLETTERS
respectivamente.names
componente que es uncharacter
objeto R con valorc("a","b")
.Desde el nivel R, puede recuperar los atributos de un objeto con la
attributes
función.El valor clave típico de un mapa hash en R es solo una ilusión. Cuando tu dices:
esto es lo que pasa:
[[
función de subconjunto se llama;"a"
) es de tipocharacter
, por lo que se le indica al método que busque dicho valor desde elnames
atributo (si está presente) del objetoz
;names
atributo no está allí,NULL
se devuelve;"a"
se busca el valor en él. Si"a"
no es un nombre del objeto,NULL
se devuelve;z[[1]]
.La búsqueda de valor clave es bastante indirecta y siempre es posicional. Además, útil para tener en cuenta:
names
en R deben ser cadenas (character
vectores);en los mapas hash no puede tener dos claves idénticas. En R, puede asignar
names
a un objeto con valores repetidos. Por ejemplo:es perfectamente válido en R. Cuando intenta
y[["same"]]
, se recupera el primer valor. Deberías saber por qué en este punto.En conclusión, la capacidad de otorgar atributos arbitrarios a un objeto le da la apariencia de algo diferente desde un punto de vista externo. Pero las R
list
no son mapas hash de ninguna manera.fuente
Con respecto a los vectores y el concepto hash / array de otros idiomas:
Los vectores son los átomos de R. Por ejemplo,
rpois(1e4,5)
(5 números aleatorios),numeric(55)
(longitud-55 cero vector sobre dobles) ycharacter(12)
(12 cadenas vacías), son todos "básicos".Pueden tener listas o vectores
names
.Los vectores requieren que todo sea del mismo tipo de datos. Ver este:
Las listas pueden contener diferentes tipos de datos, como se ve en otras respuestas y en la pregunta del OP en sí.
He visto lenguajes (ruby, javascript) en los que las "matrices" pueden contener tipos de datos variables, pero, por ejemplo, en C ++ las "matrices" deben ser del mismo tipo de datos. Creo que esto es una cuestión de velocidad / eficiencia: si tienes un
numeric(1e6)
, sabes su tamaño y la ubicación de cada elemento a priori ; si la cosa puede contener"Flying Purple People Eaters"
una porción desconocida, entonces debes analizar cosas para conocer datos básicos sobre ella.Ciertas operaciones R estándar también tienen más sentido cuando el tipo está garantizado. Por ejemplo,
cumsum(1:9)
tiene sentido mientrascumsum(list(1,2,3,4,5,'a',6,7,8,9))
que no, sin que se garantice que el tipo sea doble.En cuanto a tu segunda pregunta:
Las funciones devuelven diferentes tipos de datos de los que se ingresan todo el tiempo.
plot
devuelve una trama aunque no tome una trama como entrada.Arg
devuelve unnumeric
a pesar de que aceptó acomplex
. Etc.(Y en cuanto a
strsplit
: el código fuente está aquí ).fuente
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 de los objetos existentes. No es fácil para un novato R pensar "en una caja R" desde el principio.
Entonces, yo mismo comencé a usar muletas a continuación, lo que me ayudó mucho a descubrir qué objeto usar para qué datos, y básicamente a imaginar el uso en el mundo real.
Aunque no estoy dando respuestas exactas a la pregunta, el breve texto a continuación podría ayudar al lector que acaba de comenzar con R y hace preguntas similares.
[
subconjuntos.[
subconjuntos.[
subconjuntos por filas y columnas, o por secuencia.list
donde puedo subconjunto usando[
filas y columnas, pero incluso usando[[
.tree structure
donde[i]
selecciona y devuelve ramas enteras y[[i]]
devuelve el artículo de la rama. Y debido a que es asítree like structure
, incluso puede usar unindex sequence
para abordar cada hoja de un complejolist
usando su[[index_vector]]
. Las listas pueden ser simples o muy complejas y pueden mezclar varios tipos de objetos en uno.Entonces
lists
, puede terminar con más formas de seleccionar unaleaf
situación dependiendo de la situación, como en el siguiente ejemplo.Esta forma de pensar me ayudó mucho.
fuente
Si ayuda, tiendo a concebir las "listas" en R como "registros" en otros lenguajes pre-OO:
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 por eso que su nombre se sugirió a sí mismo: como listas (de campos).
fuente
¿Por qué estos dos operadores diferentes
[ ]
, y[[ ]]
devuelven el mismo resultado?[ ]
proporciona una operación de configuración secundaria. En general, el subconjunto de cualquier objeto tendrá el mismo tipo que el objeto original. Por lo tanto,x[1]
proporciona una lista. Del mismo modox[1:2]
es un subconjunto de la lista original, por lo tanto, es una lista. Ex.[[ ]]
es para extraer un elemento de la lista.x[[1]]
es válido y extrae el primer elemento de la lista.x[[1:2]]
no es válido ya[[ ]]
que no proporciona subconfiguración como[ ]
.fuente