Introdução à análise de dados com PySpark utilizando os dados dos campeões de League of Legends

geazi_anc

Geazi Anc

Posted on September 15, 2022

Introdução à análise de dados com PySpark utilizando os dados dos campeões de League of Legends

O League of Legends, também conhecido como lolzinho, para os íntimos, é um jogo ambientado no mundo fantasioso de Runeterra, com batalhas sangrentas e muita magia. Em League of Legends, os jogadores controlam personagens conhecidos como campeões, cada um com suas habilidades e diferentes estilos de jogo.

Neste artigo, iremos analisar algumas estatísticas desses campeões fazendo o uso do PySpark, uma API do framework Apache Spark desenvolvida para a linguagem de programação Python 🐍. Os dados serão extraídos da web API Data Dragon, uma API pública da Riot Games.

Para isso, vamos desenvolver um notebook no Google Colab, um serviço de nuvem gratuito criado pelo Google para incentivar pesquisas na área de machine learning e inteligência artificial.

Caso não saiba como usar o Google Colab, confira este excelente artigo da Alura escrito pelo Thiago Santos que ensina, de forma muito didática, como usar o Colab e criar seus primeiros códigos!

O notebook deste artigo também está disponível em meu GitHub 😉.

Peguem suas espadas, preparem suas magias, e vamos começar ⚔🧙🏼‍♀️!

Instalação

Antes de começarmos, é necessário fazer a instalação de duas bibliotecas: PySpark e Requests.

A biblioteca PySpark, como foi dito, é a API oficial do Python para o Apache Spark. É com ela que vamos realizar nossa análise de dados 🎲.

Já a biblioteca Requests é uma biblioteca que nos permite fazer solicitações HTTP a um determinado website. Mediante a ela que iremos extrair os dados dos campeões através da API pública da Riot Games 🚀.

Crie uma nova célula de código no Colab e execute a seguinte linha:

!pip install pyspark
!pip install requests
Enter fullscreen mode Exit fullscreen mode

Inicialização

Logo após a instalação das bibliotecas, precisamos inicializar o Apache Spark. Para isso, importamos a classe SparkSession dentro do módulo sql da biblioteca pyspark.

Depois da importação, instanciamos a classe SparkSession através de uma série de métodos encadeados, como appName e getOrCreate.

from pyspark.sql import SparkSession

spark = (SparkSession.builder
         .appName("Introdução à análise de dados com PySpark utilizando os dados dos campeões de League of Legends")
         .getOrCreate()
         )
Enter fullscreen mode Exit fullscreen mode

Extração de dados dos campeões

A extração dos dados dos campeões de League of Legends é feita através de uma solicitação HTTP à um endpoint da API Data Dragon, uma API pública da Riot Games que centraliza os dados do jogo, como campeões, itens, magias e ETC.

A resposta é um objeto JSON semelhante a este:

{
    "type": "champion",
    "format": "standAloneComplex",
    "version": "12.17.1",
    "data": {
        "Aatrox": {},
        "Ahri": {...},
        "Akali": {...},
        "Akshan": {...},
        "Alistar": {...},
        ...,
    }
}
Enter fullscreen mode Exit fullscreen mode

Observe que os dados que queremos está dentro da chave data. Vamos pegar esses dados, descartando os demais, e exibir apenas o nome de todos os campeões.

Crie uma nova célula de código e execute o seguinte bloco:

import requests

response=requests.get(
"https://ddragon.leagueoflegends.com/cdn/12.17.1/data/pt_BR/champion.json")

champions=response.json().get("data")
champions.keys()
Enter fullscreen mode Exit fullscreen mode

Resultado:

dict_keys(['Aatrox', 'Ahri', 'Akali', 'Akshan', 'Alistar', ...])

Também podemos ver os dados de um campeão em específico. Nesse caso, vamos ver os dados estatísticos da Akali.

champions.get("akali")

Resultado:

{'version': '12.17.1',
 'id': 'Akali',
 'key': '84',
 'name': 'Akali',
 'title': 'a Assassina Renegada',
 'blurb': 'Abandonando a Ordem Kinkou e seu título de Punho das Sombras, Akali agora ataca sozinha, pronta para ser a arma mortal que seu povo precisa. Embora ela mantenha tudo o que aprendeu com seu mestre Shen, ela se comprometeu a defender Ionia de seus...',
 'info': {'attack': 5, 'defense': 3, 'magic': 8, 'difficulty': 7},
 'image': {'full': 'Akali.png',
  'sprite': 'champion0.png',
  'group': 'champion',
  'x': 96,
  'y': 0,
  'w': 48,
  'h': 48},
 'tags': ['Assassin'],
 'partype': 'Energia',
 'stats': {'hp': 570,
  'hpperlevel': 119,
  'mp': 200,
  'mpperlevel': 0,
  'movespeed': 345,
  'armor': 23,
  'armorperlevel': 4.7,
  'spellblock': 37,
  'spellblockperlevel': 2.05,
  'attackrange': 125,
  'hpregen': 9,
  'hpregenperlevel': 0.9,
  'mpregen': 50,
  'mpregenperlevel': 0,
  'crit': 0,
  'critperlevel': 0,
  'attackdamage': 62,
  'attackdamageperlevel': 3.3,
  'attackspeedperlevel': 3.2,
  'attackspeed': 0.625}}
