Símbolos y operadores de R data.table que debe conocer

El código de R data.table se vuelve más eficiente y elegante cuando se aprovechan sus símbolos y funciones especiales. Con eso en mente, veremos algunas formas especiales de crear subconjuntos, contar y crear nuevas columnas.

Para esta demostración, usaré datos de la encuesta de desarrolladores de Stack Overflow de 2019, con aproximadamente 90,000 respuestas. Si desea seguir adelante, puede descargar los datos de Stack Overflow.

Si el paquete data.table no está instalado en su sistema, instálelo desde CRAN y luego cárguelo como de costumbre con library(data.table). Para empezar, es posible que desee leer solo las primeras filas del conjunto de datos para facilitar el examen de la estructura de los datos. Puede hacer eso con la fread()función y el nrowsargumento de data.table . Leeré en 10 filas:

muestra_datos <- fread ("data / survey_results_public.csv", nrows = 10)

Como verá, hay 85 columnas para examinar. (Si desea saber qué significan todas las columnas, hay archivos en la descarga con el esquema de datos y un PDF de la encuesta original). 

Para leer todos los datos, usaré:

mydt <- fread ("data / survey_results_public.csv")

A continuación, crearé una nueva tabla de datos con solo unas pocas columnas para que sea más fácil trabajar con ella y ver los resultados. Un recordatorio de que data.table usa esta sintaxis básica: 

mydt [i, j, por]

La introducción del paquete data.table dice leer esto como "tomar dt, subconjunto o reordenar filas usando i, calcular j, agrupar por". Tenga en cuenta que i y j son similares al orden de los corchetes de la base R: primero las filas, luego las columnas. Entonces, i es para operaciones que harías en filas (eligiendo filas según los números o condiciones de las filas); j es lo que haría con las columnas (seleccionar columnas o crear nuevas columnas a partir de cálculos). Sin embargo, tenga en cuenta también que puede hacer mucho más dentro de los corchetes de data.table que un marco de datos R base. Y la sección "por" es nueva en data.table.

Como estoy seleccionando columnas, ese código va en el lugar "j", lo que significa que los corchetes necesitan una coma primero para dejar el lugar "i" vacío:

mydt [, j]

Seleccionar columnas de tabla de datos

Una de las cosas que me gustan de data.table es que es fácil seleccionar columnas entre comillas o no . Sin comillas es a menudo más conveniente (que suele ser la forma tidyverse). Pero citado es útil si está usando data.table dentro de sus propias funciones, o si desea pasar un vector que creó en otro lugar de su código.

Puede seleccionar columnas de data.table de la forma R base típica, con un vector convencional de nombres de columna entre comillas. Por ejemplo: 

dt1 <- mydt [, c ("LanguageWorkedWith", "LanguageDesireNextYear",

"OpenSourcer", "CurrencySymbol", "ConvertedComp",

"Aficionado")]

Si desea usarlos sin comillas, cree una lista en lugar de un vector y puede pasar los nombres sin comillas. 

dt1 <- mydt [, list (LanguageWorkedWith, LanguageDesireNextYear,

OpenSourcer, CurrencySymbol, ConvertedComp,

Aficionado)]

Y ahora llegamos a nuestro primer símbolo especial. En lugar de escribir list(), puede usar un punto:

dt1 <- mydt [,. (LanguageWorkedWith, LanguageDesireNextYear,

OpenSourcer, CurrencySymbol, ConvertedComp,

Aficionado)]

Ese .()es un atajo para los list()corchetes internos de data.table.

¿Qué sucede si desea utilizar un vector ya existente de nombres de columna? Poner el nombre del objeto vectorial entre corchetes data.table no funcionará. Si creo un vector con nombres de columna entre comillas, así: 

mycols <- c ("LanguageWorkedWith", "LanguageDesireNextYear",

"OpenSourcer", "CurrencySymbol", "ConvertedComp", "Aficionado")

Entonces este código  no funcionará: 

dt1 <- mydt [, mycols]

En su lugar, debe poner .. (son dos puntos) delante del nombre del objeto vectorial:

dt1 <- mydt [, ..mycols]

