Hooked on Data - What Types Should You Have on Your Pokémon Team? Efficient Simulation with Matrices in R (2024)

I recently started playing Pokémon again - “Pokémon Let’s Go Eevee” on the Nintendo Switch to be specific. In the classic Pokémon games, you have a team of 6 Pokémon that you use to battle against other trainers. In battles, type match-ups are very important, as some types of moves are “super effective” against other types. For example, fire moves are super effective against grass Pokémon, which means they do double the damage they normally would. If you can set your team up so that you’re always optimally matched, you’re going to have a much easier time.

But there are 18 types and you only get 6 Pokémon on your team. This leads to the question - what are the combinations of 6 types that make you super effective against the most types of Pokémon?1 It turns out this is a question a lot of people have asked.

I knew there was a chart out there that matches up every attacking type against every defending and tells you whether they’re super effective, normal, not very effective, or doesn’t have any effect. So I decided to use my R skills to answer this question (many thanks to my brother David Robinson for his guidance at various points). Along the way, we’ll do a quick exploratory analysis, learn about combinatorials, and leave the tidyverse to use matrices and some base functions.

Data Exploration

I found a csv of the Pokémon type chart on GitHub. Using read_csv() on the url didn’t work, and rather than try to debug it, I decided to “cheat”” and use the magic package datapasta package. On Github, I clicked to edit the file, copied everything in it, and then used tribble_paste(), which output my clipboard into the code that would create a tibble I called type_comparisons.

[Added 8/26]: As Jim Hester kindly pointed out, read_csv() will work if I use it on the raw link, generated by clicking the “Raw” button.

library(tidyverse)# this didn't work# type_comparisons <- read_csv("https://github.com/robinsones/pokemon-chart/blob/master/chart.csv")
library(datapasta)# use tribble_paste()type_comparisons <- tibble::tribble( ~Attacking, ~Normal, ~Fire, ~Water, ~Electric, ~Grass, ~Ice, ~Fighting, ~Poison, ~Ground, ~Flying, ~Psychic, ~Bug, ~Rock, ~Ghost, ~Dragon, ~Dark, ~Steel, ~Fairy, "Normal", 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.5, 0, 1, 1, 0.5, 1, "Fire", 1, 0.5, 0.5, 1, 2, 2, 1, 1, 1, 1, 1, 2, 0.5, 1, 0.5, 1, 2, 1, "Water", 1, 2, 0.5, 1, 0.5, 1, 1, 1, 2, 1, 1, 1, 2, 1, 0.5, 1, 1, 1, "Electric", 1, 1, 2, 0.5, 0.5, 1, 1, 1, 0, 2, 1, 1, 1, 1, 0.5, 1, 1, 1, "Grass", 1, 0.5, 2, 1, 0.5, 1, 1, 0.5, 2, 0.5, 1, 0.5, 2, 1, 0.5, 1, 0.5, 1, "Ice", 1, 0.5, 0.5, 1, 2, 0.5, 1, 1, 2, 2, 1, 1, 1, 1, 2, 1, 0.5, 1, "Fighting", 2, 1, 1, 1, 1, 2, 1, 0.5, 1, 0.5, 0.5, 0.5, 2, 0, 1, 2, 2, 0.5, "Poison", 1, 1, 1, 1, 2, 1, 1, 0.5, 0.5, 1, 1, 1, 0.5, 0.5, 1, 1, 0, 2, "Ground", 1, 2, 1, 2, 0.5, 1, 1, 2, 1, 0, 1, 0.5, 2, 1, 1, 1, 2, 1, "Flying", 1, 1, 1, 0.5, 2, 1, 2, 1, 1, 1, 1, 2, 0.5, 1, 1, 1, 0.5, 1, "Psychic", 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 0.5, 1, 1, 1, 1, 0, 0.5, 1, "Bug", 1, 0.5, 1, 1, 2, 1, 0.5, 0.5, 1, 0.5, 2, 1, 1, 0.5, 1, 2, 0.5, 0.5, "Rock", 1, 2, 1, 1, 1, 2, 0.5, 1, 0.5, 2, 1, 2, 1, 1, 1, 1, 0.5, 1, "Ghost", 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 0.5, 1, 1, "Dragon", 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 0.5, 0, "Dark", 1, 1, 1, 1, 1, 1, 0.5, 1, 1, 1, 2, 1, 1, 2, 1, 0.5, 1, 0.5, "Steel", 1, 0.5, 0.5, 0.5, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 0.5, 2, "Fairy", 1, 0.5, 1, 1, 1, 1, 2, 0.5, 1, 1, 1, 1, 1, 1, 2, 2, 0.5, 1 )

To make it easier to explore the data, I’m going to start by tidying it.

