import pandas as pd
import glob
import re
import warnings
from datetime import datetime
import plotly.graph_objs as go
import plotly.express as px
import plotly.colors as pcAnálise Básica de Dados da Plataforma Sucupira - Tempo de Titulação
Sobre
Neste documento veremos como criar alguns gráficos sobre o tempo de titulação dos discentes do programa.
Na seção Baixando os dados obtivemos vários dados da Plataforma Sucupira, inclusive dados sobre discentes. Os dados foram baixados de duas fontes diferentes na plataforma: planilhas com dados já homologados e a planilha do ano corrente. Como as duas fontes criam arquivos com estruturas diferentes precisamos unificá-las em um dataframe.
Unificando os dados
Antes de iniciar o processamento de dados para unificação das planilhas vamos importar as bibliotecas necessárias:
Os dados sobre os discentes de cada ano estão nas abas Discentes das planilhas baixadas anteriormente. A figura abaixo mostra a aba, com alguns campos expandidos:
Podemos ver que dados dos discentes podem ocupar mais de uma linha na planilha, caso tenham mais de um orientador e/ou mais de uma situação no ano da coleta. Como queremos somente os dados de alunos cuja Situação Discente seja igual a TITULADO basta considerar somente estas linhas das planilhas e remover as duplicadas (que ocorrerão quando tivermos mais de um orientador).
def preprocessaDiscentesTitulados(arquivo):
# Lemos a planilha
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=UserWarning,
module=re.escape('openpyxl.styles.stylesheet'))
df = pd.read_excel(arquivo,engine='openpyxl',sheet_name='Discentes')
# Definimos as colunas de interesse.
colunas = [
"Ano do Calendário",
"Nome Discente",
"Nível Discente",
"Situação Discente",
"Data Matrícula",
"Data Titulação"
]
# Filtramos pelas colunas.
df = df[colunas]
# Garantimos que estes campos serão datas:
df["Data Matrícula"] = pd.to_datetime(df["Data Matrícula"], format="%d/%m/%Y")
df["Data Titulação"] = pd.to_datetime(df["Data Titulação"], format="%d/%m/%Y")
# Filtramos por 'TITULADO'
df_filtered = df[df["Situação Discente"] == "TITULADO"]
# Removemos duplicatas
df_filtered = df_filtered.drop_duplicates(subset=colunas, keep='first')
return df_filteredAs abas Discentes das planilhas de anos anteriores ao atual tem outras informações que podem ser interessantes para análise de discentes por gênero e raça. Ignoraremos estes dados pois os mesmos não estão disponíveis na planilha do ano corrente.
Com isto podemos criar um dataframe com todas as planilhas de anos anteriores:
dir = "Resources/Data/ColetaSucupira/"
fileNames = glob.glob(dir+"relatorio_dados_enviados_coleta_20??.xlsx")
dfs = []
for file in fileNames:
df = preprocessaDiscentesTitulados(file)
dfs.append(df)
dfconcatenado = pd.concat(dfs, ignore_index=True) Vamos ver parte do dataframe:
dfconcatenado| Ano do Calendário | Nome Discente | Nível Discente | Situação Discente | Data Matrícula | Data Titulação | |
|---|---|---|---|---|---|---|
| 0 | 2015 | FELIPE RODOLFO SANTOS CORREA | Mestrado | TITULADO | 2012-03-01 | 2015-05-12 |
| 1 | 2015 | GUSTAVO FURTADO DE OLIVEIRA ALVES | Mestrado | TITULADO | 2012-03-01 | 2015-05-14 |
| 2 | 2015 | JULIANA APARECIDA ANOCHI | Doutorado | TITULADO | 2010-12-01 | 2015-11-03 |
| 3 | 2015 | LUCIANA BRASIL REBELO DOS SANTOS | Doutorado | TITULADO | 2010-09-01 | 2015-10-02 |
| 4 | 2015 | MANOEL BAPTISTA DA SILVA JUNIOR | Mestrado | TITULADO | 2012-06-01 | 2015-05-29 |
| ... | ... | ... | ... | ... | ... | ... |
| 134 | 2016 | LUIS FERNANDO AMORIM FRANCA | Doutorado | TITULADO | 2011-03-01 | 2016-05-16 |
| 135 | 2016 | MARIA EUGENIA SAUSEN WELTER | Mestrado | TITULADO | 2013-04-12 | 2016-05-27 |
| 136 | 2016 | MARILYN MENECUCCI IBANEZ | Doutorado | TITULADO | 2016-02-13 | 2016-02-19 |
| 137 | 2016 | MARLUCE DA CRUZ SCARABELLO | Doutorado | TITULADO | 2012-06-01 | 2016-09-01 |
| 138 | 2016 | VANDER LUIS DE SOUZA FREITAS | Doutorado | TITULADO | 2016-03-03 | 2016-03-03 |
139 rows × 6 columns
Já é possível observar alguns erros de preenchimento, que serão processados posteriormente.
Como queremos também usar os dados da planilha do ano corrente precisamos importá-la, filtrar os dados e converter em um dataframe para unificação (mantendo os mesmos campos). Mas a planilha dos dados do ano corrente tem algumas diferenças: como mostrado na seção Baixando os dados a planilha tem somente uma aba (Discentes - Orientadores), não apresenta o ano do calendário e tem um campo Data Situação em vez de Data Titulação, comforme mostrado na figura a seguir.
Precisaremos de uma função modificada para ler esta planilha para unificá-la com os dados dos anos passados:
def preprocessaDiscentesTituladosAnoAtual(arquivo,ano_calendario):
# Lemos a planilha
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=UserWarning,
module=re.escape('openpyxl.styles.stylesheet'))
df = pd.read_excel(arquivo,engine='openpyxl',sheet_name='Discentes - Orientadores')
# Definimos as colunas de interesse.
colunas = [
"Nome Discente",
"Nível Discente",
"Situação Discente",
"Data Matrícula",
"Data Situação" # Campo com nome diferente!
]
# Filtramos pelas colunas.
df = df[colunas]
# Indicamos o ano do calendário explicitamente.
df["Ano do Calendário"] = ano_calendario
# Garantimos que estes campos serão datas:
df["Data Matrícula"] = pd.to_datetime(df["Data Matrícula"], format="%d/%m/%Y")
df["Data Situação"] = pd.to_datetime(df["Data Situação"], format="%d/%m/%Y")
# Filtramos por 'TITULADO'
df_filtered = df[df["Situação Discente"] == "TITULADO"]
# Removemos duplicatas
df_filtered = df_filtered.drop_duplicates(subset=colunas, keep='first')
# Renomeamos 'Data Situação' para 'Data Titulação'
df_filtered.rename(columns={"Data Situação": "Data Titulação"}, inplace=True)
return df_filteredTodos os dataframes podem ser unificados com:
arquivo = dir+"Discentes2024.xlsx"
dfAnoAtual = preprocessaDiscentesTituladosAnoAtual(arquivo,2024)
df = pd.concat([dfconcatenado, dfAnoAtual], ignore_index=True)Pequenas correções
Uma rápida vista nos dados importados mostra que existem alguns erros de preenchimento que acabaram passando para as planilhas. Os mais óbvios são sobre as datas de matrícula, que em alguns casos tem o mesmo ano que as datas de titulação. Verifiquemos com uma contagem mais flexível, que indica quantos mestres se formaram em menos de 18 meses e quantos doutores em menos de 24:
df["Tempo de Titulação (meses)"] = (df["Data Titulação"] - df["Data Matrícula"]).dt.days / 30
# Contamos casos suspeitos:
mMin = 18
dMin = 24
mErro = df[(df["Nível Discente"] == "Mestrado") & (df["Tempo de Titulação (meses)"] < mMin)].shape[0]
dErro = df[(df["Nível Discente"] == "Doutorado") & (df["Tempo de Titulação (meses)"] < dMin)].shape[0]
mErro,dErro(0, 2)
Podemos descartar os dados suspeitos com estas linhas:
dfOK = df[
~(
((df["Nível Discente"] == "Mestrado") & (df["Tempo de Titulação (meses)"] < mMin)) |
((df["Nível Discente"] == "Doutorado") & (df["Tempo de Titulação (meses)"] < dMin))
)
]Podemos armazenar este dataframe em um arquivo para outros usos:
dfOK.to_csv(dir+"Discentes-TempoDeTitulacao.csv",index=False) Análise Exploratória de Dados
Vamos ver duas séries de boxplots, uma para o mestrado:
df_mes = dfOK[dfOK["Nível Discente"] == "Mestrado"]
count_mes = df_mes["Ano do Calendário"].value_counts().sort_index()
# Criamos o box plot para Mestrado
fig = px.box(df_mes,
x="Ano do Calendário",
y="Tempo de Titulação (meses)",
title="Distribuição do Tempo de Titulação - Mestrado",
labels={"Tempo de Titulação (meses)": "Tempo de Titulação (meses)",
"Ano do Calendário": "Ano"},
color_discrete_sequence=["#F3801B"])
# Adiciono linhas para os prazos.
fig.add_hline(y=24, line_color="blue", annotation_text="24 meses", annotation_position="top left")
fig.add_hline(y=36, line_color="red", annotation_text="36 meses", annotation_position="top left")
# Ajusto para o eixo Y iniciar de zero.
max_y_mes = df_mes["Tempo de Titulação (meses)"].max() + 6
fig.update_yaxes(range=[0, max_y_mes])
# Adicionamos número de titulados por ano no gráfico de Mestrado
for ano, count in count_mes.items():
fig.add_annotation(x=ano, y=max_y_mes-3, text=f"{count} titulados",
showarrow=False, font=dict(size=12, color="black"))
fig.show() E uma para o doutorado:
df_dou = dfOK[dfOK["Nível Discente"] == "Doutorado"]
count_dou = df_dou["Ano do Calendário"].value_counts().sort_index()
anos_ordenados = sorted(df_dou["Ano do Calendário"].unique())
# Criamos o box plot para Doutorado
fig = px.box(df_dou,
x="Ano do Calendário",
y="Tempo de Titulação (meses)",
title="Distribuição do Tempo de Titulação - Doutorado",
labels={"Tempo de Titulação (meses)": "Tempo de Titulação (meses)",
"Ano do Calendário": "Ano"},
color_discrete_sequence=["#1268AF"])
# Adiciono linhas para os prazos.
fig.add_hline(y=36, line_color="blue", annotation_text="36 meses", annotation_position="top left")
fig.add_hline(y=60, line_color="red", annotation_text="60 meses", annotation_position="top left")
# Ajusto para o eixo Y iniciar de zero.
max_y_dou = df_dou["Tempo de Titulação (meses)"].max() + 6
fig.update_yaxes(range=[0, max_y_dou])
fig.update_xaxes(dtick=1)
# Adicionamos número de titulados por ano no gráfico de Doutorado
for ano, count in count_dou.items():
fig.add_annotation(x=ano, y=max_y_dou-3, text=f"{count} titulados",
showarrow=False, font=dict(size=12, color="black"))
fig.show()

