Gustavo Coimbra de Souza Teixeira, Julia Folgueral, Maria Eduarda Villéla Silva, Nicole de Barros Silva
Introdução
Comparação entre diferentes linguagens para visualização de dados;
Foco em gráficos;
Banco de dados usado: Análise de desempenho de funcionários para RH
O banco usado está disponível aqui.
Sobre o Banco de Dados
O conjunto de dados apresenta informações dos funcionários de uma empresa, incluindo:
Departamento
Formação acadêmica
Gênero
Canal de recrutamento
Nº de treinamentos
Idade
Avaliação anterior
Tempo de serviço
Indicadores-chave de desempenho(KPI’s) atingidos
Média de treinamentos
Critérios para comparação entre linguagens
Para realizar uma comparação justa entre R, Python e Julia foram considerados os seguintes critérios:
Tempo de execução do gráfico;
Memória usada para plotar o gráfico;
Quantidade de linhas de código.
Linguagem R
Boxplot
Pontuação em treinamentos por formação acadêmica
library(ggplot2)ggplot(dados_corrigidos, aes(x = education, y = avg_training_score, fill = KPIs_met_more_than_80 )) +geom_boxplot(alpha =0.6,width =0.65,outlier.color ="red",color ="black",size =0.3 ) +labs(title ="Pontuação em Treinamentos por Formação Acadêmica",subtitle ="Comparação entre funcionários com KPI > 80 e KPI ≤ 80",x ="Formação Acadêmica",y ="Pontuação Média em Treinamentos",fill ="KPI > 80" ) +scale_fill_manual(values =c("Não"="#F8766D","Sim"="#00BFC4" )) +scale_x_discrete(labels =c("Below Secondary"="Sem Ensino Superior","Bachelors"="Bacharelado","Masters & above"="Mestrado ou Superior" )) +theme_classic(base_size =12) +theme(plot.title =element_text(size =14, face ="bold"),plot.subtitle =element_text(size =11),axis.text.x =element_text(angle =0, hjust =0.5),legend.position ="right" )
Boxplot usando ggthemes()
Tema: FiveThirtyEight (famoso site norte-americano de jornalismo de dados).
Estilo clean, jornalístico e moderno aos gráficos.
# Converte "education" para fator e ordena o eixo xdados_corrigidos$education <-factor( dados_corrigidos$education,levels =c("Below Secondary", "Bachelors", "Masters & above")) # Gráficoggplot(dados_corrigidos, aes(x = education, y = avg_training_score, fill = KPIs_met_more_than_80 )) +geom_boxplot(alpha =0.7, width =0.6, outlier.color ="red" ) +scale_fill_fivethirtyeight() +# Paleta de cores do estilo FiveThirtyEight.theme_fivethirtyeight() +# Define o tema do FiveThirtyEight.labs(title ="Pontuação em Treinamentos por Formação Acadêmica", subtitle ="Comparação entre funcionários com KPI > 80 e KPI ≤ 80", x ="Formação Acadêmica", y ="Pontuação Média em Treinamentos", fill ="KPI > 80" ) +scale_x_discrete(labels =c("Below Secondary"="Sem Ensino Superior","Bachelors"="Bacharelado","Masters & above"="Mestrado ou Superior" )) +theme(legend.position ="top", plot.title =element_text(size =14), plot.subtitle =element_text(size =11), axis.text.x =element_text(size =10), axis.title =element_text(size =11) )
Histograma
Distribuição das idades dos funcionários
# Gráficodados_corrigidos %>%ggplot(aes(x = age)) +geom_histogram(binwidth =1,fill ="#69b3a2",color ="white",alpha =0.85 ) +ggtitle("Distribuição das Idades dos Funcionários") +labs(x ="Idade",y ="Quantidade de Funcionários" ) +theme_minimal(base_size =13) +theme(plot.title =element_text(size =16, face ="bold", hjust =0.5),axis.text =element_text(size =11),axis.title =element_text(size =13, face ="bold"),panel.grid.minor =element_blank(),panel.grid.major.x =element_blank(),plot.margin =margin(12, 12, 12, 12) ) +scale_x_continuous(breaks =seq(min(dados_corrigidos$age), max(dados_corrigidos$age), 5) )
Histograma usando o ggthemes
Tema: Stata (software profissional de estatística).
# Gráficodados_corrigidos %>%ggplot(aes(x = age)) +geom_histogram(binwidth =1,fill ="#69b3a2",color ="white",alpha =0.85 ) +ggtitle("Distribuição das Idades dos Funcionários") +labs(x ="Idade",y ="Quantidade de Funcionários" ) +theme_stata(base_size =13) +# Tema Stata (pacote ggthemes)theme(plot.title =element_text(size =16, face ="bold", hjust =0.5),axis.text =element_text(size =11),axis.title =element_text(size =13, face ="bold"),panel.grid.minor =element_blank(),plot.margin =margin(12, 12, 12, 12) ) +scale_x_continuous(breaks =seq(min(dados_corrigidos$age), max(dados_corrigidos$age), 5) )
Gráfico de linhas facetado
Média de treinamentos ao longo do tempo de serviço
# Preparação dos dadosdados_linha2 <- dados_corrigidos %>%group_by(department, length_of_service) %>%summarise(media_treinamentos =mean(no_of_trainings), .groups ="drop") %>%drop_na() # Gráfico de linhas facetadoggplot(dados_linha2, aes(x = length_of_service, y = media_treinamentos, color = department)) +geom_line(size =0.7) +geom_point(size =1.5) +facet_wrap(~ department) +scale_color_viridis_d(option ="turbo") +# Paleta viridis scale_x_continuous(breaks = scales::pretty_breaks(n =8)) +scale_y_continuous(limits =c(1, 2)) +# mesmo intervalo em todas as facetaslabs(title ="Média de Treinamentos ao Longo do Tempo de Serviço",subtitle ="Resultados separados por departamento",x ="Tempo de vínculo (anos)",y ="Média do número de treinamentos",color ="Departamento" ) +theme_minimal(base_size =14) +theme(legend.position ="none", strip.text =element_text(face ="bold", size =13),plot.title =element_text(face ="bold", size =17),plot.subtitle =element_text(size =13, margin =margin(b =10)),panel.grid.minor =element_blank(),panel.grid.major.x =element_blank() )
Gráfico de barras usando o ggthemes
Tema: The Economist (replica o estilo visual dos gráficos publicados pela revista The Economist)
Design limpo, profissional e sofisticado.
Distribuição de funcionários por gênero e departamento
# Graficoggplot(dados_corrigidos, aes(x = department, fill = gender)) +geom_bar(position =position_dodge(width =0.8), width =0.7) +labs(title ="Distribuição de Funcionários por Gênero e Departamento",x ="Departamento",y ="Número de Funcionários",fill ="Gênero" ) +# Paleta e tema Economist (ggthemes)scale_fill_economist() +theme_economist() +# Ajustes estéticostheme(plot.title =element_text(face ="bold", size =15, hjust =0.5),plot.subtitle =element_text(size =10, hjust =0.5, margin =margin(b =10)),axis.text.x =element_text(angle =35, vjust =1, hjust =1),legend.position ="right",legend.text =element_text(size =9),legend.title =element_text(size =11),panel.grid.major.x =element_blank(),panel.grid.minor =element_blank(),axis.title =element_text(face ="bold") )
Python
Boxplot
Pontuação em treinamentos por formação acadêmica
from plotnine import ( ggplot, aes, geom_boxplot, labs, theme_minimal, scale_fill_manual, scale_x_discrete, theme, element_text)# Graficoplot1_python = (( ggplot(dados_corrigidos, aes( x='education', y='avg_training_score', fill='KPIs_met_more_than_80' ))+ geom_boxplot(alpha=0.7, outlier_color="red")+ labs( title="Relação entre Formação Acadêmica, Pontuação em Treinamentos e KPI > 80", x="Formação Acadêmica", y="Pontuação Média em Treinamentos", fill="KPI > 80" )+ scale_fill_manual(values={"Não": "#F8766D","Sim": "#00BFC4" })+ scale_x_discrete(labels={"Below Secondary": "Sem Ensino Superior","Bachelors": "Bacharelado","Masters & above": "Mestrado ou Superior" })+ theme_minimal()+ theme( plot_title=element_text(size=12, weight="bold"), axis_text_x=element_text(angle=0, ha="center") )))plot1_python.save("plot1_python.png", width=12, height=8, dpi=300)
Média de treinamentos ao longo do tempo de serviço
Pacote “seaborn”: facilita a criação de um gráfico facetado.
import pandas as pdimport matplotlib.pyplot as pltimport seaborn as sns#Preparação dos dados - Criar tabela POR DEPARTAMENTO e tempo de serviçodados_linha2 = (dados_corrigidos .groupby(['department', 'length_of_service']) .agg(media_treinamentos=('no_of_trainings', 'mean')) .reset_index())# Configurar estilosns.set_style("whitegrid")plt.rcParams['font.size'] =14# Gráfico g = sns.FacetGrid( dados_linha2, col='department', col_wrap=3, sharey=True, # Mesma escala em todas as facetas height=4, aspect=1.2)g.map( plt.plot,'length_of_service','media_treinamentos', color='#2C7BB6', linewidth=1.2, marker='o', markersize=5)g.fig.subplots_adjust(hspace=0.35, wspace=0.25)# Títulosg.set_axis_labels('Tempo de vínculo empregatício', 'Média do número de treinamentos')g.set_titles(col_template="{col_name}", size=12, weight='bold')g.fig.suptitle('Tempo de Vínculo x Média de Treinamentos por Departamento', fontsize=16, fontweight='bold', y=1.05)plt.tight_layout()# Salvar figuraplt.savefig('plot6_dep_python.png', dpi=300, bbox_inches='tight')plt.show()
Gráfico de barras
Quantidade de funcionários por genêro em cada departamento
import timeimport psutilimport osfrom plotnine import ( ggplot, aes, geom_bar, labs, theme_minimal, theme, element_rect, element_text, scale_fill_manual)# Gráficoplot7_python = ( ggplot(dados_corrigidos, aes(x='department', fill='gender'))+ geom_bar(position='dodge')+ labs( title="Distribuição de Funcionários por Gênero e Departamento", x="Departamento", y="Número de Funcionários", fill="Gênero" )+ scale_fill_manual( values={"masculino": "#1f77b4", # azul"feminino": "#ff69b4"# rosa } )+ theme_minimal(base_size=14)+ theme( panel_background=element_rect(fill="#E6F0FA", color=None), plot_background=element_rect(fill="#E6F0FA", color=None), axis_text_x=element_text(angle=25, ha="right"), plot_title=element_text(weight="bold", size=16) ))# Salvar o gráficoplot7_python.save("plot7_python.png", width=12, height=8, dpi=300)
Julia
Boxplot
usingDataFramesusingStatsPlotsusingPlotsresultado =@timedbegin plot1_julia =@df df groupedboxplot(:education_pt,:avg_training_score, group =:KPIs_met_more_than_80, fillalpha =0.7, outliers =true, outliercolor =:red, palette = ["#F8766D", "#00BFC4"], title ="Relação entre Formação Acadêmica, Pontuação em Treinamentos e KPI > 80", xlabel ="Formação Acadêmica", ylabel ="Pontuação Média em Treinamentos", label = ["Não""Sim"], legend =:topright, size = (1200, 800), titlefontsize =12, left_margin =10Plots.mm, bottom_margin =5Plots.mm ) Plots.savefig(plot1_julia, "plot1_julia.png") plot1_juliaend# Exibir o gráficodisplay(plot1_julia)
Histograma
Distribuição das idades dos funcionários
#using DataFrames#using StatsPlots#using Plots# Converter para DataFramedf =DataFrame(dados_corrigidos)# Criar histogramaresultado =@timedbeginplot4_julia =@df df Plots.histogram(:age, bins =minimum(:age):1:maximum(:age), fillcolor ="#69b3a2", linecolor ="#e9ecef", fillalpha =0.9, title ="Distribuição das idades dos funcionários", xlabel ="Idade", ylabel ="Quantidades de funcionários", legend =false, size = (1200, 800), titlefontsize =15,#titlefont = font(15, "bold"), guidefontsize =12, left_margin =10Plots.mm, bottom_margin =5Plots.mm)Plots.savefig(plot4_julia, "plot4_julia.png") plot4_juliaend# Exibir o gráficodisplay(plot4_julia)
Gráfico de linhas facetado
Média de treinamentos ao longo do tempo de serviço - separado por área
# Converter para DataFramedf =DataFrame(dados_corrigidos)# Preparação dos dados - Criar tabela agregada por departamento e tempo de serviçodados_linha2 =combine(groupby(df, [:department, :length_of_service]), :no_of_trainings => mean =>:media_treinamentos)# Ordenar por departamento e tempo de serviçosort!(dados_linha2, [:department, :length_of_service])# Obter departamentos únicosdepartments =sort(unique(dados_linha2.department))n_dept =length(departments)# Fixar a mesma escala para todas as facetasx_min, x_max =extrema(dados_linha2.length_of_service)y_min, y_max =extrema(dados_linha2.media_treinamentos)# Criar função para gerar os gráficos de linhasfunctioncreate_line_plot(dept_name, dados_linha2) df_dept =filter(row -> row.department == dept_name, dados_linha2)returnplot( df_dept.length_of_service, df_dept.media_treinamentos, linewidth =1.5, linecolor ="#2C7BB6", marker =:circle, markersize =3, markercolor ="#2C7BB6", markerstrokewidth =0, xlabel ="Tempo de vínculo", ylabel ="Média de treinamentos", title = dept_name, legend =false, titlefontsize =12, guidefontsize =10, tickfontsize =9, grid =true, gridstyle =:dot, gridalpha =0.3, xlim = (x_min, x_max), ylim = (y_min, y_max) )end# Plotar o gráficoresultado =@timedbegin plots_list = [create_line_plot(dept, dados_linha2) for dept in departments] n_rows =Int(ceil(n_dept /3)) plot6f_julia =plot( plots_list..., layout = (n_rows, 3), size = (1400, 1000), left_margin =10Plots.mm, bottom_margin =10Plots.mm )plot!(plot6f_julia, plot_title ="Tempo de Vínculo x Média de Treinamentos por Departamento", plot_titlefontsize =16) Plots.savefig(plot6f_julia, "plot6f_julia.png") plot6f_juliaenddisplay(plot6f_julia)
Quantidade de funcionários por genêro em cada departamento
# Converter para DataFramedf =DataFrame(dados_corrigidos)# Contar frequências por departmento e gênerocounts =combine(groupby(df, [:department, :gender]), nrow =>:count)# Preparação dos dadoscounts_wide =unstack(counts, :department, :gender, :count, fill=0)departments = counts_wide[:, 1]genders =names(counts_wide)[2:end]data_matrix =Matrix(counts_wide[:, 2:end])# Criar gráficoresultado =@timedbeginplot7_julia =groupedbar( data_matrix, bar_position =:dodge, xticks = (1:length(departments), departments), xrotation =25, label =permutedims(genders), title ="Distribuição de Funcionários por Gênero e Departamento", xlabel ="Departamento", ylabel ="Número de Funcionários", legend =:topright, size = (1200, 800), titlefontsize =16, guidefontsize =14, tickfontsize =12, legendfontsize =12, left_margin =10Plots.mm, bottom_margin =10Plots.mm, background_color ="#E6F0FA", palette = ["#FF9999", "#6699CC"])Plots.savefig(plot7_julia, "plot7_julia.png") plot7_juliaend# Exibir o gráficodisplay(plot7_julia)
Explorando Alternativas de Visualização em Julia
O principal pacote utilizado para gerar gráficos em Julia foi o Plots.jl, que apresentou bom desempenho e flexibilidade.
Ainda assim, buscamos explorar outras opções para entender melhor o ecossistema de visualização da linguagem.
Entre os pacotes encontrados, o UnicodePlots.jl se destacou por suas vantagens únicas:
Permite gerar gráficos diretamente no terminal, sem depender de janelas gráficas;
É extremamente leve e rápido, ideal para máquinas com poucos recursos;
Possui sintaxe simples e visualização imediata;
Essa diversidade mostra como Julia oferece diferentes soluções de visualização, cada uma adequada a necessidades específicas.
usingPlots# Define UnicodePlots como backendunicodeplots()# Criar histograma simplesages = df.agehistogram(ages; nbins =10, title ="Distribuição das idades dos funcionários", xlabel ="Idade", ylabel ="Quantidade de funcionários")
Plots.UnicodePlotsBackend()
⠀Distribuição das idades dos funcionários⠀
┌────────────────────────────────────────┐
5 010│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡏⠉⚬⠉⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│y1
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│y1
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⡧⠤⚬⠤⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⡏⠉⚬⠉⡇⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
Quantidade de funcionários│⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⡧⚬⠤⢤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⢸⠉⚬⠉⢹⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⠀⡇⠀⠀⢸⠀⠀⠀⢸⠉⚬⠉⢹⣀⚬⣀⣀⠀⠀⠀⠀⠀⠀│
0│⠀⠀⣖⣒⚬⣒⣇⣀⣀⣀⣇⣀⣀⣀⣇⣀⣀⣀⣇⣀⣀⣸⣀⣀⣀⣸⣀⣀⣀⣸⣀⣀⣀⣸⣀⚬⣀⣀⠀⠀│
└────────────────────────────────────────┘
⠀17.219⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Idade⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀67.781⠀
Qual é a melhor linguagem para visualização de dados?
Linguagem
Tempo médio de execução (s)
Memória usada (MB)
Linhas de código
R
15.91
1.81
105
Python
2.76
43.28
116
Julia
5.75
303.72
108
Observações:
Na linguagem R, o gráfico de linhas facetado é um outlier nas métricas. Seu tempo de execução foi de aproximadamente 62 segundos, enquanto os demais gráficos ficaram próximos de 1 segundo, além de apresentar maior consumo de memória.
Os gráficos produzidos com o pacote ggthemes não apresentaram diferenças significativas no número de linhas quando comparados aos demais.
Em Julia, notou-se também que o gráfico de boxplot consumiu aproximadamente 1GB, enquanto os outros não superaram 200MB.
Comparação dos Pacotes – Julia
Pacotes de Julia
Tempo médio de execução (s)
Memória usada (MB)
Linhas de código
Plots
21.18000
203.14
52
UnicodePlots
0.18675
3.08
32
Observações:
Para esta comparação utilizamos apenas 2 gráficos (os mesmos feitos com o UnicodePlots), por isso as medidas usando Plots divergem das apresentadas na tabela anterior.
Conclusão
De modo geral, as três linguagens apresentam vantagens e desvantagens para objetivos diferentes:
R foi a linguagem que apresentou menor uso de memória e maior diversidade de temas disponíveis para personalização dos gráficos;
Python teve o menor tempo médio de execução, embora ofereça menos opções de personalização visual;
Julia apresentou maior uso de memória e maior tempo de execução, mas conta com bibliotecas (não exploradas em profundidade neste trabalho) que podem otimizar esses fatores, além de boas possibilidades de personalização.
As três linguagens apresentam quantidades muito próximas de linhas de código.