tidied_comparison <- type_comparisons %>% gather(Defending, outcome, -Attacking)tidied_comparison %>% slice(103:109) %>% knitr::kable()
AttackingDefendingoutcome
RockIce2
GhostIce1
DragonIce1
DarkIce1
SteelIce2
FairyIce1
NormalFighting1

We now have a dataset of 324 rows, with each Attacking-Defending combination and what the outcome is. Here, outcome is 2 if it’s super effective (what we’re interested in), 1 if normal, .5 if not very effective, and 0 if no effect.

What types are super effective against the most other types?

tidied_comparison %>% group_by(Attacking) %>% summarize(nb_super_effective = sum(ifelse(outcome == 2, 1, 0))) %>% arrange(desc(nb_super_effective)) %>% knitr::kable()
Attackingnb_super_effective
Fighting5
Ground5
Fire4
Ice4
Rock4
Bug3
Fairy3
Flying3
Grass3
Steel3
Water3
Dark2
Electric2
Ghost2
Poison2
Psychic2
Dragon1
Normal0

Fighting and Ground are both super effective against 5 different types, while Normal isn’t super effective against any.

Are there any types where only one Attacking type is super-effective?

tidied_comparison %>% filter(outcome == 2) %>% add_count(Defending) %>% arrange(n) %>% head(4) %>% knitr::kable()
AttackingDefendingoutcomen
FightingNormal21
GroundElectric21
ElectricWater22
GrassWater22

Yes - if we want to be super effective against Normal and Electric types, we need Fighting and Ground types respectively.

Building Pokémon Teams

The first step is to build out all the hypothetical teams of 6. If you remember your introduction to statistics days, this is a combinatorial problem: we have 18 options (although we don’t expect “Normal” to show up since it’s not super effective against anything), need to choose 6, and the order doesn’t matter (e.g.1 to 6 is the same as 6 to 1). We can do this in R with the function combn:

all_combinations <- combn(18, 6)dim(all_combinations)
[1] 6 18564

all_combinations is a 6 by 18,564 matrix: each column is a different combination of types. For example, let’s look at the first two columns:

all_combinations[, 1:2]
 [,1] [,2][1,] 1 1[2,] 2 2[3,] 3 3[4,] 4 4[5,] 5 5[6,] 6 7

The first column is one team with the types 1 through 6, while the second is a team with 1 through 5 and 7.

Now we need to take this and understand how many types each team is super effective against.

Matrix Magic

I originally was thinking of calling this post “Going back to the Base[ics],” since I’m moving out of the tidyverse and into the world of matrices, but there’s really nothing basic about this. Let’s walk through it step by step.

First, we’re going to take our table and make it a matrix. We can’t just do as.matrix() directly, as it will make the Attacking column the first column, while we want that to be the rownames, so we’ll do it in two steps.

m <- as.matrix(type_comparisons[, -1])rownames(m) <- type_comparisons$Attacking

Next, because we only care about whether the entry is 2 or not, we’ll change every entry that’s a 2 to be 1 and every entry that’s not to be 0 (the 1L * makes it 1 or 0 instead of TRUE or FALSE).

super_effective_m <- (m == 2) * 1L
super_effective_m
 Normal Fire Water Electric Grass Ice Fighting Poison Ground FlyingNormal 0 0 0 0 0 0 0 0 0 0Fire 0 0 0 0 1 1 0 0 0 0Water 0 1 0 0 0 0 0 0 1 0Electric 0 0 1 0 0 0 0 0 0 1Grass 0 0 1 0 0 0 0 0 1 0Ice 0 0 0 0 1 0 0 0 1 1Fighting 1 0 0 0 0 1 0 0 0 0Poison 0 0 0 0 1 0 0 0 0 0Ground 0 1 0 1 0 0 0 1 0 0Flying 0 0 0 0 1 0 1 0 0 0Psychic 0 0 0 0 0 0 1 1 0 0Bug 0 0 0 0 1 0 0 0 0 0Rock 0 1 0 0 0 1 0 0 0 1Ghost 0 0 0 0 0 0 0 0 0 0Dragon 0 0 0 0 0 0 0 0 0 0Dark 0 0 0 0 0 0 0 0 0 0Steel 0 0 0 0 0 1 0 0 0 0Fairy 0 0 0 0 0 0 1 0 0 0 Psychic Bug Rock Ghost Dragon Dark Steel FairyNormal 0 0 0 0 0 0 0 0Fire 0 1 0 0 0 0 1 0Water 0 0 1 0 0 0 0 0Electric 0 0 0 0 0 0 0 0Grass 0 0 1 0 0 0 0 0Ice 0 0 0 0 1 0 0 0Fighting 0 0 1 0 0 1 1 0Poison 0 0 0 0 0 0 0 1Ground 0 0 1 0 0 0 1 0Flying 0 1 0 0 0 0 0 0Psychic 0 0 0 0 0 0 0 0Bug 1 0 0 0 0 1 0 0Rock 0 1 0 0 0 0 0 0Ghost 1 0 0 1 0 0 0 0Dragon 0 0 0 0 1 0 0 0Dark 1 0 0 1 0 0 0 0Steel 0 0 1 0 0 0 0 1Fairy 0 0 0 0 1 1 0 0

