Gotta rank’em all:
What is the best Pokémon?

Pokémon Go has made the whole world gone wild on the hunt for those cute little creatures. After catching hundreds of Weedles, Rattatas and Pidgeys, I got a bit tired and thought it is time to do some Pokémon science.

Naturally, the whole Pokémon hype has already led to several interesting analyses with available data mainly from the awesome PokeAPI. For instance, this blog post about a cluster analysis of the original 151 Pokémon or this extended analysis of all 721 available Pokémon.

Since clustering is boring, I will do something more exciting and try to rank Pokemon according to their strength with a little bit of help from my own research.

Scrape’em all

The data for the analysis comes from the PokeAPI mentioned above. Getting the data is as easy as downloading the relevant csv files from here. The data is very rich, yet we are going to stick with the six main stats (attack, defense, speed, HP, special attack and special defense) and the type. Also, we do not consider all 721 Pokémon but only the 151 original ones. Let’s be honest, those are the only real Pokémon. I mean, Chandelure…seriously?

The following R code creates a matrix that we will use in the analysis.

#read in relevant data
poke=read.table("pokemon.csv",sep=",",header=T)
poke_stats=read.table("pokemon_stats.csv",sep=",",header=T)
stats=read.table("stats.csv",sep=",",header=T)

#create a matrix containing the stats
poke_stat_mat=matrix(poke_stats$base_stat,811,6,byrow=T)[1:151,]
rownames(poke_stat_mat)=poke$identifier[1:151]
colnames(poke_stat_mat)=stats$identifier[1:6]

Rank’em all

So which is the best Pokémon? A simple question that can lead to heated discussions among hardcore fans without any consent. There is team Charizard (me), Blastoise, Venusaur and of course Mewtwo, the Bayern Munich of Pokémon.

A straightforward approach we could employ is to simply sum up the 6 stat values for each Pokémon and rank order them according to the sum.

rownames(poke_stat_mat)[order(rowSums(poke_stat_mat))]

This gives as the following top ranked

rank name score
1 mewtwo 680
2 dragonite 600
2 mew 600
4 articuno 580
4 zapdos 580
4 moltres 580

and bottom ranked Pokémon.

rank name score
146 zubat 245
147 metapod 205
147 kakuna 205
149 magikarp 200
150 caterpie 195
150 weedle 195

I guess these results are not really surprising at all. But let’s look at it a bit more in detail.

Science! I choose you!

When aggregating values across different dimensions, we usually have to make sure that the variables are all on the same scale. We were lucky here, since the different stats are all on the same scale. But imagine we would add another variable, say height, would it still make sense to sum the values up?

Even if they are on the same scale we can run into problems. Take the following two Pokemon.

name hp attack defense special attack special defense speed
chansey 250 5 5 35 105 50
mewtwo 106 110 90 154 90 130

Chansey (63.) is ranked much lower than Mewtwo (1.). However, it outperforms Mewtwo in HP by 144 points! Also its special defense is much higher. It is Chansey’s ridiculously low attack and defense values that make it rank so low.

The problem that comes with summing up the stat values is that we assume interchangeability of values. A Pokémon can compensate on a low score in one by a high score in another stat. But does this make it superior? What if we do a weighted sum, because we think that HP are more important than attack and defense? Take this weighting scheme for example:

score=HP+ 0.2\times attack + 0.2\times defense + 0.5\times sp.attack +  0.5\times sp.defense+0.1\times speed

Then, Chansey would suddenly be better then Mewtwo. I admit, this scenario is crafted, yet quite real in the “real world”.

Now consider these two Pokémon.

name hp attack defense special attack special defense speed
magikarp 20 10 55 15 20 80
gyarados 95 125 79 60 100 81

Gyarados is better than magikarp in every single stat. In this case, it doesn’t matter how much mathematical skulduggery we apply, gyarados will always be better (ranked higher) than magikarp. We can say that gyarados dominates magikarp.

Not that this was already clear without looking at data…

Domination among Pokémon

We now look for more of these dominance relations. That is pairs of Pokémon, where one is better in all the stat values. This is done with the following code.

A=matrix(0,151,151)
for(i in 1:151){
  for(j in 1:151){
    if(i!=j){
      A[i,j]=all(poke_stat_mat[i,]>=poke_stat_mat[j,])+0
    }
  }
}

This is really just a quick and dirty implementation. Seriously, two for loops with an if statement? That’s like catching a Magikarp with a masterball. It works, but you shouldn’t do it.

Anyways now we have the dominance relations and relations always mean networks (hooray!).  The below figure shows all the dominance relations among the Pokémon in a hierarchical way.

all pokemon

The y coordinate of a Pokémon tells you how many other Pokémon this one dominates. Although I deleted all transitive relations, the plot is still quite messy. But some things are notable. Mew and Mewto clearly dominate the most, followed by the legendary Pokémon, Dragonite and Arcanine. Charizard dominates a few more Pokémon than Blastoise and Venusaur, making Charmander theoretically the best starter. Also look at Chansey. It neither dominates nor is dominated by anyone due to its extraordinary amount of HP.

Type Specific ranking

The plot above is hard to read, so let’s look at the domination hierarchies for Pokémon types.

all_types

The x coordinate are chosen at random, what makes some of the plots look strange. The igraph package unfortunately does not have a nice layout algorithm that would improve overlaps. However, R was the only tool I could manage to create these plots automatically and with the pictures as nodes.

If you want to use images as nodes in igraph for your own project, you can take this example code as a start.

library(png)
library(igraph)

get.img=function(name){
  readPNG(paste0("pokemon_png/",name,".png"))
}
g=graph_from_adjacency_matrix(A,"directed")
V(g)$name=rownames(poke_stat_mat)
V(g)$raster <- sapply(V(g)$name,get.img) 
plot(g, vertex.shape="raster")

Now go a head and take out your old gameboy, start Pokémon Red and put together the most dominating team to beat the Top Four!

Leave a Reply

Your email address will not be published. Required fields are marked *