Saltar al contenido principal
LibreTexts Español

7.5: Extracción de un subconjunto de un marco de datos

  • Page ID
    151598
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    En esta sección nos dirigimos a la pregunta de cómo subdividir un marco de datos en lugar de un vector. Para ello, lo primero que debo señalar es que, si todo lo que quieres hacer es subdividir una de las variables dentro del marco de datos, entonces como de costumbre el operador $ es tu amigo. Por ejemplo, supongamos que estoy trabajando con el marco de datos itng, y lo que quiero hacer es crear la lista speech.by.char. Puedo usar exactamente los mismos trucos que usé la última vez, ya que lo que realmente quiero hacer es dividir () el vector itng$utterance, usando el vector itng$speaker como variable de agrupación. Sin embargo, la mayoría de las veces lo que realmente quieres hacer es seleccionar varias variables diferentes dentro del marco de datos (es decir, mantener solo algunas de las columnas), o tal vez un subconjunto de casos (es decir, mantener solo algunas de las filas). Para entender cómo funciona esto, necesitamos hablar más específicamente sobre los marcos de datos y cómo subagruparlos.

    Uso de la función subset ()

    Hay varias formas diferentes de subdividir un marco de datos en R, algunas más fáciles que otras. Comenzaré discutiendo la función subset (), que es probablemente la forma conceptualmente más simple de hacerlo. Para nuestros propósitos hay tres argumentos diferentes que más te interesarán:

    • x. El marco de datos que desea subconjuntos.
    • subconjunto. Un vector de valores lógicos que indica qué casos (filas) del marco de datos desea conservar. Por defecto, se conservarán todos los casos.
    • seleccionar. Este argumento indica qué variables (columnas) en el marco de datos desea conservar. Esto puede ser una lista de nombres de variables, o un vector lógico que indica cuáles conservar, o incluso solo un vector numérico que contiene los números de columna relevantes. Por defecto, se conservarán todas las variables.

    Empecemos con un ejemplo en el que utilizo los tres argumentos. Supongamos que quiero subdividir el marco de datos itng, manteniendo solo las expresiones hechas por Makka-Pakka. Lo que eso significa es que necesito usar el argumento select para seleccionar la variable de enunciado, y también necesito usar la variable subconjunto, para seleccionar los casos en los que Makka-Pakka está hablando (es decir, speaker == “makka-pakka”). Por lo tanto, el comando que necesito usar es este:

    df <- subset( x = itng,                            # data frame is itng
                  subset = speaker == "makka-pakka",   # keep only Makka-Pakkas speech
                  select = utterance )                 # keep only the utterance variable
    print( df )
    ##    utterance
    ## 7        pip
    ## 8        pip
    ## 9        onk
    ## 10       onk

    La variable df aquí sigue siendo un marco de datos, pero solo contiene una variable (llamada enunciación) y cuatro casos. Observe que los números de fila son en realidad los mismos del marco de datos original. Vale la pena tomarse un momento para explicar brevemente esto. La razón por la que esto sucede es que estos “números de fila” son en realidad nombres de fila. Cuando crea un nuevo marco de datos desde cero, R asignará a cada fila un nombre de fila bastante aburrido, que es idéntico al número de fila. Sin embargo, cuando subconjuntos el marco de datos, cada fila mantiene su nombre de fila original. Esto puede ser bastante útil, ya que —como en el ejemplo actual— te proporciona un recordatorio visual de a qué corresponde cada fila del nuevo marco de datos en el marco de datos original. Sin embargo, si le molesta, puede cambiar los nombres de las filas usando la función rownames (). 113

    En cualquier caso, volvamos a la función subset (), y veamos qué sucede cuando no usamos los tres argumentos. En primer lugar, supongamos que no me molesté en especificar el argumento select. Veamos qué pasa:

    subset( x = itng,
            subset = speaker == "makka-pakka" )
    
    ##        speaker utterance
    ## 7  makka-pakka       pip
    ## 8  makka-pakka       pip
    ## 9  makka-pakka       onk
    ## 10 makka-pakka       onk

    No es sorprendente que R haya mantenido los mismos casos del conjunto de datos original (es decir, las filas 7 a 10), pero esta vez ha mantenido todas las variables del marco de datos. Igualmente, como era de esperar, si no especifico el argumento subconjunto, lo que encontramos es que R guarda todos los casos:

    subset( x = itng, 
             select = utterance )
    
    ##    utterance
    ## 1        pip
    ## 2        pip
    ## 3        onk
    ## 4        onk
    ## 5         ee
    ## 6         oo
    ## 7        pip
    ## 8        pip
    ## 9        onk
    ## 10       onk

    Nuevamente, es importante tener en cuenta que esta salida sigue siendo un marco de datos: es solo un marco de datos con una sola variable.

    Uso de corchetes: I. Filas y columnas

    A lo largo del libro hasta ahora, cada vez que he estado subestableciendo un vector he tendido a usar los corchetes [] para hacerlo. Pero en la sección anterior cuando comencé a hablar de subestablecer un marco de datos usé la función subset (). Como consecuencia, tal vez se pregunte si es posible usar los corchetes para crear un subconjunto de un marco de datos. La respuesta, por supuesto, es sí. No solo puedes usar corchetes para este propósito, a medida que te familiarices con R encontrarás que esto es en realidad mucho más conveniente que usar subset (). Desafortunadamente, el uso de corchetes para este propósito es algo complicado, y puede resultar muy confuso para los novatos. Así que ten cuidado: esta sección es más complicada de lo que parece que “debería” ser. Con esa advertencia en su lugar, voy a tratar de guiarte a través de ella lentamente. Para esta sección, usaré un conjunto de datos ligeramente diferente, a saber, el marco de datos de jardín que se almacena en el archivo “NightGarden2.rData”.

    load("./rbook-master/data/nightgarden2.Rdata" )
    garden
    ##            speaker utterance line
    ## case.1  upsy-daisy       pip    1
    ## case.2  upsy-daisy       pip    2
    ## case.3   tombliboo        ee    5
    ## case.4 makka-pakka       pip    7
    ## case.5 makka-pakka       onk    9

    Como puede ver, el marco de datos del jardín contiene 3 variables y 5 casos, y esta vez he usado la función rownames () para adjuntar etiquetas ligeramente verbosas a cada uno de los casos. Además, supongamos que lo que queremos hacer es escoger las filas 4 y 5 (los dos casos cuando Makka-Pakka está hablando), y las columnas 1 y 2 (variables altavoz y enunciado).

    ¿Cómo vamos a hacer esto? Como de costumbre, hay más de una manera. La primera forma se basa en la observación de que, dado que un marco de datos es básicamente una tabla, cada elemento del marco de datos tiene un número de fila y un número de columna. Entonces, si queremos escoger un solo elemento, tenemos que especificar el número de fila y un número de columna dentro de los corchetes. Por convención, el número de fila viene primero. Entonces, para el marco de datos anterior, que tiene 5 filas y 3 columnas, el esquema de indexación numérica se ve así:

    knitr::kable(data.frame(stringsAsFactors=FALSE, row = c("1","2","3", "4", "5"), col1 = c("[1,1]", "[2,1]", "[3,1]", "[4,1]", 
    "[5,1]"), col2 = c("[1,2]", "[2,2]", "[3,2]", "[4,2]", "[5,2]"), col3 = c("[1,3]", "[2,3]", "[3,3]", "[4,3]", "[5,3]")))
    
    fila col1 col2 col3
    1 [1,1] [1,2] [1,3]
    2 [2,1] [2,2] [2,3]
    3 [3,1] [3,2] [3,3]
    4 [4,1] [4,2] [4,3]
    5 [5,1] [5,2] [5,3]

    Si quiero el 3er caso de la 2da variable, lo que escribiría es garden [3,2], y R imprimiría alguna salida mostrando que, este elemento corresponde a la enunciación “ee”. Sin embargo, vamos a dejar de hacerlo realmente por un momento, porque hay algo ligeramente contradictorio en los detalles de lo que R hace en esas circunstancias (ver Sección 7.5.4). En cambio, apuntemos a resolver nuestro problema original, que es sacar dos filas (4 y 5) y dos columnas (1 y 2). Esto es bastante sencillo de hacer, ya que R nos permite especificar múltiples filas y múltiples columnas. Así que intentemos eso:

    garden[ 4:5, 1:2 ]
    ##            speaker utterance
    ## case.4 makka-pakka       pip
    ## case.5 makka-pakka       onk

    Claramente, eso es exactamente lo que pedimos: la salida aquí es un marco de datos que contiene dos variables y dos casos. Tenga en cuenta que podría haber obtenido la misma respuesta si hubiera usado la función c () para producir mis vectores en lugar del operador:. Es decir, el siguiente comando equivale al último:

    garden[ c(4,5), c(1,2) ]
    ##            speaker utterance
    ## case.4 makka-pakka       pip
    ## case.5 makka-pakka       onk
    

    Simplemente no es tan bonito. Sin embargo, si las columnas y filas que quieres conservar no resultan estar una al lado de la otra en el marco de datos original, entonces podrías encontrar que tienes que recurrir al uso de comandos como garden [c (2,4,5), c (1,3)] para extraerlos.

    Una segunda forma de hacer lo mismo es usar los nombres de las filas y columnas. Es decir, en lugar de usar los números de fila y los números de columna, se utilizan las cadenas de caracteres que se utilizan como etiquetas para las filas y columnas. Para aplicar esta idea a nuestro marco de datos de jardín, usaríamos un comando como este:

    garden[ c("case.4", "case.5"), c("speaker", "utterance") ]
    ##            speaker utterance
    ## case.4 makka-pakka       pip
    ## case.5 makka-pakka       onk

    Una vez más, esto produce exactamente la misma salida, así que no me he molestado en mostrarlo. Tenga en cuenta que, aunque esta versión es más molesta de escribir que la versión anterior, es un poco más fácil de leer, porque a menudo es más significativo referirse a los elementos por sus nombres en lugar de por sus números. También tenga en cuenta que no es necesario usar la misma convención para las filas y columnas. Por ejemplo, a menudo encuentro que los nombres de las variables son significativos y así a veces me refiero a ellos por su nombre, mientras que los nombres de las filas son bastante arbitrarios por lo que es más fácil referirse a ellos por número. De hecho, eso es más o menos exactamente lo que sucede con el marco de datos del jardín, por lo que probablemente tenga más sentido usar esto como comando:

    garden[ 4:5, c("speaker", "utterance") ]
    ##            speaker utterance
    ## case.4 makka-pakka       pip
    ## case.5 makka-pakka       onk
    

    Nuevamente, la salida es idéntica.

    Finalmente, tanto las filas como las columnas se pueden indexar usando vectores lógicos también. Por ejemplo, aunque antes afirmé que mi objetivo era extraer los casos 4 y 5, es bastante obvio que lo que realmente quería hacer era seleccionar los casos en los que habla Makka-Pakka. Entonces lo que podría haber hecho es crear un vector lógico que indique qué casos corresponden a Makka-Pakka hablando:

    is.MP.speaking <- garden$speaker == "makka-pakka"
    is.MP.speaking
    ## [1] FALSE FALSE FALSE  TRUE  TRUE

    Como puede ver, los elementos 4º y 5º de este vector son VERDADEROS mientras que los demás son FALSOS. Ahora que he construido esta variable “indicadora”, lo que puedo hacer es usar este vector para seleccionar las filas que quiero conservar:

    garden[ is.MP.speaking, c("speaker", "utterance") ]
    ##            speaker utterance
    ## case.4 makka-pakka       pip
    ## case.5 makka-pakka       onk

    Y claro que la salida es, una vez más, la misma.

    Uso de corchetes: II. Algunas elaboraciones

    Hay dos elaboraciones bastante útiles sobre este enfoque de “filas y columnas” que debo señalar. En primer lugar, ¿y si quieres conservar todas las filas, o todas las columnas? Para ello, todo lo que tenemos que hacer es dejar en blanco la entrada correspondiente, ¡pero es crucial recordar mantener la coma*! Por ejemplo, supongamos que quiero mantener todas las filas en los datos del jardín, pero solo quiero conservar las dos primeras columnas. La forma más fácil de hacer esto es usar un comando como este:

    garden[ , 1:2 ]
    ##            speaker utterance
    ## case.1  upsy-daisy       pip
    ## case.2  upsy-daisy       pip
    ## case.3   tombliboo        ee
    ## case.4 makka-pakka       pip
    ## case.5 makka-pakka       onk

    Alternativamente, si quiero mantener todas las columnas pero solo quiero las dos últimas filas, utilizo el mismo truco, pero esta vez dejo en blanco el segundo índice. Entonces mi comando se convierte en:

    garden[ 4:5, ]
    ##            speaker utterance line
    ## case.4 makka-pakka       pip    7
    ## case.5 makka-pakka       onk    9

    La segunda elaboración que debo señalar es que todavía está bien usar índices negativos como una forma de decirle a R que elimine ciertas filas o columnas. Por ejemplo, si quiero eliminar la 3ª columna, entonces utilizo este comando:

    garden[ , -3 ]
    
    ##            speaker utterance
    ## case.1  upsy-daisy       pip
    ## case.2  upsy-daisy       pip
    ## case.3   tombliboo        ee
    ## case.4 makka-pakka       pip
    ## case.5 makka-pakka       onk

    mientras que si quiero eliminar la tercera fila, entonces usaría esta:

    garden[ -3,  ]
    
    ##            speaker utterance line
    ## case.1  upsy-daisy       pip    1
    ## case.2  upsy-daisy       pip    2
    ## case.4 makka-pakka       pip    7
    ## case.5 makka-pakka       onk    9

    Así que eso es lindo.

    Uso de corchetes: III. Comprensión de “caer”

    En este punto, algunos de ustedes podrían estar preguntándose por qué he sido tan terriblemente cuidadoso al elegir mis ejemplos de tal manera que se asegure de que la salida siempre tiene son múltiples filas y múltiples columnas. La razón de esto es que he estado tratando de ocultar el comportamiento algo curioso de “caída” que R produce cuando la salida solo tiene una sola columna. Empezaré mostrándote lo que pasa, y luego voy a tratar de explicarlo. En primer lugar, echemos un vistazo a lo que sucede cuando la salida contiene solo una fila:

    garden[ 5, ]
    ##            speaker utterance line
    ## case.5 makka-pakka       onk    9

    Esto es exactamente lo que esperarías ver: un marco de datos que contiene tres variables, y solo un caso por variable. Bien, hasta ahora no hay problemas. ¿Qué pasa cuando pides una sola columna? Supongamos, por ejemplo, lo intento como un comando:

    garden[ , 3 ]

    En base a todo lo que le he mostrado hasta ahora, estaría dentro de sus derechos para esperar que R produzca un marco de datos que contenga una sola variable (es decir, línea) y cinco casos. Después de todo, eso es lo que hace el comando subset () en esta situación, y es bastante consistente con todo lo demás que te he mostrado hasta ahora sobre cómo funcionan los corchetes. En otras palabras, deberías esperar ver esto:

             line
    case.1    1
    case.2    2
    case.3    5
    case.4    7
    case.5    9

    Sin embargo, eso enfáticamente no es lo que sucede. Lo que realmente obtienes es esto:

    garden[ , 3 ]
    ## [1] 1 2 5 7 9

    ¡Esa salida no es un marco de datos en absoluto! Eso es solo un vector numérico ordinario que contiene 5 elementos. Lo que está pasando aquí es que R ha “notado” que la salida que hemos pedido realmente no “necesita” estar envuelta en un marco de datos en absoluto, porque solo corresponde a una sola variable. Entonces lo que hace es “soltar” la salida de un marco de datos que contiene una sola variable, “abajo” a una salida más simple que corresponde a esa variable. Este comportamiento es realmente muy conveniente para el uso diario una vez que te has familiarizado con él —y supongo que esa es la verdadera razón por la que R hace esto— pero no se puede escapar el hecho de que es profundamente confuso para los principiantes. Es especialmente confuso porque el comportamiento aparece solo para un caso muy específico: (a) solo funciona para columnas y no para filas, porque las columnas corresponden a variables y las filas no, y (b) solo se aplica a la versión “filas y columnas” de los corchetes, y no al subconjunto ( ), 114 o al uso de “solo columnas” de los corchetes (siguiente sección). Como digo, es muy confuso cuando recién estás empezando. Por lo que vale, puedes suprimir este comportamiento si quieres, estableciendo drop = FALSE cuando construyas tu expresión entre corchetes. Es decir, podrías hacer algo como esto:

    garden[ , 3, drop = FALSE ]
    ##        line
    ## case.1    1
    ## case.2    2
    ## case.3    5
    ## case.4    7
    ## case.5    9

    Supongo que eso ayuda un poco, en que te da cierto control sobre el comportamiento de caída, pero no estoy seguro de que ayude a que las cosas sean más fáciles de entender. En fin, ese es el caso especial de “caída”. Divertido, ¿no?

    Uso de corchetes: IV. Sólo columnas

    Como si el extraño comportamiento de “caída” no fuera lo suficientemente molesto, R en realidad proporciona una forma completamente diferente de usar corchetes para indexar un marco de datos. Específicamente, si solo das un único índice, R asumirá que quieres las columnas correspondientes, no las filas. No se deje engañar por el hecho de que este segundo método también usa corchetes: se comporta de manera diferente al método de “filas y columnas” que he discutido en las últimas secciones. De nuevo, lo que voy a hacer es mostrarte lo que pasa primero, y después voy a tratar de explicar por qué sucede después. Para ello, comencemos con el siguiente comando:

    garden[ 1:2 ]
    ##            speaker utterance
    ## case.1  upsy-daisy       pip
    ## case.2  upsy-daisy       pip
    ## case.3   tombliboo        ee
    ## case.4 makka-pakka       pip
    ## case.5 makka-pakka       onk

    Como pueden ver, la salida me da las dos primeras columnas, tanto como si hubiera escrito garden [, 1:2]. No me da las dos primeras filas, que es lo que habría conseguido si hubiera usado un comando como jardín [1:2,]. No solo eso, si pido una sola columna, R no baja la salida:

    garden[3]
    ##        line
    ## case.1    1
    ## case.2    2
    ## case.3    5
    ## case.4    7
    ## case.5    9

    Como dije antes, el único caso en el que la caída ocurre por defecto es cuando se usa la versión “fila y columnas” de los corchetes, y la salida pasa a corresponder a una sola columna. Sin embargo, si realmente quieres forzar a R a que baje la salida, puedes hacerlo usando la notación “corchetes dobles”:

    garden[[3]]
    ## [1] 1 2 5 7 9

    Tenga en cuenta que R solo le permitirá pedir una columna a la vez usando los corchetes dobles. Si intentas pedir varias columnas de esta manera, obtienes un comportamiento completamente diferente, 115 que puede o no producir un error, pero definitivamente no te dará la salida que estás esperando. La única razón por la que lo estoy mencionando en absoluto es que podrías encontrarte con corchetes dobles al hacer más lecturas, y muchos libros no señalan explícitamente la diferencia entre [y [[. Sin embargo, prometo que no voy a usar [[en ningún otro lugar de este libro.

    Bien, para esos pocos lectores que han perseverado con esta sección el tiempo suficiente para llegar hasta aquí sin haber prendido fuego al libro, debería explicar por qué R tiene estos dos sistemas diferentes para subestablecer un marco de datos (es decir, “fila y columna” versus “solo columnas”), y por qué se comportan así de manera diferente entre sí. No estoy 100% seguro de esto ya que sigo leyendo algunas de las viejas referencias que describen el desarrollo temprano de R, pero creo que la respuesta se relaciona con el hecho de que los marcos de datos son en realidad un híbrido muy extraño de dos tipos diferentes de cosas. En un nivel bajo, un marco de datos es una lista (Sección 4.9). Puedo demostrarle esto anulando la función normal print () 116 y forzando a R a imprimir el marco de datos del jardín usando el método de impresión predeterminado en lugar del especial que se define solo para marcos de datos. Esto es lo que obtenemos:

    print.default( garden )
    ## $speaker
    ## [1] upsy-daisy  upsy-daisy  tombliboo   makka-pakka makka-pakka
    ## Levels: makka-pakka tombliboo upsy-daisy
    ## 
    ## $utterance
    ## [1] pip pip ee  pip onk
    ## Levels: ee onk oo pip
    ## 
    ## $line
    ## [1] 1 2 5 7 9
    ## 
    ## attr(,"class")
    ## [1] "data.frame"

    Aparte de la extraña parte de la salida justo en la parte inferior, esto es idéntico a la impresión que obtienes al imprimir una lista (ver Sección 4.9). En otras palabras, un marco de datos es una lista. Vista desde esta perspectiva “basada en listas”, está claro qué es garden [1]: es la primera variable almacenada en la lista, es decir, speaker. En otras palabras, cuando usas la forma de “solo columnas” de indexar un marco de datos, usando solo un único índice, R asume que estás pensando en el marco de datos como si se tratara de una lista de variables. De hecho, cuando usas el operador $ estás aprovechando que el marco de datos es secretamente una lista.

    Sin embargo, un marco de datos es más que una simple lista. Es un tipo de lista muy especial donde todas las variables son de la misma longitud, y el primer elemento de cada variable pasa a corresponder al primer “caso” en el conjunto de datos. Es por eso que nadie quiere ver un marco de datos impreso de la manera predeterminada “similar a una lista” que he mostrado en el extracto anterior. En términos del significado más profundo detrás de lo que se usa un marco de datos, un marco de datos realmente tiene esta forma rectangular:

    print( garden )
    ##            speaker utterance line
    ## case.1  upsy-daisy       pip    1
    ## case.2  upsy-daisy       pip    2
    ## case.3   tombliboo        ee    5
    ## case.4 makka-pakka       pip    7
    ## case.5 makka-pakka       onk    9

    Debido al hecho de que un marco de datos es básicamente una tabla de datos, R proporciona un segundo método de “fila y columna” para interactuar con el marco de datos (ver Sección 7.11.1 para un ejemplo relacionado). Este método tiene mucho más sentido en términos de la tabla de alto nivel de interpretación de datos de lo que es un marco de datos, y por lo que en su mayor parte es este método el que la gente tiende a preferir. De hecho, a lo largo del resto del libro estaré apegándome al enfoque de “fila y columna” (aunque usaré mucho $), y nunca más referirme al enfoque de “solo columnas”. No obstante, sí se acostumbra mucho en la práctica, así que creo que es importante que este libro explique lo que está pasando.

    Y ahora nunca volvamos a hablar de esto.


    This page titled 7.5: Extracción de un subconjunto de un marco de datos is shared under a CC BY-SA 4.0 license and was authored, remixed, and/or curated by Danielle Navarro via source content that was edited to the style and standards of the LibreTexts platform; a detailed edit history is available upon request.