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='distill-env', python_version="3.8.8")
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 🐼
Luego de finalizar el proyecto es posible eliminar este environment.
Se define que el environment a utilizar es el que ha sido creado:
reticulate::use_condaenv(condaenv = 'distill-env', 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🎥.
df <- read_csv('https://raw.githubusercontent.com/karbartolome/data/main/IMDb_df.csv')
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_ano '~year.isna()', engine='python')
.query('year', as_index=False)
.groupby(
.size()'size':'number_of_films'},axis=1)
.rename({ )
Cantidad de películas por ano | |
---|---|
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.
Text and figures are licensed under Creative Commons Attribution CC BY 4.0. The figures that have been reused from other sources don't fall under this license and can be recognized by a note in their caption: "Figure from ...".
For attribution, please cite this work as
Bartolomé & Zambrano (2021, June 13). Rafael Zambrano: %>% Tidypandas. Retrieved from https://rafael-zambrano-blog-ds.netlify.app/posts/tidypandas/
BibTeX citation
@misc{bartolomé2021%>%, author = {Bartolomé, Karina and Zambrano, Rafael}, title = {Rafael Zambrano: %>% Tidypandas}, url = {https://rafael-zambrano-blog-ds.netlify.app/posts/tidypandas/}, year = {2021} }