Pular para o conteúdo principal

Machine Learning em R para iniciantes

Este tutorial rápido apresenta os fundamentos de machine learning em R: você vai ver como usar R para trabalhar com KNN.
Atualizado 17 de set. de 2026  · 15 min lido

Explorar com IA

ChatGPTClaudePerplexity

Apresentando: machine learning em R

Machine learning é uma área da ciência da computação que estuda o design de algoritmos capazes de aprender. Tarefas típicas incluem aprendizado de conceitos, aprendizado de funções ou “modelagem preditiva”, clustering e descoberta de padrões preditivos. Esses aprendizados acontecem a partir de dados disponíveis, observados por experiências ou instruções, por exemplo. A ideia é que, ao incorporar experiência às tarefas, o aprendizado melhore com o tempo. O objetivo final é tornar esse aprendizado automático, de modo que pessoas como nós não precisem mais interferir.

Este tutorial rápido apresenta os fundamentos de machine learning em R: mais especificamente, mostra como usar R para trabalhar com o conhecido algoritmo “KNN”, ou k-vizinhos mais próximos.

Se você quer seguir um curso, confira nossa Introduction to Machine Learning with R ou o curso de Unsupervised Learning in R da DataCamp!

Usando R para k-Nearest Neighbors (KNN)

O algoritmo KNN, ou k-vizinhos mais próximos, é um dos mais simples em machine learning e é um exemplo de aprendizado baseado em instâncias, em que novos dados são classificados com base em exemplos rotulados armazenados.

Mais especificamente, calcula-se a distância entre os dados armazenados e a nova instância por meio de alguma medida de similaridade. Normalmente, essa similaridade é expressa por uma medida de distância, como a distância euclidiana, a similaridade do cosseno ou a distância de Manhattan.

Em outras palavras, para cada novo ponto de dados que você coloca no sistema, calcula-se a semelhança com os dados que já estavam lá.

Depois, você usa esse valor de similaridade para fazer modelagem preditiva. A modelagem preditiva pode ser classificação, atribuindo um rótulo ou classe à nova instância, ou regressão, atribuindo um valor à nova instância. Se você vai classificar ou prever um valor depende de como você constrói seu modelo com KNN.

O algoritmo de k-vizinhos mais próximos acrescenta a isso que, após calcular a distância do novo ponto para todos os pontos armazenados, os valores são ordenados e os k vizinhos mais próximos são determinados. Reúnem-se os rótulos desses vizinhos e usa-se uma votação por maioria ou ponderada para fins de classificação ou regressão.

Em outras palavras, quanto maior a pontuação de um ponto de dados já armazenado, maior a chance de a nova instância receber a mesma classificação do vizinho. No caso de regressão, o valor atribuído ao novo ponto é a média de seus k vizinhos mais próximos.



Passo 1. Obtenha seus dados

Machine learning geralmente começa a partir de dados observados. Você pode usar seu próprio conjunto de dados ou buscar em outras fontes para encontrar um.

Conjuntos de dados nativos do R

Este tutorial usa o conjunto Iris, muito conhecido em machine learning. Esse dataset já vem no R, então você pode inspecioná-lo digitando o seguinte no console:

eyJsYW5ndWFnZSI6InIiLCJzYW1wbGUiOiJpcmlzIn0=

Repositório de Machine Learning da UC Irvine

Se preferir baixar o conjunto de dados em vez de usar o que já vem no R, acesse o UC Irvine Machine Learning Repository e procure pelo conjunto Iris.


Dica: não veja só a pasta de dados do conjunto Iris — dê uma olhada também na página de descrição!

Depois, use o comando abaixo para carregar os dados:

eyJsYW5ndWFnZSI6InIiLCJzYW1wbGUiOiIjIFJlYWQgaW4gYGlyaXNgIGRhdGFcbmlyaXMgPC0gcmVhZC5jc3YodXJsKFwiaHR0cDovL2FyY2hpdmUuaWNzLnVjaS5lZHUvbWwvbWFjaGluZS1sZWFybmluZy1kYXRhYmFzZXMvaXJpcy9pcmlzLmRhdGFcIiksIFxuICAgICAgICAgICAgICAgICBoZWFkZXIgPSBGQUxTRSkgXG5cbiMgUHJpbnQgZmlyc3QgbGluZXNcbmhlYWQoaXJpcylcblxuIyBBZGQgY29sdW1uIG5hbWVzXG5uYW1lcyhpcmlzKSA8LSBjKFwiU2VwYWwuTGVuZ3RoXCIsIFwiU2VwYWwuV2lkdGhcIiwgXCJQZXRhbC5MZW5ndGhcIiwgXCJQZXRhbC5XaWR0aFwiLCBcIlNwZWNpZXNcIilcblxuIyBDaGVjayB0aGUgcmVzdWx0XG5pcmlzIn0=

Esse comando lê o arquivo .csv (Comma Separated Value) do site. O argumento header foi definido como FALSE, indicando que a fonte do Iris não traz os nomes das colunas.

Em vez dos nomes de atributos, você pode ver nomes estranhos como “V1” ou “V2” ao inspecionar o objeto iris com uma função como head(). Eles são atribuídos automaticamente.

Para facilitar o trabalho, vale a pena definir você mesmo os nomes das colunas com a função names(), que obtém ou define os nomes de um objeto. Concatene os nomes dos atributos como gostaria que aparecessem. No trecho acima, você listou Sepal.Length, Sepal.Width, Petal.Length, Petal.Width e Species.

Esses nomes não surgem do nada: confira a descrição do conjunto de dados no link acima; normalmente, todos eles aparecem ali.

Learn Python for Data Science With DataCamp

Passo 2. Conheça seus dados

Agora que você carregou o conjunto Iris no RStudio, é hora de entender de verdade do que seus dados tratam. Só olhar ou ler sobre eles não basta para começar!

Coloque a mão na massa: explore e visualize o dataset e aprofunde o conhecimento de domínio, se achar que os dados estão além do seu alcance.

Provavelmente você já tem o conhecimento de domínio necessário, mas vale lembrar: toda flor tem sépalas e pétalas. As sépalas envolvem as pétalas e costumam ser verdes e parecer folhas, enquanto as pétalas são as “folhas” coloridas. No caso das íris, isso é um pouco diferente, como você vê na imagem a seguir:

machine learning R