¿Por qué dos puntos? Eso me pareció algo aleatorio hasta que leí la explicación. Piense en ello como los dos puntos en una terminal de línea de comandos de Unix que lo mueven hacia arriba en un directorio. Aquí, está subiendo un espacio de nombres , desde el entorno dentro de los corchetes de data.table hasta el entorno global. (¡Eso realmente me ayuda a recordarlo!)

Contar filas de la tabla de datos

Pase al siguiente símbolo. Para contar por grupo, puede usar el .Nsímbolo de data.table , donde  .Nsignifica "número de filas". Puede ser el número total de filas o el número de filas por grupo si está agregando en la sección "por". 

Esta expresión devuelve el número total de filas en la tabla de datos: 

mydt [, .N]

El siguiente ejemplo calcula el número de filas agrupadas por una variable: si las personas en la encuesta también codifican como pasatiempo (la Hobbyistvariable).

mydt [, .N, aficionado]

# devoluciones:

Aficionado N 1: Sí 71257 2: No 17626

Puede usar el nombre de la columna sin formato dentro de los corchetes data.table si solo hay una variable. Si desea agrupar por dos o más variables, use el .símbolo. Por ejemplo:

mydt [, .N,. (aficionado, OpenSourcer)]

Para ordenar los resultados de mayor a menor, puede agregar un segundo conjunto de corchetes después del primero. El .Nsímbolo genera automáticamente una columna llamada N (por supuesto, puede cambiarle el nombre si lo desea), por lo que ordenar por el número de filas puede verse así:

mydt [, .N,. (Aficionado, OpenSourcer)] [pedido (Aficionado, -N)]

A medida que aprendo el código data.table, encuentro útil leerlo paso a paso. Así que leería esto como “Para todas las filas en mydt (ya que no hay nada en el punto“ I ”), cuente el número de filas, agrupando por Hobbyist y OpenSourcer. Luego ordene primero por aficionado y luego por el número de filas descendentes ". 

Eso es equivalente a este código dplyr:

mydf%>%

count (aficionado, OpenSourcer)%>%

orden (aficionado, -n)

Si encuentra que el enfoque multilínea convencional de tidyverse es más legible, este código data.table también funciona:

mydt [, .N,

. (Aficionado, OpenSourcer)] [

orden (aficionado, -N)

]

Agregar columnas a una tabla de datos

A continuación, me gustaría agregar columnas para ver si cada encuestado usa R, si usa Python, si usa ambos o si no usa ninguno. La LanguageWorkedWithcolumna tiene información sobre los idiomas utilizados, y algunas filas de esos datos se ven así:

Sharon Machlis

Cada respuesta es una cadena de un solo carácter. La mayoría tiene varios idiomas separados por un punto y coma.

Como suele ser el caso, es más fácil buscar Python que R, ya que no puede simplemente buscar "R" en la cadena (Ruby y Rust también contienen una R mayúscula) de la misma forma que puede buscar "Python". Este es el código más simple para crear un vector VERDADERO / FALSO que verifica si cada cadena LanguageWorkedWithcontiene Python:

ifelse (LanguageWorkedWith% like% "Python", TRUE, FALSE)

Si conoce SQL, reconocerá esa sintaxis de "me gusta". A mí, bueno, me gusta que %like%. sea ​​una buena forma simplificada de verificar la coincidencia de patrones. La documentación de la función dice que debe usarse dentro de los corchetes data.table, pero en realidad puede usarla en cualquier código, no solo con data.tables. Verifiqué con el creador de data.table Matt Dowle, quien dijo que el consejo de usarlo dentro de los corchetes es porque allí ocurre una optimización adicional del rendimiento.

A continuación, aquí está el código para agregar una columna llamada PythonUser a data.table:

dt1 [, PythonUser: = ifelse (LanguageWorkedWith% like% "Python", TRUE, FALSE)]

Fíjate en el :=operador. Python también tiene un operador así, y desde que escuché que se llamaba "operador de morsa", así es como lo llamo. Creo que es oficialmente "asignación por referencia". Esto se debe a que el código anterior cambió el objeto existente dt1 data.table al agregar la nueva columna, sin necesidad de guardarlo en una nueva variable .

