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
Your Guide to How to Budget Money - NerdWallet
I need to transfer my ETH from BSC Network to Ethereum Mainnet
Davita Internet
Ets Lake Fork Fishing Report
Caroline Cps.powerschool.com
Lycoming County Docket Sheets
Nichole Monskey
Slmd Skincare Appointment
A Guide to Common New England Home Styles
Highland Park, Los Angeles, Neighborhood Guide
Espn Horse Racing Results
Hanger Clinic/Billpay
Vintage Stock Edmond Ok
Axe Throwing Milford Nh
Mychart Anmed Health Login
Crawlers List Chicago
Dragger Games For The Brain
Glover Park Community Garden
Target Minute Clinic Hours
Meta Carevr
Where to eat: the 50 best restaurants in Freiburg im Breisgau
Deepwoken: Best Attunement Tier List - Item Level Gaming
Promatch Parts
Helloid Worthington Login
Taktube Irani
Boneyard Barbers
A Grade Ahead Reviews the Book vs. The Movie: Cloudy with a Chance of Meatballs - A Grade Ahead Blog
2430 Research Parkway
Aladtec Login Denver Health
Seymour Johnson AFB | MilitaryINSTALLATIONS
The Syracuse Journal-Democrat from Syracuse, Nebraska
Anya Banerjee Feet
One Main Branch Locator
Pay Entergy Bill
877-292-0545
Sam's Club Gas Prices Deptford Nj
Umiami Sorority Rankings
manhattan cars & trucks - by owner - craigslist
Dinar Detectives Cracking the Code of the Iraqi Dinar Market
Cocaine Bear Showtimes Near Cinemark Hollywood Movies 20
Comanche Or Crow Crossword Clue
Borat: An Iconic Character Who Became More than Just a Film
Spurs Basketball Reference
Oakley Rae (Social Media Star) – Bio, Net Worth, Career, Age, Height, And More
Sitka Alaska Craigslist
Craigslist Cars For Sale By Owner Memphis Tn
Craigslist Pets Lewiston Idaho
Kenmore Coldspot Model 106 Light Bulb Replacement
Tyrone Dave Chappelle Show Gif
Nkey rollover - Hitta bästa priset på Prisjakt
Service Changes and Self-Service Options
Overstock Comenity Login
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.