Visão inicial do conjunto de dados

Para começar, você pode ter uma ideia dos dados criando alguns gráficos, como histogramas ou boxplots. Neste caso, porém, gráficos de dispersão ajudam muito: é interessante ver quanto uma variável é afetada por outra.

Em outras palavras, você quer ver se há correlação entre duas variáveis.

Você pode fazer scatterplots com o ggvis package, por exemplo.

Observação: primeiro, carregue o pacote ggvis:

# Load in `ggvis`
library(ggvis)

# Iris scatter plot
iris %>% ggvis(~Sepal.Length, ~Sepal.Width, fill = ~Species) %>% layer_points()

correlation iris

Você vê uma alta correlação entre o comprimento e a largura das sépalas das flores Setosa, enquanto a correlação é um pouco menor para Virginica e Versicolor: os pontos ficam mais espalhados e não formam um aglomerado como no caso das Setosa.

O gráfico que cruza comprimento e largura das pétalas conta uma história parecida:

iris %>% ggvis(~Petal.Length, ~Petal.Width, fill = ~Species) %>% layer_points()

scatterplot iris

Este gráfico indica uma correlação positiva entre comprimento e largura das pétalas para todas as espécies do conjunto Iris. Claro que você deve testar essa hipótese mais a fundo para ter certeza:

eyJsYW5ndWFnZSI6InIiLCJzYW1wbGUiOiIjIE92ZXJhbGwgY29ycmVsYXRpb24gYFBldGFsLkxlbmd0aGAgYW5kIGBQZXRhbC5XaWR0aGBcbmNvcihpcmlzJFBldGFsLkxlbmd0aCwgaXJpcyRQZXRhbC5XaWR0aClcblxuIyBSZXR1cm4gdmFsdWVzIG9mIGBpcmlzYCBsZXZlbHMgXG54PWxldmVscyhpcmlzJFNwZWNpZXMpXG5cbiMgUHJpbnQgU2V0b3NhIGNvcnJlbGF0aW9uIG1hdHJpeFxucHJpbnQoeFsxXSlcbmNvcihpcmlzW2lyaXMkU3BlY2llcz09eFsxXSwxOjRdKVxuXG4jIFByaW50IFZlcnNpY29sb3IgY29ycmVsYXRpb24gbWF0cml4XG5wcmludCh4WzJdKVxuY29yKGlyaXNbaXJpcyRTcGVjaWVzPT14WzJdLDE6NF0pXG5cbiMgUHJpbnQgVmlyZ2luaWNhIGNvcnJlbGF0aW9uIG1hdHJpeFxucHJpbnQoeFszXSlcbmNvcihpcmlzW2lyaXMkU3BlY2llcz09eFszXSwxOjRdKSJ9

Quando você combina as três espécies, a correlação fica um pouco mais forte do que ao olhar separadamente: a correlação geral é 0,96, enquanto para Versicolor é 0,79. Já Setosa e Virginica têm correlações entre comprimento e largura das pétalas de 0,31 e 0,32 (arredondando).

Dica: ficou curioso sobre ggvis, gráficos ou histogramas? Veja nosso tutorial de histograma e/ou o curso de ggvis.

Após uma visão geral visual dos dados, você também pode ver o dataset digitando

eyJsYW5ndWFnZSI6InIiLCJzYW1wbGUiOiIjIFJldHVybiBhbGwgYGlyaXNgIGRhdGFcbmlyaXNcblxuIyBSZXR1cm4gZmlyc3QgNSBsaW5lcyBvZiBgaXJpc2BcbmhlYWQoaXJpcylcblxuIyBSZXR1cm4gc3RydWN0dXJlIG9mIGBpcmlzYFxuc3RyKGlyaXMpIn0=

Mas, como verá pelo resultado, esse não é o melhor jeito de inspecionar os dados a fundo: o dataset ocupa muito espaço no console e dificulta a análise. É melhor usar head(iris) ou str(iris).

Note que o último comando ajuda a distinguir claramente o tipo num e os três níveis do atributo Species, que é um fator. Isso é útil, já que muitos classificadores de machine learning em R exigem que a variável alvo seja um fator.

Lembre-se: variáveis fator representam variáveis categóricas no R, com número limitado de valores possíveis.

Um olhar rápido para o atributo Species mostra que a divisão das espécies é 50-50-50. Já para ver a divisão percentual, você pode solicitar uma tabela de proporções:

eyJsYW5ndWFnZSI6InIiLCJzYW1wbGUiOiIjIERpdmlzaW9uIG9mIGBTcGVjaWVzYFxudGFibGUoaXJpcyRTcGVjaWVzKSBcblxuIyBQZXJjZW50dWFsIGRpdmlzaW9uIG9mIGBTcGVjaWVzYFxucm91bmQocHJvcC50YWJsZSh0YWJsZShpcmlzJFNwZWNpZXMpKSAqIDEwMCwgZGlnaXRzID0gMSkifQ==

Observação: o round arredonda os valores do primeiro argumento, prop.table(table(iris$Species))*100, para o número de casas definido em digits, aqui 1. Ajuste como preferir.

Entendimento aprofundado dos dados

Não fique só na visão de alto nível! O R permite ir mais fundo com a função summary(). Ela retorna mínimo, 1º quartil, mediana, média, 3º quartil e máximo para os tipos numéricos do Iris. Para a variável de classe, retorna a contagem dos fatores:

eyJsYW5ndWFnZSI6InIiLCJzYW1wbGUiOiIjIFN1bW1hcnkgb3ZlcnZpZXcgb2YgYGlyaXNgXG5zdW1tYXJ5KC4uLi4pIFxuXG4jIFJlZmluZWQgc3VtbWFyeSBvdmVydmlld1xuc3VtbWFyeSguLi4uW2MoXCJQZXRhbC5XaWR0aFwiLCBcIlNlcGFsLldpZHRoXCIpXSkiLCJzb2x1dGlvbiI6IiMgU3VtbWFyeSBvdmVydmlldyBvZiBgaXJpc2BcbnN1bW1hcnkoaXJpcykgXG5cbiMgUmVmaW5lZCBzdW1tYXJ5IG92ZXJ2aWV3XG5zdW1tYXJ5KGlyaXNbYyhcIlBldGFsLldpZHRoXCIsIFwiU2VwYWwuV2lkdGhcIildKSIsInNjdCI6InRlc3RfZnVuY3Rpb24oXCJzdW1tYXJ5XCIsYXJncz1cIm9iamVjdFwiLCBpbmRleD0xKVxudGVzdF9mdW5jdGlvbihcInN1bW1hcnlcIiwgYXJncz1cIm9iamVjdFwiLCBpbmRleD0yKVxuc3VjY2Vzc19tc2coXCJHcmVhdCBqb2IhXCIpIn0=

