A visualization for Pokemon type distribution
The Wordle craze brought about many Wordle variants, and one that I particularly enjoy is Squirdle, in which you are given a limited number of attempts to guess a specific Pokemon. Having a strong familiarity with how the Pokemon types are distributed throughout the generations is extremely useful in playing Squirdle, so I created a visualization to help identify trends worth noting here: michaelcw.com/pokemon_types/. This currently covers all Pokemon from generations 1 to 8. Generation 9 is on the horizon, but we only know a few of the Pokemon at the time of writing.
(One could also use this as an aid while playing, but I prefer to play without any outside help.)
Those familiar with Pokemon, particularly the more recent generations, will know that there are many cases where a Pokemon has alternate forms. I therefore needed to make some decisions about whether an alternate form counts as a separate entry. For this project:
- Mega evolutions are considered separate entries.
- Alolan, Galarian, and Hisuian forms are considered separate entries.
- Some other form variants are also considered separate. If a variant results in a different type, it will definitely be counted separately.
- For example, Rotom forms, Shaymin Sky Forme, Oricorio styles, etc., are considered different entries.
- On the other hand, the different Deoxys forms, or the different Basculin colors are not considered different entries.
Observations
Playing around with the tool, we can make some fun observations:
- Secondary Flying is unique in that it forms a combination with every other type. This has been true since Gen 6, thanks to Hawlucha. Primary Water and Primary Rock come close, but are missing two each: Water/Normal, Water/Fire, Rock/Normal, and Rock/Ghost don't exist yet.
- Dragon forms a type combination with every other type except for Bug. This has been true since Gen 7, thanks to Drampa and the Kommo-o line.
- Recently, Pokemon with Flying as a primary type have been showing up. It started with Noibat and Noivern (Dragon/Flying) in Gen 6, but Gen 8 added Cramorant (Flying/Water) and Corviknight (Flying/Steel).
- Flying remains the rarest single-type by a large margin. Tornadus used to hold the title of the only pure Flying type, but Gen 8 added Rookidee and Corvisquire.
- Steel is the next rarest single-type with 11 (including Galarian Meowth and Mega Aggron).
- In Gen 1 alone, six types have no single-type Pokemon: Ice, Flying, Rock, Ghost, Dark, Steel.
- Rock, Ghost, Dark were added in Gen 2 (Sudowoodo, Misdreavus, Umbreon).
- Ice and Steel were added in Gen 3 (Snorunt/Glalie/Regice, Registeel).
- Flying was added in Gen 5 (Tornadus).
- Ignoring primary vs. secondary, there are 16 type combinations that do not have any Pokemon:
- Normal/Ice
- Normal/Poison
- Normal/Bug
- Normal/Rock
- Normal/Steel
- Fire/Grass
- Fire/Fairy
- Electric/Fighting
- Ice/Poison
- Fighting/Ground
- Fighting/Fairy
- Poison/Steel
- Ground/Fairy
- Bug/Dragon
- Bug/Dark
- Rock/Ghost
- Ignoring primary vs. secondary, there are 24 type combinations that only have one Pokemon:
- Normal/Water - Bibarel
- Normal/Dragon - Drampa
- Fire/Water - Volcanion
- Fire/Electric - Rotom (Heat)
- Fire/Ice - Galarian Darmanitan (Zen)
- Fire/Steel - Heatran
- Water/Steel - Empoleon
- Grass/Ground - Torterra
- Electric/Ground - Stunfisk
- Electric/Psychic - Alolan Raichu
- Electric/Ghost - Rotom
- Electric/Dark - Morpeko
- Ice/Dragon - Kyurem (and Black Kyurem/White Kyurem)
- Ice/Fighting - Crabominable
- Ice/Ghost - Froslass
- Ice/Fairy - Alolan Ninetales
- Fighting/Rock - Terrakion
- Fighting/Ghost - Marshadow
- Poison/Rock - Nihilego
- Poison/Fairy - Galarian Weezing
- Bug/Ghost - Shedinja
- Rock/Dark - Tyranitar (and Mega Tyranitar)
- Ghost/Fairy - Mimikyu
- Dragon/Fairy - Mega Altaria
- In addition, 27 more type combinations only have one evolution line:
- Normal/Fire - Litleo, Pyroar
- Normal/Grass - Deerling, Sawsbuck
- Normal/Electric - Helioptile, Heliolisk
- Normal/Ghost - Hisuian Zoroa, Hisuian Zoroark
- Fire/Poison - Salandit, Salazzle
- Water/Grass - Lotad, Lombre, Ludicolo
- Grass/Ice - Snover, Abomasnow
- Grass/Rock - Lileep, Cradily
- Electric/Poison - Toxel, Toxtricity
- Electric/Rock - Alolan Geodude, Alolan Graveler, Alolan Golem
- Ice/Steel - Alolan Sandshrew, Alolan Sandslash
- Ice/Dark - Sneasel, Weavile
- Ice/Bug - Snom, Frosmoth
- Ice/Ground - Swinub, Pilowsine, Mamoswine
- Fighting/Dragon - Hakamo-o, Kommo-o
- Poison/Ghost - Gastly, Haunter, Gengar
- Poison/Psychic - Galarian Slowbro, Galarian Slowking
- Poison/Flying - Zubat, Golbat, Crobat
- Poison/Ground - Nidoqueen, Nidoking
- Ground/Psychic - Baltoy, Claydol
- Ground/Dark - Sandile, Krokorok, Krookodile
- Psychic/Bug - Dottler, Orbeetle
- Bug/Fairy - Cutiefly, Ribombee
- Rock/Dragon - Tyrunt, Tyrantrum
- Ghost/Steel - Honedge, Doublade, Aegislash
- Dark/Fairy - Impidimp, Morgrem, Grimmsnarl
- Dark/Steel - Pawniard, Bisharp
Development
For those curious about how this was created, I first wrote a one-off Python script with the help of the Beautiful Soup web scraping library. This script scrapes the table from https://pokemondb.net/pokedex/all and molds it into a data format that can be consumed by Javascript.
The actual HTML page at michaelcw.com/pokemon_types/ simply expresses the table (without any contents) as well as the control elements, and leaves it to a couple of Javascript files to dynamically load the data and populate the table. You can peek at the Javascript here (although be warned that my Javascript is very... amateur).