En este paso, no cargamos ningún archivo externo, sino que le pedimos a R que genere datos ficticios usando una distribución normal. Con esta línea:
hist(rnorm(100, mean = 50, sd = 25))
lo que ocurre es:
rnorm(100, mean = 50, sd = 25) crea 100 números aleatorios con media 50 y desviación estándar 25.
hist(…) grafica esos valores en un histograma, mostrando visualmente cómo se distribuyen.
Es una forma sencilla de familiarizarnos con cómo R genera datos y los visualiza de manera inmediata.
Ahora, resulta un error con respecto al gráfico que se señala en la página, que es un gráfico de dispersión, no un histograma.
Para cargar los datos podemos ir a Open Data y escoger con los que queremos trabajar. En este caso como ya tenemos los datos de los lugares para grabar películas en el condado de NY, utilizaremos los siguientes comandos para cargar los datos en el entorno de R:
library(data.table)
nyc_films <- fread("data/Film_Permits.csv")
Hay una opción para ver los datos cargados en una tabla y es que si nos ubicamos en el Entorno podremos ver los datos, en la esquina izquierda vemos una flecha para desglosar los mismos, pero en el símbolo de recuadro nos abrirá otra pestaña con todos los datos organizados por filas y columnas.
Esto es útil, pero en este caso como tenemos tantísimos datos necesitamos resumirlos.
Para tener una visión rápida de todo el dataset (cuántas variables, tipos de datos, valores faltantes…), instalamos y usamos summarytools
library(summarytools) dfSummary(nyc_films)
Esto genera un resumen detallado en la ventana de visor o Viewer, lo que ayuda mucho cuando los datos son extensos y complejos. No obstante, siguen siendo muchos datos y podemos visualizarlos en el navegador yéndonos al botón junto a la escoba (cuidadito, Wasowski), abajo de “Plots”.
Para efectos de la Mariana del futuro olvidadiza. Dejaré un Chunk donde se vea el resultado final de lo que se espera ver.
library(data.table) library(summarytools)
Resultado final:
library(summarytools)
print(
dfSummary(nyc_films),
method = "render"
)
No | Variable | Stats / Values | Freqs (% of Valid) | Graph | Valid | Missing | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | EventID [integer] |
|
50728 distinct values | 50728 (100.0%) | 0 (0.0%) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
2 | EventType [character] |
|
|
50728 (100.0%) | 0 (0.0%) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
3 | StartDateTime [character] |
|
|
50728 (100.0%) | 0 (0.0%) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
4 | EndDateTime [character] |
|
|
50728 (100.0%) | 0 (0.0%) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
5 | EnteredOn [character] |
|
|
50728 (100.0%) | 0 (0.0%) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
6 | EventAgency [character] | 1. Mayor's Office of Film, T |
|
50728 (100.0%) | 0 (0.0%) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
7 | ParkingHeld [character] |
|
|
50728 (100.0%) | 0 (0.0%) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
8 | Borough [character] |
|
|
50728 (100.0%) | 0 (0.0%) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
9 | CommunityBoard(s) [character] |
|
|
50728 (100.0%) | 0 (0.0%) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
10 | PolicePrecinct(s) [character] |
|
|
50728 (100.0%) | 0 (0.0%) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
11 | Category [character] |
|
|
50728 (100.0%) | 0 (0.0%) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
12 | SubCategoryName [character] |
|
|
50728 (100.0%) | 0 (0.0%) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
13 | Country [character] |
|
|
50728 (100.0%) | 0 (0.0%) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
14 | ZipCode(s) [character] |
|
|
50728 (100.0%) | 0 (0.0%) |
Generated by summarytools 1.1.4 (R version 4.4.1)
2025-08-21
Bueno, ahora viene lo bueno. Nos dicen que se otorgaron 50,728 permisos de cine. En los datos cargados se ve que hay diferentes columnas que ofrecen información sobre cada permiso. Por ejemplo, la columna “Borough” indica el distrito de cada solicitud, independientemente de si se realizó para: Manhattan, Brooklyn, Bronx, Queen’s o Staten Island. Ahora podemos formular nuestra primera pregunta y aprender a crear gráficos en R.
Bueno, antes de lanzarnos a hacer un gráfico, es importante hacer un recuento de lo que vamos a querer visualizar.
Con el siguiente código vamos a poder cargar el recuento en el entorno y verla luego en una tabla organizada.
library(dplyr)
counts <- nyc_films %>%
group_by(Borough) %>%
summarize(count_of_permits = length(Borough))
La información que deberíamos encontrar sería la de los 5 distritos de NY con el número de permisos solicitados para grabar películas.
Se observa la información en siguiente chunk:
library(dplyr)
counts <- nyc_films %>%
group_by(Borough) %>%
summarize(count_of_permits = n())
counts
## # A tibble: 5 × 2
## Borough count_of_permits
## <chr> <int>
## 1 Bronx 1406
## 2 Brooklyn 15389
## 3 Manhattan 25373
## 4 Queens 7934
## 5 Staten Island 626
Una vez tengamos el recuento vamos a poder graficar usando el paquete”fantástico” de ggplot2.
library(ggplot2)
ggplot(counts, aes(x = Borough, y = count_of_permits )) +
geom_bar(stat="identity")
Con este gráfico es mucho más fácil responder nuestra pregunta de ¿dónde se solicitan más permisos para filmar? Ya sabemos que la mayoría de los permisos de rodaje se solicitaron en Manhattan, seguido de Brooklyn, luego Queen’s, el Bronx y, finalmente, Staten Island.
Ok, next question…
Ok, aquí ya me están preguntando por otra variable, por categoría, podríamos irnos a la tabla con los datos pero sería abrumador, let’s be more clever y hagamos otro gráfico con el código que ya nos presentan en el laboratorio.
counts <- nyc_films %>%
group_by(Category) %>%
summarize(count_of_permits = length(Category))
ggplot(counts, aes(x = Category, y = count_of_permits )) +
geom_bar(stat="identity")+
theme(axis.text.x = element_text(angle = 90, hjust = 1))
Bueno, con este gráfico cruzamos las variables de Número de permisos pero ahora con Categoría. En ese sentido podemos observar que la categoría del tipo de películas que más se está solicitando realizar en NY es el de televisión.
Bueno, lo que tenemos que tener claro es que ggplot funciona por capas cuando estamos creando un gráfico. Las capas se irán creando cuando usemos los signos “+” y en el orden en que los vayamos escribiendo.
Nos explican también que ggplot2 dibuja de abajo hacía arriba. Con la práctica será más fácil entender a qué se refieren con eso último.
ggplot(name_of_data, aes(x = name_of_x_variable, y = name_of_y_variable)) + geom_layer()+ geom_layer()+ geom_layer()
Bueno, gracias a Dios no fui la única que le parecieron terribles las etiquetas, entonces crearon una forma para modificarlas.
En las gráficas anteriores se tomó tal cual el nombre de la variable, pero con ylab() podemos cambiárselo.
ggplot(counts, aes(x = Category, y = count_of_permits )) +
geom_bar(stat="identity") +
theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
ylab("No. de películas permitidas")
Modificamos ligeramente la etiqueta X
ggplot(counts, aes(x = Category, y = count_of_permits )) +
geom_bar(stat="identity") +
theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
ylab("No. de películas permitidas") +
xlab("Categoría de la peli")
ggplot(counts, aes(x = Category, y = count_of_permits )) +
geom_bar(stat="identity") +
theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
ylab("No. de películas permitidas") +
xlab("Categoría de la peli") +
ggtitle("No. de películas permitidas en NYC por categoría")
ggplot(counts, aes(x = Category, y = count_of_permits, color=Category )) +
geom_bar(stat="identity") +
theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
ylab("No. de pelis permitidas") +
xlab("Categoría de la peli") +
ggtitle("# de pelis permitidas en NY por categoría")
Aquí hay un error cuando se corta la leyenda de Categoría porque queda muy arriba.
Antes de rellenarlo de color, en el código corrijo el error anterior que identifiqué ajustando el tamaño del texto de la leyenda y los cuadros de color.
Resultado:
ggplot(counts, aes(x = Category, y = count_of_permits, fill = Category)) +
geom_bar(stat = "identity") +
theme(axis.text.x = element_text(angle = 90, hjust = 1),
legend.text = element_text(size = 8), # Tamaño del texto de la leyenda
legend.key.size = unit(0.5, "cm")) + # Tamaño de los cuadros de color
ylab("No. de pelis permitidas") +
xlab("Categoría de la peli") +
ggtitle("# de pelis permitidas en NY por categoría")
ggplot(counts, aes(x = Category, y = count_of_permits,
color=Category,
fill= Category )) +
geom_bar(stat="identity") +
theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
ylab("No.de pelis permitidas") +
xlab("Categoría de la peli") +
ggtitle("# de pelis permitidas en NYC por Categoría") +
theme(legend.position="none")
Para gustos, los colores y si queremos ser más aesthetic y quitarle el fondo de recuadros gris de los anteriores gráficos, aquí está el cógido que agrega otra capa con el elemento theme_classic() para dejar un fondo blanco.
ggplot(counts, aes(x = Category, y = count_of_permits,
color=Category,
fill= Category )) +
geom_bar(stat="identity") +
theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
ylab("No. de pelis permitidas") +
xlab("Categoría de la peli") +
ggtitle("# de pelis permitidas en NYC por Categoría") +
theme(legend.position="none") +
theme_classic()
Ahhhhh, ¿qué pasó con los nombres de las categorías?
#1.2.5.8 A veces el orden de las capas importa
Pues como se mencionaba al inicio en los conceptos básicos de ggplot2, pues el orden de las capas va a influir directamente en cómo lea y dibije mi gráfico y en este caso, el elemento de theme_classic() para quitar el fondo se comporta extraño.
Lo que hacemos es cambiar theme_classic() de lugar y lo ubicamos arriba, ya no de último lugar como hicimos antes, sino que ahora va a estar en la 2da capa, justo abajo de geom_bar()
ggplot(counts, aes(x = Category, y = count_of_permits,
color=Category,
fill= Category )) +
geom_bar(stat="identity") +
theme_classic() +
theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
ylab("No. de pelis permitidas") +
xlab("Categoría de la peli") +
ggtitle("# de pelis permitidas en NYC por categoría") +
theme(legend.position="none")
Para cambiar el tamaño de la fuente usamos un nuevo elemento llamado base_size que irá dentro de [insertar] de theme_classic
ggplot(counts, aes(x = Category, y = count_of_permits,
color=Category,
fill= Category )) +
geom_bar(stat="identity") +
theme_classic(base_size = 14) +
theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
ylab("Number of Film Permits") +
xlab("Category of film") +
ggtitle("Number of Film permits in NYC by Category") +
theme(legend.position="none")
O se puede hacer títulos y en general letra más pequeña.
ggplot(counts, aes(x = Category, y = count_of_permits,
color=Category,
fill= Category )) +
geom_bar(stat="identity") +
theme_classic(base_size = 10) +
theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
ylab("Number of Film Permits") +
xlab("Category of film") +
ggtitle("Number of Film permits in NYC by Category") +
theme(legend.position="none")
Graficando la variable X de Sub categorías, vemos que las series episódicas son las más comunes.
counts <- nyc_films %>%
group_by(SubCategoryName) %>%
summarize(count_of_permits = length(SubCategoryName))
ggplot(counts, aes(x = SubCategoryName, y = count_of_permits,
color=SubCategoryName,
fill= SubCategoryName )) +
geom_bar(stat="identity") +
theme_classic(base_size = 10) +
theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
ylab("Number of Film Permits") +
xlab("Sub-category of film") +
ggtitle("Number of Film permits in NYC by Sub-category") +
theme(legend.position="none")
Sabemos que algunas películas se hacen en diferentes distritos y que las mismas películas se hacen en diferentes categorías, pero ¿los diferentes distritos tienen patrones diferentes para las categorías de películas para las que solicitan permisos? ¿Hay más programas de televisión en Brooklyn?
Con el elemento de facet_wrap() podemos darle respuesta.
counts <- nyc_films %>%
group_by(Borough, Category) %>%
summarize(count_of_permits = length(Category))
ggplot(counts, aes(x = Category, y = count_of_permits,
color=Category,
fill= Category )) +
geom_bar(stat="identity") +
theme_classic(base_size = 10) +
theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
ylab("Number of Film Permits") +
xlab("Category of film") +
ggtitle("Number of Film permits in NYC by Category and Borough") +
theme(legend.position="none") +
facet_wrap(~Borough, ncol=3)
El gráfico muestra la distribución de los permisos de filmación en Nueva York desagregados por categoría de producción y organizados en paneles según cada borough (distrito).
ggplot(counts, aes(x = Borough, y = count_of_permits,
color=Borough,
fill= Borough )) +
geom_bar(stat="identity") +
theme_classic(base_size = 10) +
theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
ylab("Number of Film Permits") +
xlab("Borough") +
ggtitle("Number of Film permits in NYC by Category and Borough") +
theme(legend.position="none") +
facet_wrap(~Category, ncol=5)
Para ello:
Se agrupan los datos por categoría y distrito, contando el número de permisos en cada combinación.
Se construyen gráficos de barras donde el eje x representa las distintas categorías (por ejemplo, películas, comerciales, televisión, etc.) y el eje y la cantidad de permisos otorgados.
Al facetar por distrito, se permite comparar cómo varía la actividad audiovisual entre Manhattan, Brooklyn, Queens, Bronx y Staten Island, evidenciando qué tipos de producciones son más frecuentes en cada zona de la ciudad.
Instalar paquete Gapminder para accerder a los datos.
install.packages(“gapminder”)
Llamar al paquete gapminder previamente instalado para usarlo en este laboratorio, además, los datos gapminder se agregan a un marco de datos para poder visualizarlos.
library(gapminder)
gapminder_df<-gapminder
Haciendo uso de “summarytools” podemos ver los datos para ver que contiene y también un resumen de los mismos.
view(dfSummary(gapminder_df))
A continuación, con los datos de gapminder y los comandos del paquete ggplot2 se harán los gráficos de ejemplo para practicar y responder preguntas como:
Para responder, en este caso, se usará un histograma para visualizar la distribución.
ggplot(gapminder_df, aes(x=lifeExp))+
geom_histogram(color="white")
Y, después de aplicar más capas, queda de la siguiente forma:
ggplot(gapminder_df, aes(x = lifeExp)) +
geom_histogram(color="white")+
theme_classic(base_size = 15) +
ylab("Frequency count") +
xlab("Life Expectancy") +
ggtitle("Histogram of Life Expectancy from Gapminder")
Cambiando el intérvalo para modificar el grosor o el número de barras queda de la siguiente forma:
ggplot(gapminder_df, aes(x = lifeExp)) +
geom_histogram(color="white", bins=50)+
theme_classic(base_size = 15) +
ylab("Frequency count") +
xlab("Life Expectancy") +
ggtitle("Histogram of Life Expectancy from Gapminder")
Ahora, podemos hacer diagramas de dispersión para responder preguntas como:
A medida que avanzamos hacia el futuro, ¿viven las personas más?
ggplot(gapminder_df, aes(y= lifeExp, x= year))+
geom_point()
A continuación, lo que haremos es graficar la esperanza de vida pero para un país en específico, para así poder verla año a año. Para eso, usamos la función filter de dplyr. Primero filtramos
#filtrar país Canadá
smaller_df <- gapminder_df %>%
filter(country == "Canada")
Ya filtrado, se usa esa base de datos más “pequeña” para hacer el mismo gráfico, y queda así:
ggplot(smaller_df, aes(y= lifeExp, x= year))+
geom_point()
Este mismo ejercicio lo haremos ahora con varios países al mismo tiempo, pero no tantos como para que logre ser legible la gráfica.
smaller_df <- gapminder_df %>%
filter(country %in% c("Canada","France","Brazil") == TRUE)
ggplot(smaller_df, aes(y= lifeExp, x= year, group= country))+
geom_point()
El siguiente paso que hacemos es agregar leyendas para que los datos sean reconocibles a simple vista, diferenciando los países por color
ggplot(smaller_df,aes(y= lifeExp, x= year,
group= country, color = country)) +
geom_point()+
theme_classic(base_size = 15) +
ylab("Life Expectancy") +
xlab("Year") +
ggtitle("Life expectancy by year for three countries")
Por último en este apartado, vamos a usar líneas para unir los puntos y así nos sea más fácil identificar una secuencia o tendencia.
ggplot(smaller_df,aes(y= lifeExp, x= year,
group= country, color = country)) +
geom_point()+
geom_line()+
theme_classic(base_size = 15) +
ylab("Life Expectancy") +
xlab("Year") +
ggtitle("Life expectancy by year for three countries")
Ahora, como ejercicio de generalización vamos a usar lo aprendido para responder a dos problemas/preguntas planteadas en el laboratorio.
Para resolver este ejercicio lo primero que haremos es:
Lo hacemos de esta forma ya que de lo contrario, el gráfico que obtendremos tendrá muchas líneas entrecruzadas y no será entendible.
#Agrupamos los datos por continente y sacamos el promedio con funciones que ya hemos utilizado como summarise y mean
continents <- gapminder_df %>% group_by(continent, year) %>% summarise(media_lifeExp = mean(lifeExp), .groups = "drop")
#Luego, ya podremos hacer el gráfico más organizado
ggplot(continents,aes(y= media_lifeExp, x= year,
group= continent, color = continent)) +
geom_point()+
geom_line()+
theme_classic(base_size = 15) +
ylab("Life Expectancy") +
xlab("Year") +
ggtitle("Esperanza de vida por año por continente")
#Primero filtramos los datos para los países elegidos
PIB_per_capita <- gapminder_df %>%
filter(country %in% c("United States","Canada","Mexico") == TRUE)
#Con una nueva base filtrada, podemos usar los mismos comandos para hacer el gráfico
ggplot(PIB_per_capita,aes(y= gdpPercap, x= year,
group= country, color = country)) +
geom_point()+
geom_line()+
theme_classic(base_size = 15) +
ylab("PIB per capita") +
xlab("Year") +
ggtitle("PIB per capita anual para EE. UU., Canadá y México")
Describe qué son los histogramas, cómo interpretarlos y para qué sirven.
R// Un histograma es un tipo de gráfico que muestra la distribución de una variable numérica. Se construye dividiendo el rango de valores en intervalos. Luego, se cuentan cuántos datos caen en cada intervalo.
En el gráfico, cada intervalo se representa con una barra cuya altura indica la frecuencia (cuántas observaciones hay en ese rango).
El histograma nos ayuda a visualizar la forma de la distribución, a saber si los datos son simétricos, sesgados a la derecha o a la izquierda, para también ver si se asemejan a una distribución normal (en forma de campana), a detectar concentraciones de datos, mostrar en qué rangos se acumulan más observaciones e identificar valores atípicos (outliers).
Se ven como barras aisladas muy lejos de las demás.
R// Estos son gráficos de la frecuencia de los datos en intervalos. Cuando hay un gran número de datos numéricos y no podemos, o queremos, ver cada valor individual, agrupamos los valores en rangos. Cada barra dice cuántas observaciones hay en el rango. Por ejemplo, podemos representar la altura de los estudiantes de Icesi, una barra cubriría el intervalo de las estaturas del 1.70 a 1.80. No tendrías que mostrar cada dato individual, sólo el número del grupo.
R// La cantidad depende de quien hace la gráfica. Al usar pocas barras, la distribución de rangos se puede aplastar y se podría llegar a perder información. Esto es porque valores que pueden ser muy distintos y significar cosas diferentes pueden caer en una misma barra. Por otro lado, al usar demasiadas barras, tendremos mucho más detalle, pero el gráfico podría ser más difícil de interpretar. Por eso es importante equilibrar entre calidad y detalle estos histogramas. Siguiendo el ejemplo previo, sí hiciéramos dos barras para medir todas las estaturas de todos los estudiantes de Icesi, una barra de 1.45 a 1.70 y 1.71 a 2.10, estarías juntando estaturas radicalmente distintas en una misma barra.
R// La altura de una barra muestra la cantidad de observaciones que están dentro de su rango correspondiente. Entre más alta la barra, más común es el rango dentro del conjunto de datos que se analizan. Ejemplo: Si la barra de 1.70 – 1.75 es más alta que la de 1.80 – 1-85, entonces hay más estudiantes entre el primer rango.
En el eje X se encuentran los intervalos de las variables numéricas que se están estudiando. Rangos de edad, de estatura, entre otros. Se muestra como la base de cada barra. Mientras que en el eje Y está el conteo de casos por intervalo. Puesto de forma sencilla, X muestra los valores posibles y Y cuántos de esos aparecen en el conjunto de datos.
R// Como se mencionó previamente, la altura de las barras indica la cantidad de casos observados de un intervalo. Por lo tanto, la barra más alta muestra el intervalo con más concentración de observaciones. Es donde hay más frecuencia de datos comunes. Sí la barra de 1.70 – 1.75 es la más alta, entonces ese rango es el que más se repite en Icesi.
R//Una barra muy baja, o inexistente, indica que el intervalo en cuestión tiene pocos o ningún dato. Un rango de datos poco frecuentes.
R// Los histogramas sirven para mostrar y entender cómo se distribuyen datos. Con estas gráficas se pueden identificar concentraciones alrededor de ciertos valores o si están muy dispersos, si la distribución es simétrica o no, si hay patrones o picos de frecuencia, etc. En resumen, convierte los datos sin procesar en una representación visual que facilita la comprensión y el análisis de patrones en el conjunto de datos.
R// El histograma “angosto con pico alto” refleja mayor consistencia, esto pues significa que más observaciones están concentradas en un pequeño rango. Por lo tanto, son números más cercanos entre sí. En contraste, un histograma ancho muestra que los valores están repartidos en rangos más amplios, lo que implica más variabilidad y poca consistencia entre intervalos. Por ejemplo, si mostramos que varias personas tardan en correr de un punto a otro y obtenemos un gran pico, eso significa que casi todos los corredores tienen un rendimiento similar, son datos cercanos. Por otro lado, un histograma ancho implicaría que los tiempos entre corredores varían mucho.