Como você vê, a função c() foi adicionada ao comando original: as colunas petal width e sepal width foram concatenadas e foi pedido um resumo só dessas duas colunas do Iris.

Passo 3. E agora?

Depois de entender bem seus dados, decida quais casos de uso fazem sentido para o seu conjunto. Em outras palavras, pense no que ele pode te ensinar ou no que você pode aprender com ele. A partir daí, defina que tipo de algoritmo aplicar para chegar aos resultados desejados.

Dica: quanto mais familiar você estiver com seus dados, mais fácil será definir casos de uso e escolher o algoritmo adequado.

Neste tutorial, o conjunto Iris será usado para classificação, um exemplo de modelagem preditiva. O último atributo, Species, será a variável alvo — aquela que você quer prever.

Observação: você também pode escolher uma das variáveis numéricas como alvo se quiser usar KNN para regressão.

Passo 4. Prepare seu ambiente

Muitos algoritmos usados em machine learning não vêm por padrão no R. É bem provável que você precise baixar os pacotes que quer usar ao começar.


Dica: já sabe qual algoritmo usar, mas não qual pacote precisa? Encontre um panorama bem completo dos pacotes de R usados em ML neste link.

Para ilustrar o KNN, este tutorial usa o pacote class:

eyJsYW5ndWFnZSI6InIiLCJzYW1wbGUiOiJsaWJyYXJ5KC4uLi4uKSIsInNvbHV0aW9uIjoibGlicmFyeShjbGFzcykiLCJzY3QiOiJ0ZXN0X2Z1bmN0aW9uKFwibGlicmFyeVwiLCBhcmdzPVwicGFja2FnZVwiKVxuc3VjY2Vzc19tc2coXCJBd2Vzb21lIGpvYiFcIikifQ==

Se você ainda não tem esse pacote, instale rapidamente com a linha abaixo:

install.packages("<package name>")

Lembrete nerd: se não tem certeza se o pacote está instalado, use o comando abaixo para conferir!

any(grepl("<name of your package>", installed.packages()))

Passo 5. Prepare seus dados

Depois de explorar os dados e preparar o ambiente, foque na tarefa: construir um modelo de machine learning. Antes, porém, é importante preparar os dados. A seguir, duas formas de fazer isso: normalizando os dados (se necessário) e dividindo em conjuntos de treino e teste.

Normalização

Na preparação, pode ser preciso normalizar os dados para que fiquem consistentes. Para este tutorial introdutório, lembre-se: a normalização facilita o aprendizado do KNN. Existem dois tipos:

  • normalização por exemplo, que ajusta cada exemplo individualmente; e
  • normalização por atributo, que ajusta cada atributo da mesma forma em todos os exemplos.

Quando normalizar o dataset?

Resumindo: quando você suspeitar que os dados não são consistentes.

Isso aparece facilmente no resultado do summary(). Observe mínimos e máximos de todos os atributos numéricos. Se um atributo tiver uma faixa de valores muito maior que os demais, normalize — do contrário, a distância será dominada por esse atributo.

Por exemplo, se seu dataset tem apenas X e Y, e X varia de 1 a 1000, enquanto Y vai de 1 a 100, a influência de Y na função de distância tende a ser abafada por X.

Ao normalizar, você ajusta a faixa de todos os atributos, evitando que variáveis com amplitudes maiores dominem as distâncias.

Dica: volte ao resultado de summary(iris) e veja se a normalização é necessária.

O conjunto Iris não precisa de normalização: Sepal.Length varia de 4,3 a 7,9; Sepal.Width, de 2 a 4,4; Petal.Length, de 1 a 6,9; e Petal.Width, de 0,1 a 2,5. Todos os valores ficam entre 0,1 e 7,9 — aceitável.

Mesmo assim, vale estudar a normalização e seu efeito, especialmente se você está começando. Você pode fazer normalização por atributo criando sua própria função normalize().

Depois, use esse argumento em outro comando: a função lapply() retorna uma lista do mesmo tamanho do dataset de entrada, e você coloca os resultados numa data frame com as.data.frame(). Cada elemento dessa lista é o resultado da aplicação de normalize ao dataset de entrada:

YourNormalizedDataSet <- as.data.frame(lapply(YourDataSet, normalize))

Teste isso no bloco do DataCamp Light abaixo!

eyJsYW5ndWFnZSI6InIiLCJzYW1wbGUiOiIjIEJ1aWxkIHlvdXIgb3duIGBub3JtYWxpemUoKWAgZnVuY3Rpb25cbm5vcm1hbGl6ZSA8LSBmdW5jdGlvbih4KSB7XG5udW0gPC0geCAtIG1pbih4KVxuZGVub20gPC0gbWF4KHgpIC0gbWluKHgpXG5yZXR1cm4gKG51bS9kZW5vbSlcbn1cblxuIyBOb3JtYWxpemUgdGhlIGBpcmlzYCBkYXRhXG5pcmlzX25vcm0gPC0gLi4uLi4uLi4uLi4uLiguLi4uLi4oaXJpc1sxOjRdLCBub3JtYWxpemUpKVxuXG4jIFN1bW1hcml6ZSBgaXJpc19ub3JtYFxuc3VtbWFyeSguLi4uLi4uLi4pIiwic29sdXRpb24iOiIjIEJ1aWxkIHlvdXIgb3duIGBub3JtYWxpemUoKWAgZnVuY3Rpb25cbm5vcm1hbGl6ZSA8LSBmdW5jdGlvbih4KSB7XG5udW0gPC0geCAtIG1pbih4KVxuZGVub20gPC0gbWF4KHgpIC0gbWluKHgpXG5yZXR1cm4gKG51bS9kZW5vbSlcbn1cblxuIyBOb3JtYWxpemUgdGhlIGBpcmlzYCBkYXRhXG5pcmlzX25vcm0gPC0gYXMuZGF0YS5mcmFtZShsYXBwbHkoaXJpc1sxOjRdLCBub3JtYWxpemUpKVxuXG4jIFN1bW1hcml6ZSBgaXJpc19ub3JtYFxuc3VtbWFyeShpcmlzX25vcm0pIiwic2N0IjoidGVzdF9vYmplY3QoXCJub3JtYWxpemVcIilcbnRlc3Rfb2JqZWN0KFwiaXJpc19ub3JtXCIpXG50ZXN0X2Z1bmN0aW9uKFwic3VtbWFyeVwiLCBhcmdzPVwib2JqZWN0XCIpIn0=

