Utilizar R y Python en Rstudio es cada vez más simple. En este post, se muestran las similitudes y diferencias entre tidyverse y pandas para la manipulación de datos.
Utilizar R y Python en Rstudio es cada vez más simple 🙌. Mediante reticulate 📦 es posible compartir objetos entre ambos lenguajes, visualizando los mismos en el environment. En este post, se muestra las similitudes y diferencias entre tidyverse 📦y pandas 📦 para manipulación de datos. En ambos casos se visualizan los resultados de con los mismos gráficos de ggplot 📦.
👉 Para trabajar con python en Rstudio, una opción es la siguiente:
reticulate::conda_create(envname='tidypandas', python_version="3.9")
Se activa desde la terminal de Anaconda con conda activate distill-env
Se instalan los paquetes a utilizar. En este caso, se han instalado desde la terminal del conda env con los siguientes comandos:
numpy: conda install numpy
pandas: conda install pandas 🐼
O mediante reticulate:
reticulate::conda_install(envname = 'tidypandas',
packages='numpy',
channel='conda-forge')
reticulate::conda_install(envname = 'tidypandas',
packages='pandas',
channel='conda-forge')
Se define que el environment a utilizar es el que ha sido creado:
reticulate::use_condaenv(condaenv = 'tidypandas', required = TRUE)
A continuación, se cargan las librerías utilizadas para elaborar este post.
🔹 Las siguientes librerías de R se cargan utilizando un chunk de R:
🔹Se utiliza un chunk python para cargar las librerías de python:
import pandas as pd
import numpy as np
Para estos ejemplos, se utilizan datos obtenidos de Kaggle: IMDb movies extensive dataset🎥.
Se visualizan las primeras observaciones:
df %>% head(5) %>% gt() %>%
tab_header('Datos', subtitle='IMDb movies extensive dataset') %>%
opt_align_table_header(align='left')
Datos | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
IMDb movies extensive dataset | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
imdb_title_id | year | title | language | country | votes | avg_vote | reviews_from_users | reviews_from_critics | weighted_average_vote | total_votes | mean_vote | median_vote | votes_10 | votes_9 | votes_8 | votes_7 | votes_6 | votes_5 | votes_4 | votes_3 | votes_2 | votes_1 | allgenders_0age_avg_vote | allgenders_0age_votes | allgenders_18age_avg_vote | allgenders_18age_votes | allgenders_30age_avg_vote | allgenders_30age_votes | allgenders_45age_avg_vote | allgenders_45age_votes | males_allages_avg_vote | males_allages_votes | males_0age_avg_vote | males_0age_votes | males_18age_avg_vote | males_18age_votes | males_30age_avg_vote | males_30age_votes | males_45age_avg_vote | males_45age_votes | females_allages_avg_vote | females_allages_votes | females_0age_avg_vote | females_0age_votes | females_18age_avg_vote | females_18age_votes | females_30age_avg_vote | females_30age_votes | females_45age_avg_vote | females_45age_votes | top1000_voters_rating | top1000_voters_votes | us_voters_rating | us_voters_votes | non_us_voters_rating | non_us_voters_votes |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
tt0000009 | 1894 | Miss Jerry | None | USA | 154 | 5.9 | 1 | 2 | 5.9 | 154 | 5.9 | 6 | 12 | 4 | 10 | 43 | 28 | 28 | 9 | 1 | 5 | 14 | 7.2 | 4 | 6.0 | 38 | 5.7 | 50 | 6.6 | 35 | 6.2 | 97 | 7 | 1 | 5.9 | 24 | 5.6 | 36 | 6.7 | 31 | 6.0 | 35 | 7.3 | 3 | 5.9 | 14 | 5.7 | 13 | 4.5 | 4 | 5.7 | 34 | 6.4 | 51 | 6.0 | 70 |
tt0000574 | 1906 | The Story of the Kelly Gang | None | Australia | 589 | 6.1 | 7 | 7 | 6.1 | 589 | 6.3 | 6 | 57 | 18 | 58 | 137 | 139 | 103 | 28 | 20 | 13 | 16 | 6.0 | 1 | 6.1 | 114 | 6.0 | 239 | 6.3 | 115 | 6.1 | 425 | 6 | 1 | 6.2 | 102 | 6.0 | 210 | 6.2 | 100 | 6.2 | 50 | NA | NA | 5.9 | 12 | 6.2 | 23 | 6.6 | 14 | 6.4 | 66 | 6.0 | 96 | 6.2 | 331 |
tt0001892 | 1911 | Den sorte drøm | NA | Germany, Denmark | 188 | 5.8 | 5 | 2 | 5.8 | 188 | 6.0 | 6 | 6 | 6 | 17 | 44 | 52 | 32 | 16 | 5 | 6 | 4 | NA | NA | 5.5 | 25 | 5.8 | 72 | 6.2 | 62 | 5.9 | 146 | NA | NA | 5.5 | 21 | 5.9 | 67 | 6.2 | 55 | 5.7 | 15 | NA | NA | 5.8 | 4 | 5.8 | 4 | 6.8 | 7 | 5.4 | 32 | 6.2 | 31 | 5.9 | 123 |
tt0002101 | 1912 | Cleopatra | English | USA | 446 | 5.2 | 25 | 3 | 5.2 | 446 | 5.3 | 5 | 15 | 8 | 16 | 62 | 98 | 117 | 63 | 26 | 25 | 16 | NA | NA | 5.3 | 23 | 5.0 | 111 | 5.3 | 193 | 5.1 | 299 | NA | NA | 5.2 | 20 | 4.9 | 96 | 5.2 | 171 | 5.9 | 39 | NA | NA | 5.7 | 3 | 5.5 | 14 | 6.1 | 21 | 4.9 | 57 | 5.5 | 207 | 4.7 | 105 |
tt0002130 | 1911 | L'Inferno | Italian | Italy | 2237 | 7.0 | 31 | 14 | 7.0 | 2237 | 6.9 | 7 | 210 | 225 | 436 | 641 | 344 | 169 | 66 | 39 | 20 | 87 | 7.5 | 4 | 7.0 | 402 | 7.0 | 895 | 7.1 | 482 | 7.0 | 1607 | 8 | 2 | 7.0 | 346 | 7.0 | 804 | 7.0 | 396 | 7.2 | 215 | 7.0 | 2 | 7.0 | 52 | 7.3 | 82 | 7.4 | 77 | 6.9 | 139 | 7.0 | 488 | 7.0 | 1166 |
Se presenta la misma manipulación de datos con Tidyverse y Pandas para generar un dataframe de cantidad de películas por año.
Filtro: años no nulos
Agrupamiento: año
Agregamiento: cantidad de películas por año
Cantidad de películas por año | |
year | number_of_films |
---|---|
1894 | 1 |
1906 | 1 |
1911 | 5 |
= (r.df
peliculas_por_anio '~year.isna()', engine='python')
.query('year', as_index=False)
.groupby(
.size()'size':'number_of_films'}, axis=1)
.rename({ )
Cantidad de películas por año | |
year | number_of_films |
---|---|
1894 | 1 |
1906 | 1 |
1911 | 5 |
👉 Se seleccionan los 8 países con más películas:
USA, India, UK, Japan, France, Italy, Canada and Germany
Los pasos a realizar son los siguientes:
Cantidad y promedio de votos por país y año | |||
country | year | avg_vote | number |
---|---|---|---|
Canada | 1919 | 6.3 | 1 |
Canada | 1947 | 6.3 | 1 |
Canada | 1952 | 3.7 | 1 |
p1 <- peliculas_paises %>%
ggplot(aes(x = year, y = number)) +
geom_line(color='blue') +
scale_x_continuous()+
facet_wrap(~ country, nrow = 2)+
theme_minimal()+
labs(x='Año', y='Cantidad de películas',
title='Cantidad de películas por país y año')
p2 <- peliculas_paises %>%
ggplot(aes(x = year, y = avg_vote)) +
geom_line(color='red') +
scale_x_continuous()+
facet_wrap(~ country, nrow = 2)+
theme_minimal()+
labs(x='Año', y='Promedio de votos',
title='Promedio de votos por país y año')
p1 / p2
= r.paises_frecuentes
paises_frecuentes = (r.df
peliculas_paises "country.isin(@paises_frecuentes)",engine='python')
.query('country','year'],as_index=False)
.groupby([=('avg_vote', 'mean'),
.agg(avg_vote=('imdb_title_id', 'count'))
number )
Cantidad y promedio de votos por país y año | |||
country | year | avg_vote | number |
---|---|---|---|
Canada | 1919 | 6.3 | 1 |
Canada | 1947 | 6.3 | 1 |
Canada | 1952 | 3.7 | 1 |
p1 <- py$peliculas_paises %>%
ggplot(aes(x = year, y = number)) +
geom_line(color='blue') +
scale_x_continuous()+
facet_wrap(~ country, nrow = 2)+
theme_minimal()
p2 <- py$peliculas_paises %>%
ggplot(aes(x = year, y = avg_vote)) +
geom_line(color='red') +
scale_x_continuous()+
facet_wrap(~ country, nrow = 2)+
theme_minimal()
p1 / p2
Filtro: 8 países más frecuentes
Selección de columnas: country, imdb_title_id, variables que incluyen “allages”
Eliminación de filas con valores nulos
Creación de variable: preferencia_masculina=1, representa películas en las cuales el promedio de votos masculinos es superior al promedio de votos femeninos
Agrupamiento: country
Agregamiento: promedio de votos masculinos y femeninos, número de observaciones, suma de preferencia_masculina (cantidad de películas con preferencia masculina)
Creación de variable: % de preferencia_masculina en cada país
Eliminación de columna: número de observaciones
Pivot_longer: según país como ID
genero <- df %>%
filter(country %in% paises_frecuentes) %>%
select(country, imdb_title_id, contains('allages')) %>%
drop_na() %>%
mutate(
preferencia_masculina = ifelse(males_allages_avg_vote>females_allages_avg_vote,
1,0)) %>%
group_by(country) %>%
summarise(avg_vote_male = mean(males_allages_avg_vote),
avg_vote_female = mean(females_allages_avg_vote),
observaciones = n(),
preferencia_masculina = sum(preferencia_masculina)) %>%
mutate(preferencia_masculina = preferencia_masculina/observaciones*100) %>%
select(-observaciones) %>%
pivot_longer(-country)
Género y votos | ||
country | name | value |
---|---|---|
Canada | avg_vote_female | 5.584897 |
Canada | avg_vote_male | 5.323709 |
Canada | preferencia_masculina | 27.207107 |
genero %>%
ggplot(aes(x=value, y=country, fill=country, color=name))+
geom_segment(aes(y=country, yend=country, x=0, xend=value, color=name), size=0.5) +
geom_point( size=3, fill=alpha("white", 0.3), alpha=0.9, shape=21, stroke=1) +
scale_fill_viridis_d(option='B')+
facet_wrap(~name, scales='free', nrow=3)+
theme_minimal()+
theme(legend.position='none')+
labs(x='Valor', y='País',
title='Promedio de votos femeninos y masculinos. % de preferencia masculina')
= (r.df
genero "country.isin(@paises_frecuentes)",engine='python')
.query(filter(items = ['country','imdb_title_id']+r.df.filter(regex='allages').columns.tolist())
.
.dropna()
.assign(= lambda x: np.where(x['males_allages_avg_vote']>x['females_allages_avg_vote'],1,0)
preferencia_masculina
)'country'], as_index=False)
.groupby([=('males_allages_avg_vote', 'mean'),
.agg(avg_votes_male =('females_allages_avg_vote', 'mean'),
avg_votes_female =('imdb_title_id', 'count'),
observaciones= ('preferencia_masculina','sum')
preferencia_masculina
)= lambda x: x['preferencia_masculina']/x['observaciones']*100)
.assign(preferencia_masculina 'observaciones'],axis=1)
.drop([='country')
.melt(id_vars'variable':'name'},axis=1)
.rename({ )
Género y votos | ||
country | name | value |
---|---|---|
Canada | avg_votes_female | 5.584897 |
Canada | avg_votes_male | 5.323709 |
Canada | preferencia_masculina | 27.207107 |
py$genero %>%
ggplot(aes(x=value, y=country, fill=country, color=name))+
geom_segment(aes(y=country, yend=country, x=0, xend=value, color=name), size=0.5) +
geom_point( size=3, fill=alpha("white", 0.3), alpha=0.9, shape=21, stroke=1) +
scale_fill_viridis_d(option='B')+
facet_wrap(~name, scales='free', nrow=3)+
theme_minimal()+
theme(legend.position='none') +
labs(x='Valor', y='País',
title='Promedio de votos femeninos y masculinos. % de preferencia masculina')
Para quienes utilizan Tidyverse regularmente para manipulación de datos, Pandas puede parecer complejo al comienzo 🤯, debido a que en la práctica habitual no se suele utilizar un enfoque ordenado 🙌. Que no se utilice este enfoque no significa que esto no sea posible. En este post se muestran algunos ejemplos de como hacerlo, mostrando las similitudes con Tidyverse.
Esperamos que haya sido de utilidad, gracias por leernos 👏👏👏.
Karina Bartolome, Linkedin, Twitter, Github, Blogpost.
Rafael Zambrano, Linkedin, Twitter, Github, Blogpost.
For attribution, please cite this work as
Bartolomé & Zambrano (2021, June 13). Karina Bartolome: Tidypandas. Retrieved from https://karbartolome-blog.netlify.app/posts/tidypandas/
BibTeX citation
@misc{bartolomé2021tidypandas, author = {Bartolomé, Karina and Zambrano, Rafael}, title = {Karina Bartolome: Tidypandas}, url = {https://karbartolome-blog.netlify.app/posts/tidypandas/}, year = {2021} }