Enter fullscreen mode Exit fullscreen mode

Limpesa dos dados

Antes de começarmos de fato com a análise, é necessário fazermos uma limpesa prévia nos dados. Vamos pegar apenas os que nos interessa, e remover os dicionários dentro de dicionários, deixando um único dicionário para cada campeão com os dados necessários.

champions=[{'name': value['name'], 'title': value['title'], **value['info'], **value['stats']} for key, value in champions.items()]
champions[2]
Enter fullscreen mode Exit fullscreen mode

Resultado:

{'name': 'Akali',
 'title': 'a Assassina Renegada',
 'attack': 5,
 'defense': 3,
 'magic': 8,
 'difficulty': 7,
 'hp': 570,
 'hpperlevel': 119,
 'mp': 200,
 'mpperlevel': 0,
 'movespeed': 345,
 'armor': 23,
 'armorperlevel': 4.7,
 'spellblock': 37,
 'spellblockperlevel': 2.05,
 'attackrange': 125,
 'hpregen': 9,
 'hpregenperlevel': 0.9,
 'mpregen': 50,
 'mpregenperlevel': 0,
 'crit': 0,
 'critperlevel': 0,
 'attackdamage': 62,
 'attackdamageperlevel': 3.3,
 'attackspeedperlevel': 3.2,
 'attackspeed': 0.625}
Enter fullscreen mode Exit fullscreen mode

Criando o DataFrame

Agora sim! Os dados dos campeões estão limpos, então já podemos criar nosso DataFrame com o Spark.

Infelizmente, o Spark é um tanto... seletivo com o tipo de objeto que passamos a ele para criar um DataFrame. Logo, nosso objeto atual champions, que é composto de uma lista de dicionários, não é aceito pelo Spark.

Mas existe uma solução👏🏼. A biblioteca Pandas é muito mais flexível no que se refere a criação de um novo DataFrame. Portanto, é possível criar um DataFrame do Pandas com nosso objeto champions atual, e em seguida criar um DataFrame do Spark com base no DataFrame criado pelo Pandas.

import pandas as pd

df = spark.createDataFrame(pd.DataFrame(champions))

df.select("name", "title").show(5, False)
Enter fullscreen mode Exit fullscreen mode

Resultado:

+-------+-----------------------+
|name   |title                  |
+-------+-----------------------+
|Aatrox |a Espada Darkin        |
|Ahri   |a Raposa de Nove Caudas|
|Akali  |a Assassina Renegada   |
|Akshan |o Sentinela Rebelde    |
|Alistar|o Minotauro            |
+-------+-----------------------+
only showing top 5 rows
Enter fullscreen mode Exit fullscreen mode

Concatenação de colunas

Não sei vocês, mas acho um tanto incômodo ficar selecionando o nome e os títulos dos campeões cada vez que formos visualisar seus dados. Então, vamos concatenar as colunas name e title em uma nova coluna, chamada full_name.

Para isso, vamos primeiramente utilizar o método withColumn. Em resumo, esse método nos permite criar uma nova coluna em nosso DataFrame.

O primeiro parâmetro do método é o nome da nossa coluna. Já o segundo parâmetro são os dados que queremos popular nossa nova coluna. Nesse caso, a concatenação da coluna name com a coluna title.

Para concatenar as colunas de strings, vamos utilizar a função concat.
Esta função recebe como parâmetros o nome das colunas que queremos concatenar. Contudo, não podemos passar apenas o nome dessas colunas. Caso contrário o nome e os títulos ficariam colados um ao outro. Então também usamos a função lit, que cria uma nova coluna literal com o valor que passamos a ela, isto é: ", ".

from pyspark.sql import functions as F

df = df.withColumn("full_name", F.concat(df.name, F.lit(", "), df.title))
df.select("full_name").show(5, False)
Enter fullscreen mode Exit fullscreen mode

Resultado:

+-----------------------------+
|full_name                    |
+-----------------------------+
|Aatrox, a Espada Darkin      |
|Ahri, a Raposa de Nove Caudas|
|Akali, a Assassina Renegada  |
|Akshan, o Sentinela Rebelde  |
|Alistar, o Minotauro         |
+-----------------------------+
only showing top 5 rows
Enter fullscreen mode Exit fullscreen mode

Quem são os campeões mais poderosos de League of Legends?

Curioso para saber quem são os campeões mais poderosos de League of Legends? Pois é, eu também estou. Vamos descobrir 👀!

Para esta análise, considere que o que determina o nível de poder de um campeão são seus valores de ataque, armadura, vida e mana.