The all_combinations matrix we created before is essentially a set of indices for the super_effective_m matrix. For example, column 1 of all_combinations are the numbers 1 through 6, which means we want to get rows 1 through 6 of super_effective_m. Remember, each row of super_effective_m is an attacking type on our team, and each column is a defending type. We then want to get the sum of each column and know how many columns have a sum of more than 0, meaning at least one of our attacking types was super effective against it. We’ll make a function, super_effective_nb:

super_effective_nb <- function(indices) { sum(colSums(super_effective_m[indices, ]) > 0)}

Now we can use apply() to get a vector, for all 18k+ teams, of how many types they’re super effective against. If you’re not familiar with apply(), the first argument is what we’re applying our function to, the second is whether it should apply to the rows or columns (we choose 2 for column, since each column is the team), and the third is the function.

super_effective_results <- apply(all_combinations, 2, super_effective_nb)

What are the combinations that are super effective against the maximum number of types possible?

which(super_effective_results == max(super_effective_results))
 [1] 14323 14325 15610 15612 16454 16459 16852 16854 16989 16994

We see there are 10 possible combinations of six types. Let’s take a look at them by getting those columns from all_combinations.

best_combos <- all_combinations[, super_effective_results == max(super_effective_results)]best_combos
 [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10][1,] 4 4 5 5 5 5 6 6 6 6[2,] 6 6 6 6 8 8 7 7 7 7[3,] 7 7 7 7 9 9 8 8 9 9[4,] 9 9 9 9 13 13 9 9 10 10[5,] 10 10 10 10 14 16 10 10 14 16[6,] 14 16 14 16 18 18 14 16 17 17

We now have a matrix, best_combo, where each column is a team. For example, we see a team of types 4, 6, 7, 9, 10, and 14 cover the maximum number of defending types. But what is type 4? To answer that, we take the row names from super_effective_m and index it by best_combos.

rownames(super_effective_m)[best_combos]
 [1] "Electric" "Ice" "Fighting" "Ground" "Flying" "Ghost" [7] "Electric" "Ice" "Fighting" "Ground" "Flying" "Dark" [13] "Grass" "Ice" "Fighting" "Ground" "Flying" "Ghost" [19] "Grass" "Ice" "Fighting" "Ground" "Flying" "Dark" [25] "Grass" "Poison" "Ground" "Rock" "Ghost" "Fairy" [31] "Grass" "Poison" "Ground" "Rock" "Dark" "Fairy" [37] "Ice" "Fighting" "Poison" "Ground" "Flying" "Ghost" [43] "Ice" "Fighting" "Poison" "Ground" "Flying" "Dark" [49] "Ice" "Fighting" "Ground" "Flying" "Ghost" "Steel" [55] "Ice" "Fighting" "Ground" "Flying" "Dark" "Steel" 

This gets us a character vector though. It’s in order, so we know that the first six is team 1, the second six team 2, etc., but it’s not displayed very well. We can use matrix to turn this into a matrix instead, specifying that we want 6 rows.

strongest_teams <- matrix(rownames(super_effective_m)[best_combos], nrow = 6)
strongest_teams
 [,1] [,2] [,3] [,4] [,5] [,6] [,7] [1,] "Electric" "Electric" "Grass" "Grass" "Grass" "Grass" "Ice" [2,] "Ice" "Ice" "Ice" "Ice" "Poison" "Poison" "Fighting"[3,] "Fighting" "Fighting" "Fighting" "Fighting" "Ground" "Ground" "Poison" [4,] "Ground" "Ground" "Ground" "Ground" "Rock" "Rock" "Ground" [5,] "Flying" "Flying" "Flying" "Flying" "Ghost" "Dark" "Flying" [6,] "Ghost" "Dark" "Ghost" "Dark" "Fairy" "Fairy" "Ghost" [,8] [,9] [,10] [1,] "Ice" "Ice" "Ice" [2,] "Fighting" "Fighting" "Fighting"[3,] "Poison" "Ground" "Ground" [4,] "Ground" "Flying" "Flying" [5,] "Flying" "Ghost" "Dark" [6,] "Dark" "Steel" "Steel" 