Para buscar R, usaré la expresión regular "\\bR\\b"que dice: “Busque un patrón que comience con un límite de palabra: el \\b, luego una R, y luego termine con otro límite de palabra. (No puedo buscar simplemente "R;" porque el último elemento de cada cadena no tiene punto y coma). 

Esto agrega una columna RUser a dt1:

dt1 [, RUser: = ifelse (LanguageWorkedWith% like% "\\ bR \\ b", TRUE, FALSE)]

Si quisiera agregar ambas columnas a la vez :=, necesitaría convertir ese operador de morsa en una función entre comillas inversas, así:

dt1 [, `: =` (

PythonUser = ifelse (LanguageWorkedWith% like% "Python", TRUE, FALSE),

RUser = ifelse (LanguageWorkedWith% like% "\\ bR \\ b", TRUE, FALSE)

)]

Operadores de data.table más útiles

Hay varios otros operadores de tablas de datos que vale la pena conocer. El  %between% operador tiene esta sintaxis:

myvector% entre% c (valor_inferior, valor_uperior)

Entonces, si quiero filtrar todas las respuestas donde la compensación fue entre 50,000 y 100,000 pagados en dólares estadounidenses, este código funciona:

comp_50_100k <- dt1 [CurrencySymbol == "USD" &

ConvertedComp% entre% c (50000, 100000)]

La segunda línea de arriba es la condición intermedia. Tenga en cuenta que el %between%operador incluye los valores superior e inferior cuando realiza la verificación.

Otro operador útil es %chin%. Funciona como base R, %in%pero está optimizado para la velocidad y es solo para vectores de caracteres . Entonces, si quiero filtrar todas las filas en las que la columna de OpenSourcer fue "Nunca" o "Menos de una vez al año", este código funciona:

rareos <- dt1 [OpenSourcer% chin% c ("Nunca", "Menos de una vez al año")]

Esto es bastante similar a la base R, excepto que la base R debe especificar el nombre del marco de datos dentro del corchete y también requiere una coma después de la expresión de filtro:

rareos_df <- df1 [df1 $ OpenSourcer% en% c ("Nunca", "Menos de una vez al año"),]

La nueva función fcase ()

Para esta demostración final, comenzaré creando una nueva tabla de datos con solo personas que informaron una compensación en dólares estadounidenses:

usd <- dt1 [CurrencySymbol == "USD" &! is.na (ConvertedComp)]

A continuación, crearé una nueva columna llamada Languagepara si alguien usa solo R, solo Python, ambos o ninguno. Y usaré la nueva fcase()función. En el momento en que se publicó este artículo, fcase()solo estaba disponible en la versión de desarrollo de data.table. Si ya tiene instalado data.table, puede actualizar a la última versión de desarrollo con este comando: 

data.table :: update.dev.pkg ()

La función fcase () es similar a la CASE WHENdeclaración de SQL y la case_when()función de dplyr . La sintaxis básica es  fcase(condition1, "value1", condition2, "value2")y así sucesivamente. Se puede agregar un valor predeterminado para "todo lo demás" default = value.

Aquí está el código para crear la nueva columna de Idioma:

usd [, Idioma: = fcase (

RUser &! PythonUser, "R",

PythonUser &! RUser, "Python",

PythonUser y RUser, "Ambos",

! PythonUser &! RUser, "Ninguno"

)]

Pongo cada condición en una línea separada porque me resulta más fácil de leer, pero tú no tienes que hacerlo.

Advertencia: si está utilizando RStudio, la estructura data.table no se actualiza automáticamente en el panel de RStudio superior derecho después de crear una nueva columna con el operador de morsa. Debe hacer clic manualmente en el icono de actualización para ver los cambios en el número de columnas.

Hay algunos otros símbolos que no cubriré en este artículo. Puede encontrar una lista de ellos en el archivo de ayuda data.table de "símbolos especiales" ejecutando help("special-symbols"). Uno de los más útiles, .SD, ya tiene su propio artículo y video Do More With R, "Cómo usar .SD en el paquete R data.table".

Para obtener más sugerencias de R, diríjase a la página "Haga más con R" o consulte la lista de reproducción de YouTube "Haga más con R".