No Iris, você aplicaria normalize aos quatro atributos numéricos (Sepal.Length, Sepal.Width, Petal.Length, Petal.Width) e colocaria o resultado em um data frame.

Dica: para ilustrar o efeito da normalização, compare o resultado abaixo com o summary do passo 2.

Conjuntos de treino e teste

Para avaliar o desempenho do modelo depois, divida o dataset em duas partes: treino e teste.

O primeiro treina o sistema; o segundo avalia o que foi aprendido. Na prática, a divisão costuma ser disjunta: a opção mais comum é usar 2/3 dos dados originais para treino e 1/3 para teste.

Um último olhar no dataset mostra que, se você dividisse como está, teria uma classe de treino com Setosa e Versicolor, mas sem Virginica. O modelo então classificaria todas as instâncias desconhecidas como “Setosa” ou “Versicolor”, por não conhecer a terceira espécie.

Resumindo: isso geraria previsões incorretas no conjunto de teste.

Você precisa garantir que as três classes estejam presentes no treino. E mais: a quantidade de instâncias de cada espécie deve ser mais ou menos igual, para não favorecer uma classe nas previsões.

Para montar treino e teste, primeiro defina uma semente. É o número do gerador de aleatoriedade do R. A grande vantagem é poder obter a mesma sequência de números aleatórios sempre que fornecer a mesma semente.

set.seed(1234)

Em seguida, garanta que o Iris esteja embaralhado e que haja quantidade equilibrada de cada espécie nos conjuntos.

Use sample() para tirar uma amostra com tamanho igual ao número de linhas do Iris (150). A amostra é com reposição: escolhe-se de um vetor de 2 elementos e atribui-se 1 ou 2 às 150 linhas. A atribuição segue pesos de probabilidade 0,67 e 0,33.

ind <- sample(2, nrow(iris), replace=TRUE, prob=c(0.67, 0.33))

Observação: o argumento replace está como TRUE: você atribui 1 ou 2 a uma linha e reinicia o vetor de 2 para o estado original. Assim, para as próximas linhas, você pode novamente atribuir 1 ou 2. A probabilidade de escolher 1 ou 2 segue os pesos especificados. E, mesmo que o bloco não mostre, a semente continua definida como 1234.

Lembre-se: você quer 2/3 de treino — por isso, atribui “1” com probabilidade 0,67 e “2” com 0,33 às 150 linhas.

Use então a amostra em ind para definir treino e teste:

eyJsYW5ndWFnZSI6InIiLCJwcmVfZXhlcmNpc2VfY29kZSI6InNldC5zZWVkKDEyMzQpXG5pbmQgPC0gc2FtcGxlKDIsIG5yb3coaXJpcyksIHJlcGxhY2U9VFJVRSwgcHJvYj1jKDAuNjcsIDAuMzMpKSIsInNhbXBsZSI6IiMgQ29tcG9zZSB0cmFpbmluZyBzZXRcbmlyaXMudHJhaW5pbmcgPC0gLi4uLltpbmQ9PTEsIDE6NF1cblxuIyBJbnNwZWN0IHRyYWluaW5nIHNldFxuaGVhZCguLi4uLi4uLi4uLi4uLi4uKVxuXG4jIENvbXBvc2UgdGVzdCBzZXRcbmlyaXMudGVzdCA8LSAuLi4uW2luZD09MiwgMTo0XVxuXG4jIEluc3BlY3QgdGVzdCBzZXRcbmhlYWQoLi4uLi4uLi4uLi4pIiwic29sdXRpb24iOiIjIENvbXBvc2UgdHJhaW5pbmcgc2V0XG5pcmlzLnRyYWluaW5nIDwtIGlyaXNbaW5kPT0xLCAxOjRdXG5cbiMgSW5zcGVjdCB0cmFpbmluZyBzZXRcbmhlYWQoaXJpcy50cmFpbmluZylcblxuIyBDb21wb3NlIHRlc3Qgc2V0XG5pcmlzLnRlc3QgPC0gaXJpc1tpbmQ9PTIsIDE6NF1cblxuIyBJbnNwZWN0IHRlc3Qgc2V0XG5oZWFkKGlyaXMudGVzdCkiLCJzY3QiOiJ0ZXN0X29iamVjdChcImlyaXMudHJhaW5pbmdcIilcbnRlc3RfZnVuY3Rpb24oXCJoZWFkXCIsIGFyZ3M9XCJ4XCIsIGluZGV4PTEpXG50ZXN0X29iamVjdChcImlyaXMudGVzdFwiKVxudGVzdF9mdW5jdGlvbihcImhlYWRcIiwgYXJncz1cInhcIiwgaW5kZXg9MikifQ==

Observação: além das proporções 2/3 e 1/3, você não usa todos os atributos para formar treino e teste. Especificamente, usa apenas Sepal.Length, Sepal.Width, Petal.Length e Petal.Width. Isso porque você quer prever o quinto atributo, Species — sua variável alvo. No entanto, você vai incluí-lo no KNN para que haja previsão para ele.

Portanto, armazene os rótulos de classe em vetores fator e divida-os entre treino e teste:

eyJsYW5ndWFnZSI6InIiLCJwcmVfZXhlcmNpc2VfY29kZSI6InNldC5zZWVkKDEyMzQpXG5pbmQgPC0gc2FtcGxlKDIsIG5yb3coaXJpcyksIHJlcGxhY2U9VFJVRSwgcHJvYj1jKDAuNjcsIDAuMzMpKSIsInNhbXBsZSI6IiMgQ29tcG9zZSBgaXJpc2AgdHJhaW5pbmcgbGFiZWxzXG5pcmlzLnRyYWluTGFiZWxzIDwtIGlyaXNbaW5kPT0xLDVdXG5cbiMgSW5zcGVjdCByZXN1bHRcbnByaW50KGlyaXMudHJhaW5MYWJlbHMpXG5cbiMgQ29tcG9zZSBgaXJpc2AgdGVzdCBsYWJlbHNcbmlyaXMudGVzdExhYmVscyA8LSBpcmlzW2luZD09MiwgNV1cblxuIyBJbnNwZWN0IHJlc3VsdFxucHJpbnQoaXJpcy50ZXN0TGFiZWxzKSIsInNvbHV0aW9uIjoiIyBDb21wb3NlIGBpcmlzYCB0cmFpbmluZyBsYWJlbHNcbmlyaXMudHJhaW5MYWJlbHMgPC0gaXJpc1tpbmQ9PTEsNV1cblxuIyBJbnNwZWN0IHJlc3VsdFxucHJpbnQoaXJpcy50cmFpbkxhYmVscylcblxuIyBDb21wb3NlIGBpcmlzYCB0ZXN0IGxhYmVsc1xuaXJpcy50ZXN0TGFiZWxzIDwtIGlyaXNbWmluZD09MiwgNV1cblxuIyBJbnNwZWN0IHJlc3VsdFxucHJpbnQoaXJpcy50ZXN0TGFiZWxzKSIsInNjdCI6InRlc3Rfb2JqZWN0KFwiaXJpcy50cmFpbkxhYmVsc1wiKVxudGVzdF9mdW5jdGlvbihcInByaW50XCIsIGFyZ3M9XCJ4XCIsIGluZGV4PTEpXG50ZXN0X29iamVjdChcImlyaXMudGVzdExhYmVsc1wiKVxudGVzdF9mdW5jdGlvbihcInByaW50XCIsIGFyZ3M9XCJ4XCIsIGluZGV4PTIpfQ==

Passo 6. O modelo KNN na prática

Construindo seu classificador

Depois de toda a preparação, você garantiu que todos os dados (de treino) conhecidos estão armazenados. Até aqui, nenhum modelo/treinamento foi feito. Agora, você quer encontrar os k vizinhos mais próximos do seu conjunto de treino.

Uma forma simples é usar a função knn(), que usa distância euclidiana para encontrar os k vizinhos mais próximos da nova instância desconhecida. Aqui, o parâmetro k é definido por você.

Como dito, novas instâncias são classificadas por maioria simples ou ponderada. Em classificação, o ponto com maior pontuação “vence” e a instância recebe seu rótulo. Em caso de empate, a classificação é aleatória.

Observação: o k costuma ser ímpar para evitar empates.

Para construir o classificador, use knn() e adicione os argumentos, como no exemplo:

eyJsYW5ndWFnZSI6InIiLCJwcmVfZXhlcmNpc2VfY29kZSI6ImxpYnJhcnkoY2xhc3MpXG5zZXQuc2VlZCgxMjM0KVxuaW5kIDwtIHNhbXBsZSgyLCBucm93KGlyaXMpLCByZXBsYWNlPVRSVUUsIHByb2I9YygwLjY3LCAwLjMzKSlcbmlyaXMudHJhaW5pbmcgPC0gaXJpc1tpbmQ9PTEsIDE6NF1cbmlyaXMudGVzdCA8LSBpcmlzW2luZD09MiwgMTo0XVxuaXJpcy50cmFpbkxhYmVscyA8LSBpcmlzW2luZD09MSw1XSIsInNhbXBsZSI6IiMgQnVpbGQgdGhlIG1vZGVsXG5pcmlzX3ByZWQgPC0gLi4uKHRyYWluID0gaXJpcy50cmFpbmluZywgdGVzdCA9IGlyaXMudGVzdCwgY2wgPSBpcmlzLnRyYWluTGFiZWxzLCBrPTMpXG5cbiMgSW5zcGVjdCBgaXJpc19wcmVkYFxuLi4uLi4uLi4uIiwic29sdXRpb24iOiIjIEJ1aWxkIHRoZSBtb2RlbFxuaXJpc19wcmVkIDwtIGtubih0cmFpbiA9IGlyaXMudHJhaW5pbmcsIHRlc3QgPSBpcmlzLnRlc3QsIGNsID0gaXJpcy50cmFpbkxhYmVscywgaz0zKVxuXG4jIEluc3BlY3QgYGlyaXNfcHJlZGBcbmlyaXNfcHJlZCIsInNjdCI6InRlc3RfZnVuY3Rpb24oXCJrbm5cIiwgYXJncz1jKFwidHJhaW5cIiwgXCJ0ZXN0XCIsIFwiY2xcIiwgXCJrXCIpKVxudGVzdF9vdXRwdXRfY29udGFpbnMoXCJpcmlzX3ByZWRcIiwgaW5jb3JyZWN0X21zZz1cIkRpZCB5b3UgaW5zcGVjdCBgaXJpc19wcmVkYD9cIilcbnN1Y2Nlc3NfbXNnKFwiQ29uZ3JhdHMhIFlvdSd2ZSBzdWNjZXNzZnVsbHkgYnVpbHQgeW91ciBmaXJzdCBtYWNoaW5lIGxlYXJuaW5nIG1vZGVsIVwiKSJ9

Você armazena em iris_pred a chamada a knn() com os argumentos: conjunto de treino, conjunto de teste, rótulos de treino e o número de vizinhos desejado. O resultado é um vetor fator com as classes previstas para cada linha do teste.

Observação: não inclua os rótulos do teste — eles servem para verificar se o modelo está prevendo corretamente!

Ao inspecionar iris_pred, você verá o vetor fator com as classes previstas para cada linha do conjunto de teste.

Passo 7. Avaliação do modelo

Uma etapa essencial em machine learning é avaliar o desempenho do modelo, isto é, analisar o quão corretas são as previsões.

De forma mais abstrata, compare os resultados de iris_pred com os rótulos de teste definidos anteriormente:

eyJsYW5ndWFnZSI6InIiLCJwcmVfZXhlcmNpc2VfY29kZSI6ImxpYnJhcnkoY2xhc3MpXG5zZXQuc2VlZCgxMjM0KVxuaW5kIDwtIHNhbXBsZSgyLCBucm93KGlyaXMpLCByZXBsYWNlPVRSVUUsIHByb2I9YygwLjY3LCAwLjMzKSlcbmlyaXMudHJhaW5pbmcgPC0gaXJpc1tpbmQ9PTEsIDE6NF1cbmlyaXMudGVzdCA8LSBpcmlzW2luZD09MiwgMTo0XVxuaXJpcy50cmFpbkxhYmVscyA8LSBpcmlzW2luZD09MSw1XVxuaXJpcy50ZXN0TGFiZWxzIDwtIGlyaXNbWmluZD09MiwgNV1cbmlyaXNfcHJlZCA8LSBrbm4odHJhaW4gPSBpcmlzLnRyYWluaW5nLCB0ZXN0ID0gaXJpcy50ZXN0LCBjbCA9IGlyaXMudHJhaW5MYWJlbHMsIGs9MykiLCJzYW1wbGUiOiIjIFB1dCBgaXJpcy50ZXN0TGFiZWxzYCBpbiBhIGRhdGEgZnJhbWVcbmlyaXNUZXN0TGFiZWxzIDwtIGRhdGEuZnJhbWUoLi4uLi4uLi4uLi4uLi4uKVxuXG4jIE1lcmdlIGBpcmlzX3ByZWRgIGFuZCBgaXJpcy50ZXN0TGFiZWxzYCBcbm1lcmdlIDwtIGRhdGEuZnJhbWUoLi4uLi4uLi4uLCAuLi4uLi4uLi4uLi4uLi4pXG5cbiMgU3BlY2lmeSBjb2x1bW4gbmFtZXMgZm9yIGBtZXJnZWBcbm5hbWVzKC4uLi4uKSA8LSBjKFwiUHJlZGljdGVkIFNwZWNpZXNcIiwgXCJPYnNlcnZlZCBTcGVjaWVzXCIpXG5cbiMgSW5zcGVjdCBgbWVyZ2VgIFxubWVyZ2UiLCJzb2x1dGlvbiI6IiMgUHV0IGBpcmlzLnRlc3RMYWJlbHNgIGluIGEgZGF0YSBmcmFtZVxuaXJpc1Rlc3RMYWJlbHMgPC0gZGF0YS5mcmFtZShpcmlzLnRlc3RMYWJlbHMpXG5cbiMgTWVyZ2UgYGlyaXNfcHJlZGAgYW5kIGBpcmlzLnRlc3RMYWJlbHNgIFxubWVyZ2UgPC0gZGF0YS5mcmFtZShpcmlzX3ByZWQsIGlyaXMudGVzdExhYmVscylcblxuIyBTcGVjaWZ5IGNvbHVtbiBuYW1lcyBmb3IgYG1lcmdlYFxubmFtZXMobWVyZ2UpIDwtIGMoXCJQcmVkaWN0ZWQgU3BlY2llc1wiLCBcIk9ic2VydmVkIFNwZWNpZXNcIilcblxuIyBJbnNwZWN0IGBtZXJnZWAgXG5tZXJnZSIsInNjdCI6InRlc3Rfb2JqZWN0KFwiaXJpc1Rlc3RMYWJlbHNcIilcbnRlc3RfZGF0YV9mcmFtZShcIm1lcmdlXCIsIGNvbHVtbnM9YyhcIlByZWRpY3RlZCBTcGVjaWVzXCIsIFwiT2JzZXJ2ZWQgU3BlY2llc1wiKSwgdW5kZWZpbmVkX21zZz1cIkRpZCB5b3UgY3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIGBpcmlzX3ByZWRgIGFuZCBgaXJpcy50ZXN0TGFiZWxzYCBhcyBkYXRhP1wiLCB1bmRlZmluZWRfY29sc19tc2c9XCJEaWQgeW91IGRlZmluZSB0aGUgY29sdW1ucyBgUHJlZGljdGVkIFNwZWNpZXNgIGFuZCBgT2JzZXJ2ZWQgU3BlY2llc2A/XCIsIGluY29ycmVjdF9tc2c9XCJTb21ldGhpbmcgaXNuJ3QgcmlnaHQgd2l0aCB0aGUgYG1lcmdlYCBkYXRhIGZyYW1lLiBBcmUgeW91IHN1cmUgeW91IGRlZmluZWQgdGhlIGNvcnJlY3QgZGF0YSBhbmQgY29sdW1uIG5hbWVzP1wiKVxudGVzdF9vdXRwdXRfY29udGFpbnMoXCJtZXJnZVwiKSJ9

Você vê que o modelo acerta bem, com exceção de uma classificação errada na linha 29, em que “Versicolor” foi previsto enquanto o rótulo real era “Virginica”.

Isso já indica algo sobre o desempenho, mas você pode ir mais fundo. Para isso, importe o pacote gmodels:

install.packages("package name")

Se já o tiver instalado, basta rodar

library(gmodels)

Agora crie uma tabela cruzada (contingência). Esse tipo de tabela ajuda a entender a relação entre duas variáveis. Neste caso, você quer entender como as classes do seu teste, em iris.testLabels, se relacionam com o modelo armazenado em iris_pred:

CrossTable(x = iris.testLabels, y = iris_pred, prop.chisq=FALSE)

Crosstable iris knn

Observação: o último argumento, prop.chisq, indica se a contribuição do qui-quadrado de cada célula é incluída. A estatística do qui-quadrado é a soma das contribuições das células e serve para decidir se a diferença entre observados e esperados é significativa.

Da tabela, você obtém o número de acertos e erros: uma instância do conjunto de teste foi rotulada como Versicolor pelo modelo, embora fosse Virginica. Isso aparece na primeira linha da espécie “Virginica” na coluna iris.testLabels. Nos demais casos, as previsões foram corretas. Conclusão: o desempenho do modelo é bom o suficiente e você não precisa melhorá-lo!

Learn Python for Data Science With DataCamp

Machine learning em R com caret

Nas seções anteriores, você começou com aprendizado supervisionado em R via KNN. Como deve ter notado, machine learning em R pode ficar complexo: diversos algoritmos, sintaxes diferentes, parâmetros etc. Talvez você concorde que lembrar o nome do pacote para cada algoritmo é difícil, e aplicar a sintaxe específica de cada um pode ser demais.

É aí que o pacote caret ajuda: é a sigla de “Classification and Regression Training” e reúne o que você precisa para resolver problemas de aprendizado supervisionado, oferecendo uma interface uniforme para uma porção de algoritmos. Se você conhece machine learning em Python, vai notar semelhanças com o scikit-learn!