For our final step, we’re actually going to make this a tibble, so I can look at which types appear the most often across the different team possibilities.

strongest_teams %>% as_tibble() %>% gather(team, type) %>% count(type, sort = TRUE) %>% knitr::kable()
typen
Ground10
Fighting8
Flying8
Ice8
Dark5
Ghost5
Grass4
Poison4
Electric2
Fairy2
Rock2
Steel2

We see all 10 of the teams need a ground type, where 8 have a Fighting, Flying, or Ice type. On the other hand, Electric, Fairy, Rock, and Steel are only each used by two teams.

Conclusion

While this is a bit of a silly use case, the code we walked through and lessons learned could be applied to a lot of different projects. When I advise people to make a portfolio of data science projects if they’re looking for a job, I sometimes get asked, “But how do I find something to work on?” I recommend looking in your own life and interests where you could use data science. If you’re a runner and use an activity tracker, graph how your run distances and times are related to the weather. If you’re active on a subreddit, you could use the reddit API to get the last 500 posts and do a text analysis. The possibilities are limitless!

I also played around with getting this code even faster and trying to do everything in a tidy way instead. Including those methods made the post run a little too long, so I may follow up with a part 2 of this post. To make this analysis more useful, I could also take into account how common types are - for example, only a few Pokémon have a Dragon type, so it’s less important to have types that are super effective against Dragon.

[1] Pokémon players will know that you can have more than 6 types on your team, both because some Pokémon have two types and because Pokémon can learn moves of other types (e.g.a Normal type Pokémon may be able to learn a Dark move). But for the purposes of this analysis I simplified it.

Hooked on Data - What Types Should You Have on Your Pokémon Team? Efficient Simulation with Matrices in R (2024)
Top Articles
How To Make a Tracker on Google Sheets (FAQs) | LiveFlow
Inverter vs. Converter: Which Do You Need For Your Camper Life?
Katie Pavlich Bikini Photos
Gamevault Agent
Hocus Pocus Showtimes Near Harkins Theatres Yuma Palms 14
Free Atm For Emerald Card Near Me
Craigslist Mexico Cancun
Hendersonville (Tennessee) – Travel guide at Wikivoyage
Doby's Funeral Home Obituaries
Vardis Olive Garden (Georgioupolis, Kreta) ✈️ inkl. Flug buchen
Select Truck Greensboro
Things To Do In Atlanta Tomorrow Night
How To Cut Eelgrass Grounded
Pac Man Deviantart
Alexander Funeral Home Gallatin Obituaries
Craigslist In Flagstaff
Shasta County Most Wanted 2022
Energy Healing Conference Utah
Testberichte zu E-Bikes & Fahrrädern von PROPHETE.
Aaa Saugus Ma Appointment
Geometry Review Quiz 5 Answer Key
Walgreens Alma School And Dynamite
Bible Gateway passage: Revelation 3 - New Living Translation
Yisd Home Access Center
Home
Shadbase Get Out Of Jail
Gina Wilson Angle Addition Postulate
Celina Powell Lil Meech Video: A Controversial Encounter Shakes Social Media - Video Reddit Trend
Walmart Pharmacy Near Me Open
Dmv In Anoka
A Christmas Horse - Alison Senxation
Ou Football Brainiacs
Access a Shared Resource | Computing for Arts + Sciences
Pixel Combat Unblocked
Cvs Sport Physicals
Mercedes W204 Belt Diagram
Rogold Extension
'Conan Exiles' 3.0 Guide: How To Unlock Spells And Sorcery
Teenbeautyfitness
Weekly Math Review Q4 3
Facebook Marketplace Marrero La
Nobodyhome.tv Reddit
Topos De Bolos Engraçados
Gregory (Five Nights at Freddy's)
Grand Valley State University Library Hours
Holzer Athena Portal
Hampton In And Suites Near Me
Stoughton Commuter Rail Schedule
Bedbathandbeyond Flemington Nj
Free Carnival-themed Google Slides & PowerPoint templates
Otter Bustr
Selly Medaline
Latest Posts
Article information

Author: Sen. Emmett Berge

Last Updated:

Views: 5946

Rating: 5 / 5 (60 voted)

Reviews: 91% of readers found this page helpful

Author information

Name: Sen. Emmett Berge

Birthday: 1993-06-17

Address: 787 Elvis Divide, Port Brice, OH 24507-6802

Phone: +9779049645255

Job: Senior Healthcare Specialist

Hobby: Cycling, Model building, Kitesurfing, Origami, Lapidary, Dance, Basketball

Introduction: My name is Sen. Emmett Berge, I am a funny, vast, charming, courageous, enthusiastic, jolly, famous person who loves writing and wants to share my knowledge and understanding with you.