<jupyter_start><jupyter_text>- https://altair-viz.github.io/user_guide/configuration.html- https://qualar.apambiente.pt/node/metodo-calculo-indices- https://github.com/altair-viz/altair/issues/2176issuecomment-633679510- https://github.com/altair-viz/altair/issues/1768issuecomment-549370312- https://altair-viz.github.io/user_guide/transform/impute.html- https://stackoverflow.com/a/15705958- https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.transform.html- https://tailwindcss.com/docs/font-size<jupyter_code>DATA: str = "https://raw.githubusercontent.com/dssgPT/Plotting-Good-DSSG/main/desafios/002_Qualidade_do_Ar_em_Portugal_EEA/qualidade_do_ar.csv"
df = pd.read_csv(DATA, index_col=0)
df["cidade"] = df["cidade"].str.strip()
min_year = df["ano"].min()
max_year = df["ano"].max()

print(min_year, max_year)
df.groupby(["cidade", "nome_estacao"]).size()
df["poluente"].unique()<jupyter_output><empty_output><jupyter_text>| Classificação |   PM10   |  PM2.5 |    NO2   |    O3   |    SO2   ||---------------|:--------:|:------:|:--------:|:-------:|:--------:|| Muito Bom     |   0-20   |  0-10  |   0-40   |   0-80  |   0-100  || Bom           |   21-35  |  11-20 |  41-100  |  81-100 |  101-200 || Médio         |   36-50  |  21-25 |  101-200 | 101-180 |  201-350 || Fraco         |  51-100  |  26-50 |  201-400 | 181-240 |  351-500 || Mau           | 101-1200 | 51-800 | 401-1000 | 241-600 | 501-1250 |<jupyter_code>cols = [
    "cidade",
    "poluente",
    "metrica",
    "nome_estacao",
    "ano",
    "nível_de_poluicao",
    "indice_qualidade_do_ar",
    "unidade_de_nivel_de_poluicao",
]

poluente = "NO2"
poluente_domain = [0, 40]

df_lx = df.query(
    "poluente == @poluente & metrica == 'Média anual' & (nome_estacao == 'Joaquim Magalhães' | nome_estacao == 'Beato')"
).sort_values(by=["ano", "nome_estacao"])[cols]
df_lx
# df_lx["ano"].nunique() == (df_lx["ano"].max() - df_lx["ano"].min() + 1)
# df_lx["ano"] = pd.to_datetime(df_lx["ano"], format="%Y")
df_lx["maxs"] = df_lx.groupby(["nome_estacao", "poluente"])[
    "nível_de_poluicao"
].transform("max")
df_lx["mins"] = df_lx.groupby(["nome_estacao", "poluente"])[
    "nível_de_poluicao"
].transform("min")
# df_lx.head()
df_lx[
    (df_lx["nível_de_poluicao"] == df_lx["maxs"])
    | (df_lx["nível_de_poluicao"] == df_lx["mins"])
]
# maxs = df_lx.groupby("nome_estacao", as_index=False).max()
# maxs["queries"] = (
#     "(datum.nome_estacao === '"
#     + maxs["nome_estacao"]
#     + "' && datum.nível_de_poluicao === "
#     + maxs["nível_de_poluicao"].astype(str)
#     + ")"
# )
# " || ".join(maxs["queries"])
# mins = df_lx.groupby("nome_estacao", as_index=False).min()
# mins["queries"] = (
#     "(datum.nome_estacao === '"
#     + mins["nome_estacao"]
#     + "' && datum.nível_de_poluicao === "
#     + mins["nível_de_poluicao"].astype(str)
#     + ")"
# )
# " || ".join(mins["queries"])
# point_color_query = " || ".join(maxs["queries"]) + " || " + " || ".join(mins["queries"])
# point_color_query
FONT: str = "Rubik"
FONT_SIZE_SM: int = 10
FONT_SIZE_MD: int = 12
base = alt.Chart(df_lx).encode(
    x=alt.X("ano:O", axis=alt.Axis(title=None, grid=True)),
    y=alt.Y(
        "nível_de_poluicao:Q",
        axis=alt.Axis(title=None),
        scale=alt.Scale(domain=poluente_domain),
        impute=alt.ImputeParams(
            value=None, keyvals=list(range(min_year, max_year + 1))
        ),
    ),
    color=alt.Color("nome_estacao:N"),
)

line = base.mark_line()

point = base.mark_circle(tooltip=True, size=30).encode(
    color=alt.condition(
        # point_color_query,
        (alt.datum["nível_de_poluicao"] == alt.datum["maxs"])
        | (alt.datum["nível_de_poluicao"] == alt.datum["mins"]),
        alt.value("red"),
        alt.value("blue"),
    )
)

(line + point).configure_axis(labelFont=FONT, labelFontSize=FONT_SIZE_SM).properties(
    width=300, height=150, title=poluente
).configure_view(strokeWidth=0).configure_legend(
    labelFont=FONT, labelFontSize=FONT_SIZE_MD
)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.21133764296369964, 'fraction_numerical': 0.04699154649428145, 'mean_word_length': 3.6347926267281108, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 9, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '438851', 'license': 'MIT', 'n_tokens_mistral': 1847, 'n_tokens_neox': 1688, 'n_words': 183, 'path': '02/data/line_chart.ipynb', 'repo_name': 'joaopalmeiro/plotting-good', 'size': '4022'}