A seguir, vamos repetir os passos acima, mas usando o caret para classificar os dados. Note que, se você seguiu os passos anteriores, já fez boa parte do trabalho: entendeu seus dados, explorou, preparou o ambiente etc. Agora é hora de pré-processar com o caret!

Como antes, você pode estudar o efeito da normalização — veremos isso mais adiante.

Você já sabe o próximo passo! Vamos dividir os dados em treino e teste. Aqui, fazemos diferente: a divisão é baseada nos rótulos em iris$Species. Além disso, a proporção agora é 75-25 para treino e teste.

eyJsYW5ndWFnZSI6InIiLCJwcmVfZXhlcmNpc2VfY29kZSI6ImxpYnJhcnkoY2FyZXQpXG5zZXQuc2VlZCgxMjM0KSIsInNhbXBsZSI6IiMgQ3JlYXRlIGluZGV4IHRvIHNwbGl0IGJhc2VkIG9uIGxhYmVscyAgXG5pbmRleCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKGlyaXMkU3BlY2llcywgcD0wLjc1LCBsaXN0PUZBTFNFKVxuXG4jIFN1YnNldCB0cmFpbmluZyBzZXQgd2l0aCBpbmRleFxuaXJpcy50cmFpbmluZyA8LSBpcmlzWy4uLi4uLi4sXVxuXG4jIFN1YnNldCB0ZXN0IHNldCB3aXRoIGluZGV4XG5pcmlzLnRlc3QgPC0gaXJpc1stLi4uLi4uLi4uLF0iLCJzb2x1dGlvbiI6IiMgQ3JlYXRlIGluZGV4IHRvIHNwbGl0IGJhc2VkIG9uIGxhYmVscyAgXG5pbmRleCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKGlyaXMkU3BlY2llcywgcD0wLjc1LCBsaXN0PUZBTFNFKVxuXG4jIFN1YnNldCB0cmFpbmluZyBzZXQgd2l0aCBpbmRleFxuaXJpcy50cmFpbmluZyA8LSBpcmlzW2luZGV4LF1cblxuIyBTdWJzZXQgdGVzdCBzZXQgd2l0aCBpbmRleFxuaXJpcy50ZXN0IDwtIGlyaXNbLWluZGV4LF0iLCJzY3QiOiJ0ZXN0X29iamVjdChcImluZGV4XCIpXG50ZXN0X29iamVjdChcImlyaXMudHJhaW5pbmdcIilcbnRlc3Rfb2JqZWN0KFwiaXJpcy50ZXN0XCIpXG5zdWNjZXNzX21zZyhcIkF3ZXNvbWUhIFdlbGwgZG9uZSFcIikifQ==

Tudo pronto para treinar modelos! Mas, como você lembra, o caret é um projeto enorme com muitos algoritmos. Se tiver dúvida sobre o que está incluído, liste todos com names(getModelInfo()), como no bloco abaixo. Depois, escolha um algoritmo e treine um modelo com train():

eyJsYW5ndWFnZSI6InIiLCJwcmVfZXhlcmNpc2VfY29kZSI6ImxpYnJhcnkoY2FyZXQpXG5zZXQuc2VlZCgxMjM0KVxuaW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihpcmlzJFNwZWNpZXMsIHA9MC43NSwgbGlzdD1GQUxTRSlcbmlyaXMudHJhaW5pbmcgPC0gaXJpc1tpbmRleCxdXG5pcmlzLnRlc3QgPC0gaXJpc1staW5kZXgsXSIsInNhbXBsZSI6IiMgT3ZlcnZpZXcgb2YgYWxnb3Mgc3VwcG9ydGVkIGJ5IGNhcmV0XG5uYW1lcyhnZXRNb2RlbEluZm8oKSlcblxuIyBUcmFpbiBhIG1vZGVsXG5tb2RlbF9rbm4gPC0gdHJhaW4oaXJpcy50cmFpbmluZ1ssIDE6NF0sIGlyaXMudHJhaW5pbmdbLCA1XSwgbWV0aG9kPSdrbm4nKSJ9

Perceba como criar outros modelos fica simples a partir daqui: basta mudar o argumento method, como neste exemplo:

model_cart <- train(iris.training[, 1:4], iris.training[, 5], method='rpart2')

Agora que o modelo foi treinado, é hora de prever os rótulos do conjunto de teste e avaliar o desempenho:

eyJsYW5ndWFnZSI6InIiLCJwcmVfZXhlcmNpc2VfY29kZSI6ImxpYnJhcnkoY2FyZXQpXG5zZXQuc2VlZCgxMjM0KVxuaW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihpcmlzJFNwZWNpZXMsIHA9MC43NSwgbGlzdD1GQUxTRSlcbmlyaXMudHJhaW5pbmcgPC0gaXJpc1tpbmRleCxdXG5pcmlzLnRlc3QgPC0gaXJpc1staW5kZXgsXVxubW9kZWxfa25uIDwtIHRyYWluKGlyaXMudHJhaW5pbmdbLCAxOjRdLCBpcmlzLnRyYWluaW5nWywgNV0sIG1ldGhvZD0na25uJykiLCJzYW1wbGUiOiIjIFByZWRpY3QgdGhlIGxhYmVscyBvZiB0aGUgdGVzdCBzZXRcbnByZWRpY3Rpb25zPC1wcmVkaWN0KG9iamVjdD1tb2RlbF9rbm4saXJpcy50ZXN0WywxOjRdKVxuXG4jIEV2YWx1YXRlIHRoZSBwcmVkaWN0aW9uc1xudGFibGUocHJlZGljdGlvbnMpXG5cbiMgQ29uZnVzaW9uIG1hdHJpeCBcbmNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9ucyxpcmlzLnRlc3RbLDVdKSIsInNvbHV0aW9uIjoiIyBQcmVkaWN0IHRoZSBsYWJlbHMgb2YgdGhlIHRlc3Qgc2V0XG5wcmVkaWN0aW9uczwtcHJlZGljdC50cmFpbihvYmplY3Q9bW9kZWxfa25uLGlyaXMudGVzdFssMTo0XSwgdHlwZT1cInJhd1wiKVxuXG4jIEV2YWx1YXRlIHRoZSBwcmVkaWN0aW9uc1xudGFibGUocHJlZGljdGlvbnMpXG5cbiMgQ29uZnVzaW9uIG1hdHJpeCBcbmNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9ucyxpcmlzLnRlc3RbLDVdKSIsInNjdCI6InRlc3Rfb2JqZWN0KFwicHJlZGljdGlvbnNcIilcbnRlc3RfZnVuY3Rpb24oXCJ0YWJsZVwiKVxudGVzdF9mdW5jdGlvbihcImNvbmZ1c2lvbk1hdHJpeFwiKVxuc3VjY2Vzc19tc2coXCJXZWxsIGRvbmUhXCIpIn0=

