{ "nbformat": 4, "nbformat_minor": 0, "metadata": { "colab": { "name": "lesson_12-R.ipynb", "provenance": [], "collapsed_sections": [] }, "kernelspec": { "name": "ir", "display_name": "R" }, "language_info": { "name": "R" }, "coopTranslator": { "original_hash": "fab50046ca413a38939d579f8432274f", "translation_date": "2025-08-29T23:51:48+00:00", "source_file": "4-Classification/3-Classifiers-2/solution/R/lesson_12-R.ipynb", "language_code": "br" } }, "cells": [ { "cell_type": "markdown", "metadata": { "id": "jsFutf_ygqSx" }, "source": [ "# Construir um modelo de classificação: Deliciosas culinárias asiáticas e indianas\n" ] }, { "cell_type": "markdown", "metadata": { "id": "HD54bEefgtNO" }, "source": [ "## Classificadores de culinária 2\n", "\n", "Nesta segunda lição de classificação, exploraremos `mais maneiras` de classificar dados categóricos. Também aprenderemos sobre as implicações de escolher um classificador em vez de outro.\n", "\n", "### [**Quiz pré-aula**](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/23/)\n", "\n", "### **Pré-requisitos**\n", "\n", "Assumimos que você completou as lições anteriores, já que continuaremos com alguns conceitos que aprendemos antes.\n", "\n", "Para esta lição, precisaremos dos seguintes pacotes:\n", "\n", "- `tidyverse`: O [tidyverse](https://www.tidyverse.org/) é uma [coleção de pacotes R](https://www.tidyverse.org/packages) projetada para tornar a ciência de dados mais rápida, fácil e divertida!\n", "\n", "- `tidymodels`: O [tidymodels](https://www.tidymodels.org/) é um [framework de pacotes](https://www.tidymodels.org/packages/) para modelagem e aprendizado de máquina.\n", "\n", "- `themis`: O [pacote themis](https://themis.tidymodels.org/) fornece etapas extras de receitas para lidar com dados desbalanceados.\n", "\n", "Você pode instalá-los com o seguinte comando:\n", "\n", "`install.packages(c(\"tidyverse\", \"tidymodels\", \"kernlab\", \"themis\", \"ranger\", \"xgboost\", \"kknn\"))`\n", "\n", "Alternativamente, o script abaixo verifica se você possui os pacotes necessários para completar este módulo e os instala caso estejam ausentes.\n" ] }, { "cell_type": "code", "metadata": { "id": "vZ57IuUxgyQt" }, "source": [ "suppressWarnings(if (!require(\"pacman\"))install.packages(\"pacman\"))\n", "\n", "pacman::p_load(tidyverse, tidymodels, themis, kernlab, ranger, xgboost, kknn)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "z22M-pj4g07x" }, "source": [ "## **1. Um mapa de classificação**\n", "\n", "Na nossa [lição anterior](https://github.com/microsoft/ML-For-Beginners/tree/main/4-Classification/2-Classifiers-1), tentamos responder à pergunta: como escolher entre vários modelos? Em grande parte, isso depende das características dos dados e do tipo de problema que queremos resolver (por exemplo, classificação ou regressão?).\n", "\n", "Anteriormente, aprendemos sobre as várias opções disponíveis para classificar dados usando o guia da Microsoft. O framework de Machine Learning do Python, Scikit-learn, oferece um guia semelhante, mas mais detalhado, que pode ajudar ainda mais a restringir seus estimadores (outro termo para classificadores):\n", "\n", "

\n", " \n", "

\n" ] }, { "cell_type": "markdown", "metadata": { "id": "u1i3xRIVg7vG" }, "source": [ "> Dica: [visite este mapa online](https://scikit-learn.org/stable/tutorial/machine_learning_map/) e clique ao longo do caminho para ler a documentação.\n", ">\n", "> O [site de referência do Tidymodels](https://www.tidymodels.org/find/parsnip/#models) também oferece uma excelente documentação sobre diferentes tipos de modelos.\n", "\n", "### **O plano** 🗺️\n", "\n", "Este mapa é muito útil quando você tem uma compreensão clara dos seus dados, pois pode 'caminhar' pelos seus caminhos até chegar a uma decisão:\n", "\n", "- Temos \\>50 amostras\n", "\n", "- Queremos prever uma categoria\n", "\n", "- Temos dados rotulados\n", "\n", "- Temos menos de 100K amostras\n", "\n", "- ✨ Podemos escolher um Linear SVC\n", "\n", "- Se isso não funcionar, já que temos dados numéricos\n", "\n", " - Podemos tentar um ✨ KNeighbors Classifier\n", "\n", " - Se isso não funcionar, tente ✨ SVC e ✨ Ensemble Classifiers\n", "\n", "Este é um caminho muito útil para seguir. Agora, vamos direto ao ponto usando o framework de modelagem [tidymodels](https://www.tidymodels.org/): uma coleção consistente e flexível de pacotes R desenvolvidos para incentivar boas práticas estatísticas 😊.\n", "\n", "## 2. Divida os dados e lide com o conjunto de dados desequilibrado.\n", "\n", "Nas nossas lições anteriores, aprendemos que havia um conjunto de ingredientes comuns entre nossas culinárias. Além disso, havia uma distribuição bastante desigual no número de culinárias.\n", "\n", "Vamos lidar com isso da seguinte forma:\n", "\n", "- Eliminando os ingredientes mais comuns que criam confusão entre culinárias distintas, usando `dplyr::select()`.\n", "\n", "- Usando uma `recipe` que pré-processa os dados para prepará-los para modelagem, aplicando um algoritmo de `over-sampling`.\n", "\n", "Já vimos isso na lição anterior, então deve ser tranquilo 🥳!\n" ] }, { "cell_type": "code", "metadata": { "id": "6tj_rN00hClA" }, "source": [ "# Load the core Tidyverse and Tidymodels packages\n", "library(tidyverse)\n", "library(tidymodels)\n", "\n", "# Load the original cuisines data\n", "df <- read_csv(file = \"https://raw.githubusercontent.com/microsoft/ML-For-Beginners/main/4-Classification/data/cuisines.csv\")\n", "\n", "# Drop id column, rice, garlic and ginger from our original data set\n", "df_select <- df %>% \n", " select(-c(1, rice, garlic, ginger)) %>%\n", " # Encode cuisine column as categorical\n", " mutate(cuisine = factor(cuisine))\n", "\n", "\n", "# Create data split specification\n", "set.seed(2056)\n", "cuisines_split <- initial_split(data = df_select,\n", " strata = cuisine,\n", " prop = 0.7)\n", "\n", "# Extract the data in each split\n", "cuisines_train <- training(cuisines_split)\n", "cuisines_test <- testing(cuisines_split)\n", "\n", "# Display distribution of cuisines in the training set\n", "cuisines_train %>% \n", " count(cuisine) %>% \n", " arrange(desc(n))" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "zFin5yw3hHb1" }, "source": [ "### Lidando com dados desbalanceados\n", "\n", "Dados desbalanceados frequentemente têm efeitos negativos no desempenho do modelo. Muitos modelos funcionam melhor quando o número de observações é igual e, por isso, tendem a ter dificuldades com dados desbalanceados.\n", "\n", "Existem basicamente duas maneiras de lidar com conjuntos de dados desbalanceados:\n", "\n", "- adicionar observações à classe minoritária: `Over-sampling`, por exemplo, usando um algoritmo SMOTE que gera novos exemplos sintéticos da classe minoritária utilizando os vizinhos mais próximos desses casos.\n", "\n", "- remover observações da classe majoritária: `Under-sampling`\n", "\n", "Na nossa aula anterior, demonstramos como lidar com conjuntos de dados desbalanceados usando um `recipe`. Um recipe pode ser pensado como um plano que descreve quais etapas devem ser aplicadas a um conjunto de dados para prepará-lo para análise. No nosso caso, queremos ter uma distribuição igual no número de nossas culinárias para o nosso `training set`. Vamos direto ao ponto.\n" ] }, { "cell_type": "code", "metadata": { "id": "cRzTnHolhLWd" }, "source": [ "# Load themis package for dealing with imbalanced data\n", "library(themis)\n", "\n", "# Create a recipe for preprocessing training data\n", "cuisines_recipe <- recipe(cuisine ~ ., data = cuisines_train) %>%\n", " step_smote(cuisine) \n", "\n", "# Print recipe\n", "cuisines_recipe" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "KxOQ2ORhhO81" }, "source": [ "Agora estamos prontos para treinar modelos 👩‍💻👨‍💻!\n", "\n", "## 3. Além dos modelos de regressão multinomial\n", "\n", "Na nossa aula anterior, exploramos os modelos de regressão multinomial. Agora, vamos conhecer alguns modelos mais flexíveis para classificação.\n", "\n", "### Máquinas de Vetores de Suporte\n", "\n", "No contexto de classificação, `Máquinas de Vetores de Suporte` é uma técnica de aprendizado de máquina que busca encontrar um *hiperplano* que \"melhor\" separa as classes. Vamos observar um exemplo simples:\n", "\n", "

\n", " \n", "

https://commons.wikimedia.org/w/index.php?curid=22877598
\n" ] }, { "cell_type": "markdown", "metadata": { "id": "C4Wsd0vZhXYu" }, "source": [ "H1~ não separa as classes. H2~ separa, mas apenas com uma margem pequena. H3~ as separa com a margem máxima.\n", "\n", "#### Classificador Linear de Vetores de Suporte\n", "\n", "O agrupamento por Vetores de Suporte (SVC) é um membro da família de técnicas de aprendizado de máquina baseadas em Máquinas de Vetores de Suporte. No SVC, o hiperplano é escolhido para separar corretamente `a maioria` das observações de treinamento, mas `pode classificar erroneamente` algumas observações. Ao permitir que alguns pontos fiquem do lado errado, o SVM se torna mais robusto a outliers, o que resulta em uma melhor generalização para novos dados. O parâmetro que regula essa violação é chamado de `cost`, que tem um valor padrão de 1 (veja `help(\"svm_poly\")`).\n", "\n", "Vamos criar um SVC linear definindo `degree = 1` em um modelo SVM polinomial.\n" ] }, { "cell_type": "code", "metadata": { "id": "vJpp6nuChlBz" }, "source": [ "# Make a linear SVC specification\n", "svc_linear_spec <- svm_poly(degree = 1) %>% \n", " set_engine(\"kernlab\") %>% \n", " set_mode(\"classification\")\n", "\n", "# Bundle specification and recipe into a worklow\n", "svc_linear_wf <- workflow() %>% \n", " add_recipe(cuisines_recipe) %>% \n", " add_model(svc_linear_spec)\n", "\n", "# Print out workflow\n", "svc_linear_wf" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "rDs8cWNkhoqu" }, "source": [ "Agora que capturamos as etapas de pré-processamento e a especificação do modelo em um *workflow*, podemos prosseguir para treinar o SVC linear e avaliar os resultados ao mesmo tempo. Para as métricas de desempenho, vamos criar um conjunto de métricas que avaliará: `accuracy`, `sensitivity`, `Positive Predicted Value` e `F Measure`.\n", "\n", "> `augment()` adicionará coluna(s) de previsões aos dados fornecidos.\n" ] }, { "cell_type": "code", "metadata": { "id": "81wiqcwuhrnq" }, "source": [ "# Train a linear SVC model\n", "svc_linear_fit <- svc_linear_wf %>% \n", " fit(data = cuisines_train)\n", "\n", "# Create a metric set\n", "eval_metrics <- metric_set(ppv, sens, accuracy, f_meas)\n", "\n", "\n", "# Make predictions and Evaluate model performance\n", "svc_linear_fit %>% \n", " augment(new_data = cuisines_test) %>% \n", " eval_metrics(truth = cuisine, estimate = .pred_class)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "0UFQvHf-huo3" }, "source": [ "#### Máquina de Vetores de Suporte\n", "\n", "A máquina de vetores de suporte (SVM) é uma extensão do classificador de vetores de suporte para acomodar um limite não linear entre as classes. Essencialmente, as SVMs utilizam o *truque do kernel* para ampliar o espaço de características e se adaptar a relações não lineares entre as classes. Uma função kernel popular e extremamente flexível usada pelas SVMs é a *função de base radial.* Vamos ver como ela se comporta com nossos dados.\n" ] }, { "cell_type": "code", "metadata": { "id": "-KX4S8mzhzmp" }, "source": [ "set.seed(2056)\n", "\n", "# Make an RBF SVM specification\n", "svm_rbf_spec <- svm_rbf() %>% \n", " set_engine(\"kernlab\") %>% \n", " set_mode(\"classification\")\n", "\n", "# Bundle specification and recipe into a worklow\n", "svm_rbf_wf <- workflow() %>% \n", " add_recipe(cuisines_recipe) %>% \n", " add_model(svm_rbf_spec)\n", "\n", "\n", "# Train an RBF model\n", "svm_rbf_fit <- svm_rbf_wf %>% \n", " fit(data = cuisines_train)\n", "\n", "\n", "# Make predictions and Evaluate model performance\n", "svm_rbf_fit %>% \n", " augment(new_data = cuisines_test) %>% \n", " eval_metrics(truth = cuisine, estimate = .pred_class)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "QBFSa7WSh4HQ" }, "source": [ "Muito melhor 🤩!\n", "\n", "> ✅ Por favor, veja:\n", ">\n", "> - [*Support Vector Machines*](https://bradleyboehmke.github.io/HOML/svm.html), Hands-on Machine Learning with R\n", ">\n", "> - [*Support Vector Machines*](https://www.statlearning.com/), An Introduction to Statistical Learning with Applications in R\n", ">\n", "> para leitura adicional.\n", "\n", "### Classificadores de Vizinhos Mais Próximos\n", "\n", "O algoritmo *K*-nearest neighbor (KNN) prevê cada observação com base em sua *semelhança* com outras observações.\n", "\n", "Vamos ajustar um ao nosso conjunto de dados.\n" ] }, { "cell_type": "code", "metadata": { "id": "k4BxxBcdh9Ka" }, "source": [ "# Make a KNN specification\n", "knn_spec <- nearest_neighbor() %>% \n", " set_engine(\"kknn\") %>% \n", " set_mode(\"classification\")\n", "\n", "# Bundle recipe and model specification into a workflow\n", "knn_wf <- workflow() %>% \n", " add_recipe(cuisines_recipe) %>% \n", " add_model(knn_spec)\n", "\n", "# Train a boosted tree model\n", "knn_wf_fit <- knn_wf %>% \n", " fit(data = cuisines_train)\n", "\n", "\n", "# Make predictions and Evaluate model performance\n", "knn_wf_fit %>% \n", " augment(new_data = cuisines_test) %>% \n", " eval_metrics(truth = cuisine, estimate = .pred_class)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "HaegQseriAcj" }, "source": [ "Parece que este modelo não está apresentando um bom desempenho. Provavelmente, alterar os argumentos do modelo (veja `help(\"nearest_neighbor\")`) pode melhorar o desempenho do modelo. Certifique-se de testar isso.\n", "\n", "> ✅ Por favor, veja:\n", ">\n", "> - [Hands-on Machine Learning with R](https://bradleyboehmke.github.io/HOML/)\n", ">\n", "> - [An Introduction to Statistical Learning with Applications in R](https://www.statlearning.com/)\n", ">\n", "> para aprender mais sobre classificadores *K*-Nearest Neighbors.\n", "\n", "### Classificadores de conjunto\n", "\n", "Os algoritmos de conjunto funcionam combinando múltiplos estimadores base para produzir um modelo ideal, seja por:\n", "\n", "`bagging`: aplicando uma *função de média* a uma coleção de modelos base\n", "\n", "`boosting`: construindo uma sequência de modelos que se baseiam uns nos outros para melhorar o desempenho preditivo.\n", "\n", "Vamos começar experimentando um modelo de Random Forest, que constrói uma grande coleção de árvores de decisão e, em seguida, aplica uma função de média para obter um modelo geral melhor.\n" ] }, { "cell_type": "code", "metadata": { "id": "49DPoVs6iK1M" }, "source": [ "# Make a random forest specification\n", "rf_spec <- rand_forest() %>% \n", " set_engine(\"ranger\") %>% \n", " set_mode(\"classification\")\n", "\n", "# Bundle recipe and model specification into a workflow\n", "rf_wf <- workflow() %>% \n", " add_recipe(cuisines_recipe) %>% \n", " add_model(rf_spec)\n", "\n", "# Train a random forest model\n", "rf_wf_fit <- rf_wf %>% \n", " fit(data = cuisines_train)\n", "\n", "\n", "# Make predictions and Evaluate model performance\n", "rf_wf_fit %>% \n", " augment(new_data = cuisines_test) %>% \n", " eval_metrics(truth = cuisine, estimate = .pred_class)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "RGVYwC_aiUWc" }, "source": [ "Bom trabalho 👏!\n", "\n", "Vamos também experimentar com um modelo de Árvore Reforçada.\n", "\n", "A Árvore Reforçada define um método de conjunto que cria uma série de árvores de decisão sequenciais, onde cada árvore depende dos resultados das árvores anteriores, na tentativa de reduzir o erro de forma incremental. Ela foca nos pesos dos itens classificados incorretamente e ajusta o ajuste para o próximo classificador corrigir.\n", "\n", "Existem diferentes maneiras de ajustar este modelo (veja `help(\"boost_tree\")`). Neste exemplo, ajustaremos Árvores Reforçadas usando o mecanismo `xgboost`.\n" ] }, { "cell_type": "code", "metadata": { "id": "Py1YWo-micWs" }, "source": [ "# Make a boosted tree specification\n", "boost_spec <- boost_tree(trees = 200) %>% \n", " set_engine(\"xgboost\") %>% \n", " set_mode(\"classification\")\n", "\n", "# Bundle recipe and model specification into a workflow\n", "boost_wf <- workflow() %>% \n", " add_recipe(cuisines_recipe) %>% \n", " add_model(boost_spec)\n", "\n", "# Train a boosted tree model\n", "boost_wf_fit <- boost_wf %>% \n", " fit(data = cuisines_train)\n", "\n", "\n", "# Make predictions and Evaluate model performance\n", "boost_wf_fit %>% \n", " augment(new_data = cuisines_test) %>% \n", " eval_metrics(truth = cuisine, estimate = .pred_class)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "zNQnbuejigZM" }, "source": [ "> ✅ Por favor, veja:\n", ">\n", "> - [Machine Learning for Social Scientists](https://cimentadaj.github.io/ml_socsci/tree-based-methods.html#random-forests)\n", ">\n", "> - [Hands-on Machine Learning with R](https://bradleyboehmke.github.io/HOML/)\n", ">\n", "> - [An Introduction to Statistical Learning with Applications in R](https://www.statlearning.com/)\n", ">\n", "> - - Explora o modelo AdaBoost, que é uma boa alternativa ao xgboost.\n", ">\n", "> para aprender mais sobre classificadores Ensemble.\n", "\n", "## 4. Extra - comparando múltiplos modelos\n", "\n", "Nós ajustamos um bom número de modelos neste laboratório 🙌. Pode se tornar cansativo ou trabalhoso criar muitos fluxos de trabalho a partir de diferentes conjuntos de pré-processadores e/ou especificações de modelos e, em seguida, calcular as métricas de desempenho uma por uma.\n", "\n", "Vamos ver se conseguimos resolver isso criando uma função que ajusta uma lista de fluxos de trabalho no conjunto de treinamento e, em seguida, retorna as métricas de desempenho com base no conjunto de teste. Vamos usar `map()` e `map_dfr()` do pacote [purrr](https://purrr.tidyverse.org/) para aplicar funções a cada elemento de uma lista.\n", "\n", "> As funções [`map()`](https://purrr.tidyverse.org/reference/map.html) permitem substituir muitos loops `for` por um código que é mais conciso e mais fácil de ler. O melhor lugar para aprender sobre as funções [`map()`](https://purrr.tidyverse.org/reference/map.html) é o [capítulo de iteração](http://r4ds.had.co.nz/iteration.html) em R for Data Science.\n" ] }, { "cell_type": "code", "metadata": { "id": "Qzb7LyZnimd2" }, "source": [ "set.seed(2056)\n", "\n", "# Create a metric set\n", "eval_metrics <- metric_set(ppv, sens, accuracy, f_meas)\n", "\n", "# Define a function that returns performance metrics\n", "compare_models <- function(workflow_list, train_set, test_set){\n", " \n", " suppressWarnings(\n", " # Fit each model to the train_set\n", " map(workflow_list, fit, data = train_set) %>% \n", " # Make predictions on the test set\n", " map_dfr(augment, new_data = test_set, .id = \"model\") %>%\n", " # Select desired columns\n", " select(model, cuisine, .pred_class) %>% \n", " # Evaluate model performance\n", " group_by(model) %>% \n", " eval_metrics(truth = cuisine, estimate = .pred_class) %>% \n", " ungroup()\n", " )\n", " \n", "} # End of function" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "Fwa712sNisDA" }, "source": [] }, { "cell_type": "code", "metadata": { "id": "3i4VJOi2iu-a" }, "source": [ "# Make a list of workflows\n", "workflow_list <- list(\n", " \"svc\" = svc_linear_wf,\n", " \"svm\" = svm_rbf_wf,\n", " \"knn\" = knn_wf,\n", " \"random_forest\" = rf_wf,\n", " \"xgboost\" = boost_wf)\n", "\n", "# Call the function\n", "set.seed(2056)\n", "perf_metrics <- compare_models(workflow_list = workflow_list, train_set = cuisines_train, test_set = cuisines_test)\n", "\n", "# Print out performance metrics\n", "perf_metrics %>% \n", " group_by(.metric) %>% \n", " arrange(desc(.estimate)) %>% \n", " slice_head(n=7)\n", "\n", "# Compare accuracy\n", "perf_metrics %>% \n", " filter(.metric == \"accuracy\") %>% \n", " arrange(desc(.estimate))\n" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "KuWK_lEli4nW" }, "source": [ "O pacote [**workflowset**](https://workflowsets.tidymodels.org/) permite que os usuários criem e ajustem facilmente um grande número de modelos, mas é principalmente projetado para funcionar com técnicas de reamostragem, como `cross-validation` (validação cruzada), uma abordagem que ainda vamos abordar.\n", "\n", "## **🚀Desafio**\n", "\n", "Cada uma dessas técnicas possui um grande número de parâmetros que você pode ajustar, como, por exemplo, `cost` em SVMs, `neighbors` em KNN, `mtry` (Preditores Selecionados Aleatoriamente) em Random Forest.\n", "\n", "Pesquise os parâmetros padrão de cada uma e reflita sobre o que ajustar esses parâmetros significaria para a qualidade do modelo.\n", "\n", "Para saber mais sobre um modelo específico e seus parâmetros, use: `help(\"model\")`, por exemplo, `help(\"rand_forest\")`.\n", "\n", "> Na prática, geralmente *estimamos* os *melhores valores* para esses parâmetros treinando muitos modelos em um `conjunto de dados simulado` e medindo o desempenho de todos esses modelos. Esse processo é chamado de **tuning** (ajuste fino).\n", "\n", "### [**Quiz pós-aula**](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/24/)\n", "\n", "### **Revisão e Autoestudo**\n", "\n", "Há muitos termos técnicos nessas lições, então reserve um momento para revisar [esta lista](https://docs.microsoft.com/dotnet/machine-learning/resources/glossary?WT.mc_id=academic-77952-leestott) de terminologia útil!\n", "\n", "#### AGRADECIMENTOS A:\n", "\n", "[`Allison Horst`](https://twitter.com/allison_horst/) por criar as ilustrações incríveis que tornam o R mais acolhedor e envolvente. Encontre mais ilustrações na sua [galeria](https://www.google.com/url?q=https://github.com/allisonhorst/stats-illustrations&sa=D&source=editors&ust=1626380772530000&usg=AOvVaw3zcfyCizFQZpkSLzxiiQEM).\n", "\n", "[Cassie Breviu](https://www.twitter.com/cassieview) e [Jen Looper](https://www.twitter.com/jenlooper) por criarem a versão original deste módulo em Python ♥️\n", "\n", "Bons estudos,\n", "\n", "[Eric](https://twitter.com/ericntay), Embaixador Estudante Gold da Microsoft Learn.\n", "\n", "

\n", " \n", "

Arte por @allison_horst
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n---\n\n**Aviso Legal**: \nEste documento foi traduzido utilizando o serviço de tradução por IA [Co-op Translator](https://github.com/Azure/co-op-translator). Embora nos esforcemos para garantir a precisão, esteja ciente de que traduções automatizadas podem conter erros ou imprecisões. O documento original em seu idioma nativo deve ser considerado a fonte autoritativa. Para informações críticas, recomenda-se a tradução profissional realizada por humanos. Não nos responsabilizamos por quaisquer mal-entendidos ou interpretações equivocadas decorrentes do uso desta tradução.\n" ] } ] }