Então, para vermos quem são os campeões mais poderosos, basta ordenarmos nosso DataFframe com base nessas colunas, de modo decrescente.

Uma pequena observação: atualmente todos os campeões estão no nível um.

base_columns = ["attackdamage", "armor", "hp", "mp"]

(df.orderBy(*base_columns, ascending=False)
 .select("full_name", *base_columns)
 .show(5, False)
 )
Enter fullscreen mode Exit fullscreen mode

Resultado:

+---------------------------------+------------+-----+-----+-----+
|full_name                        |attackdamage|armor|hp   |mp   |
+---------------------------------+------------+-----+-----+-----+
|Tryndamere, o Rei Bárbaro        |72.0        |33   |696.0|100.0|
|Cho'Gath, o Terror do Vazio      |69.0        |38   |644.0|270.0|
|Renekton, o Carniceiro das Areias|69.0        |35   |660.0|100.0|
|Ornn, O Fogo sob a Montanha      |69.0        |33   |660.0|340.6|
|Kayn, o Ceifador das Sombras     |68.0        |38   |655.0|410.0|
+---------------------------------+------------+-----+-----+-----+
only showing top 5 rows
Enter fullscreen mode Exit fullscreen mode

Level up!

Como dito, atualmente nossos campeões estão no nível 1. Vamos alterar o nível deles para o nível 10.

Observe que as estatísticas dos campeões devem acompanhar seus crescimentos conforme o passar dos níveis. Nesta análise, vamos alterar apenas os valores de dano, armadura, vida e mana.

Para alterarmos esses valores, vamos fazer o uso do método withColumns.
Este método recebe um objeto do tipo dicionário, onde as chaves são os nomes das colunas, e seus valores são as colunas com os dados alterados.

level = 10

df2 = df.withColumns({
    "attackdamage": df.attackdamage+df.attackdamageperlevel*level,
    "armor": df.armor+df.armorperlevel*level,
    "hp": df.hp+df.hpperlevel*level,
    "mp": df.mp+df.mpperlevel*level
})
Enter fullscreen mode Exit fullscreen mode

Quem são os campeões mais poderosos de League of Legends (de novo)?

Com todos os campeões já no nível 10, vamos ver se o rank de poder da análise anterior se manteve ou se houve mudança.
Lembrando que ainda estamos analisando o nível de poder apenas com base nas colunas dano, armadura, vida e mana.

(df2.orderBy(*base_columns, ascending=False)
 .select("full_name", *base_columns)
 .show(5, False)
 )
Enter fullscreen mode Exit fullscreen mode

Resultado:

+-----------------------------+------------+-----+------+-----+
|full_name                    |attackdamage|armor|hp    |mp   |
+-----------------------------+------------+-----+------+-----+
|Illaoi, a Sacerdotisa Cráquem|118.0       |85.0 |1746.0|800.0|
|Olaf, o Berserker            |115.0       |77.0 |1835.0|816.0|
|Darius, a Mão de Noxus       |114.0       |91.0 |1792.0|838.0|
|Yorick, o Pastor de Almas    |112.0       |91.0 |1790.0|900.0|
|Cho'Gath, o Terror do Vazio  |111.0       |85.0 |1584.0|870.0|
+-----------------------------+------------+-----+------+-----+
only showing top 5 rows
Enter fullscreen mode Exit fullscreen mode

Estatísticas dos níveis de poderes

Para finalizar, vamos ver algumas estatísticas simples de todos os nossos campeões no nível 10.

Vamos determinar a média do dano, o máximo do hp e da mana, e o mínimo da armadura.

Utilizaremos o método agg. Este método recebe como parâmetro um dicionário, onde as chaves são o nome das colunas que queremos analisar e os valores são as funções que queremos aplicar sobre elas.

(df2.agg({
    "attackdamage": "mean",
    "hp": "max",
    "mp": "max",
    "armor": "min"
})
    .show()
)
Enter fullscreen mode Exit fullscreen mode

Resultado:

+-------+----------+-----------------+-------+
|max(mp)|min(armor)|avg(attackdamage)|max(hp)|
+-------+----------+-----------------+-------+
|10000.0|      28.0|91.40481987577641| 1892.0|
+-------+----------+-----------------+-------+
Enter fullscreen mode Exit fullscreen mode

Considerações finais

É isso, meus amigos. Finalizamos nossa análise por aqui 🎆.

Neste artigo demonstrei como aplicar uma análise bem simples sobre os dados dos campeões de League of Legends. Fizemos a extração dos dados por meio da API públilca da Riot Games; fizemos uma limpesa prévia nos dados; criamos uma nova coluna com o resultado da concatenação dos nomes dos campeões e seus títulos; ranqueamos os campeões mais poderosos com base em seus níveis de poder; e, por fim, fizemos uma análise das estatísticas dos campeões tanto no nível 1 quanto no nível 10.

Espero que tenham gostado. Até a próxima 💚!

💖 💪 🙅 🚩
geazi_anc
Geazi Anc

Posted on September 15, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related