Além disso, tente repetir o teste anterior para examinar o efeito do pré-processamento, como scaling e centering, no modelo. Rode o bloco a seguir:

eyJsYW5ndWFnZSI6InIiLCJwcmVfZXhlcmNpc2VfY29kZSI6ImxpYnJhcnkoY2FyZXQpXG5zZXQuc2VlZCgxMjM0KVxuaW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihpcmlzJFNwZWNpZXMsIHA9MC43NSwgbGlzdD1GQUxTRSlcbmlyaXMudHJhaW5pbmcgPC0gaXJpc1tpbmRleCxdXG5pcmlzLnRlc3QgPC0gaXJpc1staW5kZXgsXSIsInNhbXBsZSI6IiMgVHJhaW4gdGhlIG1vZGVsIHdpdGggcHJlcHJvY2Vzc2luZ1xubW9kZWxfa25uIDwtIHRyYWluKGlyaXMudHJhaW5pbmdbLCAxOjRdLCBpcmlzLnRyYWluaW5nWywgNV0sIG1ldGhvZD0na25uJywgcHJlUHJvY2Vzcz1jKFwiY2VudGVyXCIsIFwic2NhbGVcIikpXG5cbiMgUHJlZGljdCB2YWx1ZXNcbnByZWRpY3Rpb25zPC1wcmVkaWN0LnRyYWluKG9iamVjdD1tb2RlbF9rbm4saXJpcy50ZXN0WywxOjRdLCB0eXBlPVwicmF3XCIpXG5cbiMgQ29uZnVzaW9uIG1hdHJpeFxuY29uZnVzaW9uTWF0cml4KHByZWRpY3Rpb25zLGlyaXMudGVzdFssNV0pIiwic29sdXRpb24iOiIjIFRyYWluIHRoZSBtb2RlbCB3aXRoIHByZXByb2Nlc3Npbmdcbm1vZGVsX2tubiA8LSB0cmFpbihpcmlzLnRyYWluaW5nWywgMTo0XSwgaXJpcy50cmFpbmluZ1ssIDVdLCBtZXRob2Q9J2tubicsIHByZVByb2Nlc3M9YyhcImNlbnRlclwiLCBcInNjYWxlXCIpKVxuXG4jIFByZWRpY3QgdmFsdWVzXG5wcmVkaWN0aW9uczwtcHJlZGljdC50cmFpbihvYmplY3Q9bW9kZWxfa25uLGlyaXMudGVzdFssMTo0XSwgdHlwZT1cInJhd1wiKVxuXG4jIENvbmZ1c2lvbiBtYXRyaXhcbmNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9ucyxpcmlzLnRlc3RbLDVdKSIsInNjdCI6InRlc3Rfb2JqZWN0KFwibW9kZWxfa25uXCIsIGV2YWw9RkFMU0UpXG50ZXN0X29iamVjdChcInByZWRpY3Rpb25zXCIpXG50ZXN0X2Z1bmN0aW9uKFwiY29uZnVzaW9uTWF0cml4XCIsIGFyZ3M9YyhcImRhdGFcIiwgXCJyZWZlcmVuY2VcIikpXG5zdWNjZXNzX21zZyhcIkNvbmdyYXR1bGFjacOnZXMgISBZb3UgaGF2ZSBzdWNjZXNzZnVsbHkgY29tcGxldGVkIHRoZSB0dXRvcmlhbCFcIikifQ==

Siga para Big Data

Parabéns! Você chegou ao fim deste tutorial!

O foco aqui foi aplicar o algoritmo básico KNN com a ajuda do R. O conjunto Iris é pequeno e fácil de visualizar; além de ver como executar todos os passos manualmente, você também viu como usar uma interface uniforme, como a do caret, para acelerar seu machine learning.

Mas dá para ir muito além!

Se você já experimentou bastante com os fundamentos apresentados e outros algoritmos, talvez ache interessante se aprofundar em R e análise de dados.

Tópicos
R
Ciência de dados
Aprendizado de máquina

Cursos de R

Curso

Introdução ao R

4 h
3.1M
Domine os conceitos básicos de análise de dados em R, incluindo vetores, listas e quadros de dados, e pratique o R com conjuntos de dados reais.
Ver detalhesRight Arrow
Iniciar Curso
Ver maisRight Arrow
Relacionado

Tutorial

Criação de modelos de redes neurais (NN) em R

Neste tutorial, você aprenderá a criar um modelo de rede neural no R.
Abid Ali Awan's photo

Abid Ali Awan

10 min

Tutorial

Classificação de K-Nearest Neighbors (KNN) com o tutorial do R

Aprenda a usar os pacotes R 'class' e 'caret', ajustar hiperparâmetros e avaliar o desempenho do modelo.
Abid Ali Awan's photo

Abid Ali Awan

11 min

Tutorial

Tutorial do K-Means Clustering no R

Saiba o que é o k-means e descubra por que ele é um dos algoritmos de agrupamento mais usados na ciência de dados
Eugenia Anello's photo

Eugenia Anello

8 min

Tutorial

Tutorial de regressão linear no R

Neste tutorial, você aprenderá os fundamentos de um modelo estatístico muito popular: a regressão linear.

Eladio Montero Porras

15 min

Clustering k-means

Tutorial

Introdução ao k-Means Clustering com o scikit-learn em Python

Neste tutorial, saiba como aplicar o k-Means Clustering com o scikit-learn em Python

Kevin Babitz

8 min

Tutorial

Árvores de decisão em aprendizado de máquina usando o R

Um guia abrangente para criar, visualizar e interpretar modelos de árvore de decisão com o R.
Arunn Thevapalan's photo

Arunn Thevapalan

15 min

Ver MaisVer Mais