From 7186e51ae19f431dc1a4401383d9fce865ccb2ba Mon Sep 17 00:00:00 2001 From: Pedro Castro Date: Thu, 18 Apr 2024 15:44:17 -0300 Subject: [PATCH] add br_inep_ideb --- dbt_project.yml | 3 + models/br_inep_ideb/br_inep_ideb__escola.sql | 17 + .../br_inep_ideb/br_inep_ideb__municipio.sql | 16 + models/br_inep_ideb/code/main.py | 300 ++++++++++++++++++ models/br_inep_ideb/schema.yml | 132 ++++++++ 5 files changed, 468 insertions(+) create mode 100644 models/br_inep_ideb/br_inep_ideb__escola.sql create mode 100644 models/br_inep_ideb/br_inep_ideb__municipio.sql create mode 100644 models/br_inep_ideb/code/main.py create mode 100644 models/br_inep_ideb/schema.yml diff --git a/dbt_project.yml b/dbt_project.yml index 196afaa8..93e7944a 100644 --- a/dbt_project.yml +++ b/dbt_project.yml @@ -186,6 +186,9 @@ models: br_inep_censo_escolar: +materialized: table +schema: br_inep_censo_escolar + br_inep_ideb: + +materialized: table + +schema: br_inep_ideb br_inep_enem: +materialized: table +schema: br_inep_enem diff --git a/models/br_inep_ideb/br_inep_ideb__escola.sql b/models/br_inep_ideb/br_inep_ideb__escola.sql new file mode 100644 index 00000000..b5e1c579 --- /dev/null +++ b/models/br_inep_ideb/br_inep_ideb__escola.sql @@ -0,0 +1,17 @@ +{{ config(alias="escola", schema="br_inep_ideb", materialized="table") }} +select + safe_cast(ano as int64) ano, + safe_cast(sigla_uf as string) sigla_uf, + safe_cast(id_municipio as string) id_municipio, + safe_cast(id_escola as string) id_escola, + safe_cast(rede as string) rede, + safe_cast(ensino as string) ensino, + safe_cast(anos_escolares as string) anos_escolares, + safe_cast(taxa_aprovacao as float64) taxa_aprovacao, + safe_cast(indicador_rendimento as float64) indicador_rendimento, + safe_cast(nota_saeb_matematica as float64) nota_saeb_matematica, + safe_cast(nota_saeb_lingua_portuguesa as float64) nota_saeb_lingua_portuguesa, + safe_cast(nota_saeb_media_padronizada as float64) nota_saeb_media_padronizada, + safe_cast(ideb as float64) ideb, + safe_cast(projecao as float64) projecao, +from `basedosdados-staging.br_inep_ideb_staging.escola` as t diff --git a/models/br_inep_ideb/br_inep_ideb__municipio.sql b/models/br_inep_ideb/br_inep_ideb__municipio.sql new file mode 100644 index 00000000..d906cfcc --- /dev/null +++ b/models/br_inep_ideb/br_inep_ideb__municipio.sql @@ -0,0 +1,16 @@ +{{ config(alias="municipio", schema="br_inep_ideb", materialized="table") }} +select + safe_cast(ano as int64) ano, + safe_cast(sigla_uf as string) sigla_uf, + safe_cast(id_municipio as string) id_municipio, + safe_cast(rede as string) rede, + safe_cast(ensino as string) ensino, + safe_cast(anos_escolares as string) anos_escolares, + safe_cast(taxa_aprovacao as float64) taxa_aprovacao, + safe_cast(indicador_rendimento as float64) indicador_rendimento, + safe_cast(nota_saeb_matematica as float64) nota_saeb_matematica, + safe_cast(nota_saeb_lingua_portuguesa as float64) nota_saeb_lingua_portuguesa, + safe_cast(nota_saeb_media_padronizada as float64) nota_saeb_media_padronizada, + safe_cast(ideb as float64) ideb, + safe_cast(projecao as float64) projecao, +from `basedosdados-staging.br_inep_ideb_staging.municipio` as t diff --git a/models/br_inep_ideb/code/main.py b/models/br_inep_ideb/code/main.py new file mode 100644 index 00000000..c939aaf6 --- /dev/null +++ b/models/br_inep_ideb/code/main.py @@ -0,0 +1,300 @@ +import pandas as pd +import numpy as np +import os +import zipfile +import basedosdados as bd + + +INPUT = os.path.join(os.getcwd(), "input") +OUTPUT = os.path.join(os.getcwd(), "output") + +os.makedirs(INPUT, exist_ok=True) +os.makedirs(OUTPUT, exist_ok=True) + +URLS = { + "municipios_ef_anos_iniciais": "https://download.inep.gov.br/educacao_basica/portal_ideb/planilhas_para_download/2019/divulgacao_anos_iniciais_municipios_2019.zip", + "municipios_ef_anos_finais": "https://download.inep.gov.br/educacao_basica/portal_ideb/planilhas_para_download/2019/divulgacao_anos_finais_municipios_2019.zip", + "escolas_ef_anos_iniciais": "https://download.inep.gov.br/educacao_basica/portal_ideb/planilhas_para_download/2019/divulgacao_anos_iniciais_escolas_2019.zip", + "escolas_ef_anos_finais": "https://download.inep.gov.br/educacao_basica/portal_ideb/planilhas_para_download/2019/divulgacao_anos_finais_escolas_2019.zip", +} + + +for name, url in URLS.items(): + os.system(f"cd {INPUT}; curl -O -k {url}") + +for file in os.listdir(INPUT): + with zipfile.ZipFile(os.path.join(INPUT, file), "r") as z: + z.extractall(INPUT) + + +cols = ["VL_NOTA_MATEMATICA_2015", "VL_NOTA_PORTUGUES_2015"] + +cols_municipio = ["CO_MUNICIPIO", "REDE", *cols] + +cols_escolas = ["CO_MUNICIPIO", "ID_ESCOLA", "REDE", *cols] + + +def to_float(value) -> float: + return ( + float(value.replace(",", ".").replace("*", "")) + if value not in ["-", "ND", "ND*", "ND**"] + else np.nan + ) + + +def sanitize_dataframe(df: pd.DataFrame, table_escolas: bool) -> pd.DataFrame: + df = df[cols_escolas if table_escolas else cols_municipio] # type: ignore + + df = df.loc[df["CO_MUNICIPIO"].notna(),] + + df["CO_MUNICIPIO"] = df["CO_MUNICIPIO"].astype("Int64").astype("string") + + if table_escolas: + df["ID_ESCOLA"] = df["ID_ESCOLA"].astype("Int64").astype("string") + + df["REDE"] = df["REDE"].str.lower() + + df["VL_NOTA_MATEMATICA_2015"] = df["VL_NOTA_MATEMATICA_2015"].apply(to_float) + + df["VL_NOTA_PORTUGUES_2015"] = df["VL_NOTA_PORTUGUES_2015"].apply(to_float) + + return df + + +escolas_inicial = pd.read_excel( + os.path.join( + INPUT, + "divulgacao_anos_iniciais_escolas_2019", + "divulgacao_anos_iniciais_escolas_2019.xlsx", + ), + skiprows=9, +) + +escolas_inicial = sanitize_dataframe(escolas_inicial, table_escolas=True) + +escolas_final = pd.read_excel( + os.path.join( + INPUT, + "divulgacao_anos_finais_escolas_2019", + "divulgacao_anos_finais_escolas_2019.xlsx", + ), + skiprows=9, +) + +escolas_final = sanitize_dataframe(escolas_final, table_escolas=True) + +bd_escolas_inicial = bd.read_sql( + """ +SELECT + * +FROM + `basedosdados.br_inep_ideb.escola` +WHERE + ano = 2015 + and anos_escolares = "iniciais (1-5)" +""", + billing_project_id="basedosdados-dev", +) + +bd_escolas_final = bd.read_sql( + """ +SELECT + * +FROM + `basedosdados.br_inep_ideb.escola` +WHERE + ano = 2015 + and anos_escolares = "finais (6-9)" +""", + billing_project_id="basedosdados-dev", +) + +fixed_escolas_inicial = ( + bd_escolas_inicial.merge( # type: ignore + escolas_inicial, + left_on=["id_municipio", "id_escola", "rede"], + right_on=["CO_MUNICIPIO", "ID_ESCOLA", "REDE"], + ) + .drop( + columns=[ + "nota_saeb_matematica", + "nota_saeb_lingua_portuguesa", + "REDE", + "CO_MUNICIPIO", + "ID_ESCOLA", + ] + ) + .rename( + columns={ + "VL_NOTA_MATEMATICA_2015": "nota_saeb_matematica", + "VL_NOTA_PORTUGUES_2015": "nota_saeb_lingua_portuguesa", + }, + errors="raise", + )[bd_escolas_inicial.columns] # type: ignore +) + +fixed_escolas_final = ( + bd_escolas_final.merge( # type: ignore + escolas_final, + left_on=["id_municipio", "id_escola", "rede"], + right_on=["CO_MUNICIPIO", "ID_ESCOLA", "REDE"], + ) + .drop( + columns=[ + "nota_saeb_matematica", + "nota_saeb_lingua_portuguesa", + "REDE", + "CO_MUNICIPIO", + "ID_ESCOLA", + ] + ) + .rename( + columns={ + "VL_NOTA_MATEMATICA_2015": "nota_saeb_matematica", + "VL_NOTA_PORTUGUES_2015": "nota_saeb_lingua_portuguesa", + }, + errors="raise", + )[bd_escolas_inicial.columns] # type: ignore +) + +escolas_without_2015 = bd.read_sql( + """ +SELECT + * +FROM + `basedosdados.br_inep_ideb.escola` +WHERE ano <> 2015 +""", + billing_project_id="basedosdados-dev", +) + +pd.concat( + [escolas_without_2015, fixed_escolas_inicial, fixed_escolas_final] # type: ignore +).sort_values(["ano", "sigla_uf"]).to_csv( + os.path.join(OUTPUT, "escola.csv"), index=False +) + +del escolas_without_2015 +del fixed_escolas_inicial +del fixed_escolas_final +del bd_escolas_inicial +del bd_escolas_final + +# Municipios + +municipios_inicial = pd.read_excel( + os.path.join(INPUT, "divulgacao_anos_iniciais_municipios_2019.xlsx"), skiprows=9 +) +municipios_final = pd.read_excel( + os.path.join(INPUT, "divulgacao_anos_finais_municipios_2019.xlsx"), skiprows=9 +) + +municipios_inicial = sanitize_dataframe(municipios_inicial, table_escolas=False) + +municipios_final = sanitize_dataframe(municipios_final, table_escolas=False) + + +bd_municipios_inicial = bd.read_sql( + """ +SELECT + * +FROM + `basedosdados.br_inep_ideb.municipio` +WHERE + ano = 2015 + and anos_escolares = "iniciais (1-5)" +""", + billing_project_id="basedosdados-dev", +) + +bd_municipios_final = bd.read_sql( + """ +SELECT + * +FROM + `basedosdados.br_inep_ideb.municipio` +WHERE + ano = 2015 + and anos_escolares = "finais (6-9)" +""", + billing_project_id="basedosdados-dev", +) + +fixed_municipios_inicial = ( + bd_municipios_inicial.merge( # type: ignore + municipios_inicial, + left_on=["id_municipio", "rede"], + right_on=["CO_MUNICIPIO", "REDE"], + ) + .drop( + columns=[ + "nota_saeb_matematica", + "nota_saeb_lingua_portuguesa", + "REDE", + "CO_MUNICIPIO", + ] + ) + .rename( + columns={ + "VL_NOTA_MATEMATICA_2015": "nota_saeb_matematica", + "VL_NOTA_PORTUGUES_2015": "nota_saeb_lingua_portuguesa", + }, + errors="raise", + )[bd_municipios_inicial.columns] # type: ignore +) + +fixed_municipios_final = ( + bd_municipios_final.merge( # type: ignore + municipios_final, + left_on=["id_municipio", "rede"], + right_on=["CO_MUNICIPIO", "REDE"], + ) + .drop( + columns=[ + "nota_saeb_matematica", + "nota_saeb_lingua_portuguesa", + "REDE", + "CO_MUNICIPIO", + ] + ) + .rename( + columns={ + "VL_NOTA_MATEMATICA_2015": "nota_saeb_matematica", + "VL_NOTA_PORTUGUES_2015": "nota_saeb_lingua_portuguesa", + }, + errors="raise", + )[bd_municipios_final.columns] # type: ignore +) + +municipios_without_2015 = bd.read_sql( + """ +SELECT + * +FROM + `basedosdados.br_inep_ideb.municipio` +WHERE ano <> 2015 +""", + billing_project_id="basedosdados-dev", +) + +pd.concat( + [municipios_without_2015, fixed_municipios_inicial, fixed_municipios_final] # type: ignore +).sort_values(["ano", "sigla_uf"]).to_csv( + os.path.join(OUTPUT, "municipio.csv"), index=False +) + +tb_escola = bd.Table(dataset_id="br_inep_ideb", table_id="escola") + +tb_escola.create( + os.path.join(OUTPUT, "escola.csv"), + if_table_exists="replace", + if_storage_data_exists="replace", +) + +tb_municipio = bd.Table(dataset_id="br_inep_ideb", table_id="municipio") + +tb_municipio.create( + os.path.join(OUTPUT, "municipio.csv"), + if_table_exists="replace", + if_storage_data_exists="replace", +) diff --git a/models/br_inep_ideb/schema.yml b/models/br_inep_ideb/schema.yml new file mode 100644 index 00000000..826e28fb --- /dev/null +++ b/models/br_inep_ideb/schema.yml @@ -0,0 +1,132 @@ +--- +version: 2 +models: + - name: br_inep_ideb__municipio + description: Dados do Ideb agregados para o Brasil a nível de municipio-rede-ensino-ano. + tests: + - dbt_utils.unique_combination_of_columns: + combination_of_columns: + - ano + - sigla_uf + - id_municipio + - rede + - ensino + - anos_escolares + - not_null_proportion_multiple_columns: + at_least: 0.05 + columns: + - name: ano + description: Ano + tests: + - relationships: + to: ref('br_bd_diretorios_data_tempo__ano') + field: ano.ano + - name: sigla_uf + description: Sigla da Unidade da Federação + tests: + - relationships: + to: ref('br_bd_diretorios_brasil__uf') + field: sigla + - name: id_municipio + description: ID Município - IBGE 7 Dígitos + tests: + - relationships: + to: ref('br_bd_diretorios_brasil__municipio') + field: id_municipio + - name: rede + description: Rede Escolar + tests: + - accepted_values: + values: [municipal, estadual, federal, privada, publica] + - name: ensino + description: Tipo de Ensino + tests: + - accepted_values: + values: [fundamental, medio] + - name: anos_escolares + description: Anos Escolares + tests: + - accepted_values: + values: [iniciais (1-5), finais (6-9), todos (1-4)] + - name: taxa_aprovacao + description: Taxa de Aprovação + - name: indicador_rendimento + description: Indicador de Rendimento (P) + - name: nota_saeb_matematica + description: Nota SAEB - Matemática + - name: nota_saeb_lingua_portuguesa + description: Nota SAEB - Língua Portuguesa + - name: nota_saeb_media_padronizada + description: Nota SAEB - Média Padronizada (N) + - name: ideb + description: IDEB (N x P) + - name: projecao + description: Projeção + - name: br_inep_ideb__escola + description: Dados do Ideb agregados para o Brasil a nível de escola-ano. + tests: + - dbt_utils.unique_combination_of_columns: + combination_of_columns: + - ano + - sigla_uf + - id_municipio + - id_escola + - rede + - ensino + - anos_escolares + - not_null_proportion_multiple_columns: + at_least: 0.05 + columns: + - name: ano + description: Ano + tests: + - relationships: + to: ref('br_bd_diretorios_data_tempo__ano') + field: ano.ano + - name: sigla_uf + description: Sigla da Unidade da Federação + tests: + - relationships: + to: ref('br_bd_diretorios_brasil__uf') + field: sigla + - name: id_municipio + description: ID Município - IBGE 7 Dígitos + tests: + - relationships: + to: ref('br_bd_diretorios_brasil__municipio') + field: id_municipio + - name: id_escola + description: ID Escola - INEP + tests: + - relationships: + to: ref('br_bd_diretorios_brasil__escola') + field: id_escola + - name: rede + description: Rede Escolar + tests: + - accepted_values: + values: [municipal, estadual, federal, privada, publica] + - name: ensino + description: Tipo de Ensino + tests: + - accepted_values: + values: [fundamental, medio] + - name: anos_escolares + description: Anos Escolares + tests: + - accepted_values: + values: [iniciais (1-5), finais (6-9), todos (1-4)] + - name: taxa_aprovacao + description: Taxa de Aprovação + - name: indicador_rendimento + description: Indicador de Rendimento + - name: nota_saeb_matematica + description: Nota SAEB - Matemática + - name: nota_saeb_lingua_portuguesa + description: Nota SAEB - Língua Portuguesa + - name: nota_saeb_media_padronizada + description: Nota SAEB - Média Padronizada + - name: ideb + description: IDEB (Média Padronizada x Indicador de rendimento) + - name: projecao + description: Projeção