1. Introduction

1.1 A Bit About Me

Hellow, I’m Gabriel, but you may call me Yeye for convenience, and I would prefer you to call me that :)

1.2 A bit about Yu-Gi-Oh!

In this portofolio builder, I would like to to analyze Yu-Gi-Oh! card game, spesifically the TCG (English) version. YGO is in my kind of biased opinion, the best card game I’ve ever played, and I stumbled upon this dataset by chance.

Sadly though, I have left playing it since long ago because of money issues. If you don’t know, a single card can cost a maximum of Rp 1.000.000, or maybe more.

And also, for simplicity, I’ll refer to Yu-Gi-Oh! simply as YGO.

1.3 A Little Note About This Portofolio

In this portofolio, I want to explore tidyverse, which is R package (more like a bundle), that can generalize the style of data analysis using R, since many people have different coding styles / approach, using tidyverse makes the code style uniform and much more simple and easy to understand.

1.4 Libraries

library(dplyr)
library(lemon)
library(data.table)
library(ggplot2)

2. Meet The Data

2.1 Dataset Brief

The datasets are provided by James Palmer from kaggle.com, and the data itself was scraped from Yu-Gi-Oh! Fandom Wiki. It consist of 4 files, which upon slight inspection, it’s actually quite similar.

yugi <- read.csv("dataset/yugi.csv")
View(yugi)

2.2 Dataset Characteristics

2.2.1 Parameters

  • Name : Card name
  • Card.Type : Monster / Spell / Trap card
  • Attribute : Monster Attribute (LIGHT, DARK, EARTH, WATER, etc)
  • Monster.Type : Monster type (Fairy, Zombie, Fiend, Pyro, etc), also represents the Monster Type (Synchro, Effect, Xyz, etc)
  • Level.Rank : Monster Level / Xyz Monster Rank
  • ATK.DEF : ATK & DEF of the monster
  • Passcode : Card code printed on the bottom left side
  • Materials…Ritual.spell : Material for Synchro Monsters / Fusion Monsters / Xyz Monsters / Link Monsters
  • Effect.Type : This is a bit complex, but I can give you an easy example, suppose it’s Continuous , then as long as that card is face up on the field, the effect will remain.
  • Effect : The actual effect of the card printed on it.
  • Spell.Trap.type : The type of the Spell/Trap card, like Equip / Continuous / Ritual / etc
  • TCG.sets : This represents the package code where the card originated from. A package can be a Structure Deck , Collector Tin , Booster Pack , etc. A card may have more than 1 package code because there’s a possibility of “Reprinting” the card to introduce lower money-cost
  • Ban_list : Banlist status of the card, either Unlimited / Semi Limited / Limited / Forbidden
  • Number.of.sets : If it’s more than 1, then the card has already been reprinted.
  • Link.Arrows : The direction of Link Arrow for Link Monster, it’s octagonal (8-way) and may consist of more than 1 arrow. The number of arrows also represents the Link Rating, which is like Level / Rank for other Monster.
  • Pendulum.Scale : The pendulum scale of Pendulum Monsters.
  • Set.Name : The card package name
  • Relase.Date : Well… the release date.
names(yugi)
##  [1] "X"                        "Name"                    
##  [3] "Card.type"                "Attribute"               
##  [5] "Monster.Type"             "Level.Rank"              
##  [7] "ATK.DEF"                  "Passcode"                
##  [9] "Materials...Ritual.spell" "Effect.type"             
## [11] "Effect"                   "Spell.Trap.type"         
## [13] "TCG.sets"                 "Ban_list"                
## [15] "Number.of.sets"           "Link.Arrows"             
## [17] "Pendulum.Scale"           "Rarities"                
## [19] "Set.Name"                 "Release.Date"

2.2.2 A Little Peek to the Data

head(yugi, 10)

2.3 Problem

2.3.1 What Do I Want by Analyzing The Data

  1. What is the current trending monster Type?
  2. What is the current trending monster Attribute?
  3. Is the META still controlled by Banlist?
  4. Which is more preferable from Xyz, Synchro, Pendulum, or Link?

2.3.2 Why those 3 Problems?

In YGO, the first 2 points are related to each other. The current META (Most Effective Tactic Available) is determined by these 2 points. Since the last time I played the game, the game has already moved into a very fast paced Monsters Special Summoning spam. Whoever sets their field with their desired Monster combos will likely have higher chance to win the game. And also, I want to point out, KONAMI will have a higher chance to create new cards related to the current META, the reason was obviously for marketting. By introducing a banlist and certain cards, and then creating new cards that can “hopefully” be a substitute, players will have to have higher tendency buy them if they want their deck to stay on META. That’s why we can infer what’s in the META currently by just taking a look at the how the Monster Types & Attributes are currently spread out.

In YGO, there’s also something like a family of monsters, that can be grouped together, creating the best combo synergy when the player deck consist of only those monster groups:

  • Have identical / even same prefix / suffix Name AND/OR
  • Have identical Monster Type or Attribute AND/OR
  • Have similar OR identical Monster Effect

These family of monsters are called Archetype. Even without us inferring which specific Archetype is the current trending topic, we can infer them by their Type and Attribute. Although there are also Archetype that are Non-Monster based, the Monster based Archetype are most favorable, since not only they carry effects, but can give influence in Battle Phase, while Non-Monster based only relies on effects.

Since we want to gain information of meta from monsters, we don’t need to include other Card types. And since the card name is unique by nature, we most likely will only need:

Why do we need Banlist? Let me explain a little bit. Cards that have absolutely God-like effects, will most likely enter the Banlist, either they’re Limited or even Forbidden. Here’s a summary of how banlist works:

  • Forbidden: the card are not to be put on deck under any condition of formal duel
  • Limited: only 1 copies of the card may be used on the deck
  • Semi-Limited: only 2 copies of the card may be used on the deck
  • Unlimited (not on banlist): we can play 3 copies of the card on the deck

Most of the time, cards are in the banlist because they either: - Have the effect of easily changing the game - Have the effect of making the duel ends in a DRAW (neither player win/lose) - Have a really good effect but simple cost - That particular card in an Archetype tremendously improves the synergy & combo on their corresponding Arhcetype

I can give you 1 example of 1 card, that immediately not long after it was released, ENTERS THE FORBIDDEN LIST.

Image source: https://yugipedia.com

Image source: https://yugipedia.com

The problem with this card is it’s second effect: “If a monster this card points to is destroyed by battle or sent to the GY: You can Special Summon 1 monster from your hand

This effect does not have a constraint, meaning, it can activate at any point in time, whenever monsters pointed by the Link-Arrow is sent to the Graveyard, and also can activate however many times the owner wants, meaning, you can get 1 free Monster summon each time a monster You or your opponent controls that are pointed by this card is sent to GY.

Sent to GY is the second problem. Sent to GY is a little bit broad, since a monster can be send to GY by many means:

  • Destroyed by battle
  • Destroyed by card-effect
  • Used for other cost that requires sending Monster on the field to GY
  • etc, in general, monsters that are out of the field will go to Graveyard!

This means, if you just sacrificed a Monster on the field for some effect, you can immediately summon another Monster to replace that fodder (term: sacrificed monster), and you won’t lose any advantages. Moreover, there are many monsters that triggers their effect when They are summonned.

This effect is simple, but can be deadly, as it tremendously boost the combo of ANY DECK that can use Summon Firewall Dragon.

Want to know something more? You only need to sacrifice ANY 4 monsters to get this card to play. And I should tell you, Summoning 4 monsters in a single turn is an easy matter at the current state of YGO game. When Firewall Dragon was released, players all around the world have been able to pull a combo of > 20 Special Summons in a single turn, only because this card existed.

2.3.3 Card Parameters of Interest

From the explanation above, I most likely will need things that only exist on Monster Card type as follows:

  • Monster Card Type
  • Monster Attribute
  • Monster Level/Rank (since there are Archetypes with similar level too)
  • Monster Type
  • Banlist

3. Processing the Data

3.1 Data Wrangling

3.1.1 Picking Up the Important Parameters

Let’s also include a conditional subsetting using dplyr::filter() to pick Card.type of Monsters only. To do this, we need to Card.type to factor type first.

df_main <- yugi
df_main$Card.type = factor(df_main$Card.type)

levels(df_main$Card.type)
## [1] "Monster " "Spell "   "Trap "

Hmm weird, the categorical for some reason has a blank whitespace at the end of it, this made me go visible confusion for 1 hours LOL.

Next, the main subsetting fun. This process is done in a piping style, as recommended by tidyverse.

monsters <- df_main
monsters %>%
dplyr::filter(Card.type == "Monster ") %>%
dplyr::select(
  Monster.Type, 
  Attribute, 
  Level.Rank, 
  Effect.type, 
  Ban_list
) -> monsters

head(monsters)

3.1.2 Transforming

For transforming, I want to do quite a lot of things:

Arranging

Monster.Type , Attribute, Effect.type and Banlist are all categorical, which means we need to transform them so it becomes a factor. We’ll keep the rest as is.

Also I want to arrange the dataframe in way like this, starting from left-side:

  1. Attribute
  2. Monster Type
  3. Level
  4. Banlist

Determine if it is Non-Effect Monster

And also, I want to filter out the Non-Effect monsters, we can do this by checking whether the Effect.type parameter is empty or not.

monsters_transmuted <- monsters

monsters_transmuted %>%
dplyr::filter(Effect.type != "") %>%
dplyr::transmute(
  Attribute = factor(Attribute),
  Level.Rank = Level.Rank,
  Monster.Type = factor(Monster.Type),
  Ban_list = Ban_list
) -> monsters_transmuted

head(monsters_transmuted)

3.1.3 Splitting The Monster Type

Now the dataset is quite tidied up, but there’s still 2 major issues. The first one, The Monster.Type, is not specified as it’s real monster type! The true monster type are currently only:

  • Aqua
  • Beast
  • Beast-Warrior
  • Cyberse
  • Dinosaur
  • Divine-Beast
  • Dragon
  • Fairy
  • Fiend
  • Fish
  • Insect
  • Machine
  • Plant
  • Psychic
  • Pyro
  • Reptile
  • Rock
  • Sea Serpent
  • Spellcaster
  • Thunder
  • Warrior
  • Winged Beast
  • Wyrm
  • Zombie

And the “Monster Card” types (which can be determined from the card color), are only:

  • Normal Monsters
  • Effect Monsters
  • Ritual Monsters
  • Fusion Monsters
  • Synchro Monsters
  • Xyz Monsters
  • Pendulum Monsters
  • Link Monsters

We have already filtered out the Normal Monsters out when we check the Effect.type is empty or not. But we have not “bisect” the rest. And I need to point out that the “Effect” monster replace term will have to come FIRST, otherwise all types will be replaced as “Effect” hehehe :D

You might be wondering types like Union or Gemini monsters and such, I consider this to be a “secondary” type and only signifies that it’s effect has a certain pattern.

The final concern, I just noticed, that the banlist, is not particularly clean, I don’t know if it’s a mistake on the raw data, but for some reason, there are “Effect Texts” mixed in one of the Ban_list data.

So we need to fix all of that. Firstly, for convenience we can check this using %like% from data.table library, and using conditional ifelse(). Also, to be on the safe side, let’s create a new column first.

I will create a new vector of those “true monster types”, “true monster card types”, and “Ban” type, and then iterate through it.

monster_types <- c(
  "Aqua",
  "Beast" ,
  "Beast-Warrior",
  "Cyberse" ,
  "Dinosaur",
  "Divine-Beast" ,
  "Dragon",
  "Fairy" ,
  "Fiend",
  "Fish" ,
  "Insect",
  "Machine" ,
  "Plant",
  "Psychic" ,
  "Pyro",
  "Reptile" ,
  "Rock",
  "Sea Serpent" ,
  "Spellcaster",
  "Thunder" ,
  "Warrior",
  "Winged Beast" ,
  "Wyrm",
  "Zombie"
)

monster_card_types <- c(
  "Effect",
  "Ritual",
  "Fusion",
  "Synchro",
  "Xyz",
  "Pendulum",
  "Link"
)

ban_type <- c(
  "Unlimited",
  "Forbidden",
  "Limited",
  "Semi-Limited"
)

# head(monsters_transmuted$Monster.Type)


# Fill with a copy of data
monsters_transmuted$Type <- monsters_transmuted$Monster.Type
monsters_transmuted$Card.type <- monsters_transmuted$Monster.Type
monsters_transmuted$Banlist <- monsters_transmuted$Ban_list

# iterate through the monster_types vector
for (type in monster_types) {
  monsters_transmuted$Type <- ifelse(
    monsters_transmuted$Monster.Type %like% type,
    type,
    monsters_transmuted$Type
  )
}

# iterate through the monster_card_types vector
for (card_type in monster_card_types) {
  monsters_transmuted$Card.type <- ifelse(
    monsters_transmuted$Monster.Type %like% card_type,
    card_type,
    monsters_transmuted$Card.type
  )
}

for (type in ban_type) {
  monsters_transmuted$Banlist <- ifelse(
    monsters_transmuted$Ban_list %like% type,
    type,
    monsters_transmuted$Banlist
  )
}

# Let's not forget to change it to categorical type
monsters_transmuted$Type = factor(monsters_transmuted$Type)

head(monsters_transmuted)

Cleaning the broken banlists

monsters_fixed_banlist <- monsters_transmuted

# levels(factor(monsters_fixed_banlist$Banlist))

monsters_fixed_banlist[which(
  !(
    monsters_fixed_banlist$Banlist == "Forbidden" |
    monsters_fixed_banlist$Banlist == "Limited" |
    monsters_fixed_banlist$Banlist == "Semi-Limited" |
    monsters_fixed_banlist$Banlist == "Unlimited"
  )
), "Banlist"] <- "Unlimited"

monsters_fixed_banlist$Banlist = factor(monsters_fixed_banlist$Banlist)
levels(monsters_fixed_banlist$Banlist)
## [1] "Forbidden"    "Limited"      "Semi-Limited" "Unlimited"

Cleaning the broken Card.type

monsters_fixed_card_type <- monsters_fixed_banlist

# Change to character first, we'll change it again later
monsters_fixed_card_type$Card.type <- as.character(monsters_fixed_card_type$Card.type)

monsters_fixed_card_type[which(
  !(
    monsters_fixed_card_type$Card.type == "Effect" |
    monsters_fixed_card_type$Card.type == "Fusion" |
    monsters_fixed_card_type$Card.type == "Link" |
    monsters_fixed_card_type$Card.type == "Pendulum" |
    monsters_fixed_card_type$Card.type == "Ritual" |
    monsters_fixed_card_type$Card.type == "Synchro" |
    monsters_fixed_card_type$Card.type == "Xyz"
  )
), "Card.type"] <- "Effect"

# let's not forget to change it back to factor
monsters_fixed_card_type$Card.type <- factor(monsters_fixed_card_type$Card.type)

levels(monsters_fixed_card_type$Card.type)
## [1] "Effect"   "Fusion"   "Link"     "Pendulum" "Ritual"   "Synchro"  "Xyz"

Now, we can drop the un-needed column, since we don’t need it anymore. Also I’m clumsy I know, I have to re-arrange the columns again. :)

df_final <- monsters_fixed_card_type

df_final %>%
dplyr::transmute(
  Card.type = df_final$Card.type, 
  Attribute = df_final$Attribute, 
  Type = df_final$Type, 
  Level.Rank = df_final$Level.Rank, 
  Banlist = df_final$Banlist
) -> df_final

head(df_final)

And finally, we can change the NA values from Level.Rank, to level 0. This also signifies that that monster is a Link Monster.

df_final$Level.Rank <- ifelse(
  is.na(df_final$Level.Rank),
  0,
  df_final$Level.Rank
)

head(df_final)

3.1.4 Summary of Clean Data Frame

I will display the summary using lemon. I like to see the summary with better visual :D

# Overwrites the default printing style from knit
knit_print.table <- lemon_print
summary(df_final)
Card.type Attribute Type Level.Rank Banlist
Effect :9952 DARK :3833 Warrior :2204 Min. : 0.000 Forbidden : 138
Fusion : 666 DIVINE : 23 Machine :1578 1st Qu.: 3.000 Limited : 123
Link : 456 EARTH :3011 Spellcaster:1249 Median : 4.000 Semi-Limited: 7
Pendulum: 386 FIRE :1002 Fiend :1235 Mean : 4.467 Unlimited :12567
Ritual : 145 LIGHT :2822 Dragon :1207 3rd Qu.: 6.000
Synchro : 639 WATER :1005 Fairy : 812 Max. :12.000
Xyz : 591 WIND :1139 (Other) :4550

4. Infering the Data

4.1 Visualizing The Data

4.1.1 Monsters Attribute & Type Distribution

theme_set(theme_bw())

ggplot(df_final) +
  labs(title="Monsters Attribute & Type Distribution") +
  geom_bar(aes(Type, fill=Attribute), width=0.7) +
  theme(axis.text.x = element_text(angle=90, vjust=0.5))

Quite a lot we can gain here. Firstly, surprise! Dragon-type is not the winner. I see that KONAMI actually prefers Warrior-Type a lot, with it’s notable Attributes: DARK, LIGHT, and EARTH. Some of you may already know what’s the biggest, best, the most famous warrior Archetype. Yes, the “HERO” Archetype.

Some of the “HERO” sub-archetype that I can think of are:

  • Elemental HERO (Warrior-type monsters with various Attributes)
  • Destiny HERO (Warrior-type monsters with all their member being DARK Attributes)
  • Masked HERO (Fusion Warrior-type monsters with a single monster material)
  • Neos (Fusion Warrior-type monsters that relies on “Elemental HERO - Neos”, various Attributes)

And here’s some more notable mentions that are considered META on their prime era:

  • Utopia (LIGHT, Xyz oriented)
  • Phantom Knight (DARK)
  • Noble Knight (LIGHT & DARK)
  • Tellarknight (LIGHT)
  • Ignknight (not common, but I think it’s the most famous FIRE-Warrior archetype)
  • Six Samurai (various)

And then the second highest is Machine-type. I think I can already infer that the reason behind this high number of Machine & Warrior-type monsters. Possibly it’s because:

  • GX series : The main character, Jaden Yuki, uses Elemental HERO as it’s main deck.
  • 5D’s series : in 5D’s series, the main character, Yusei Fudo, uses an Archetype called “Synchron”. And these are mainly Machines & Warrior type monsters that are geared towards fast Synchro Monsters summoning.
  • ZeXal : in this series, the main character, Yuma Tsukumo, uses a deck which consists mainly of Warrior-type, monsters.

Often, demands for supporting cards for Archetypes used by main characters are high. No wonder KONAMI still releases overly abundant Warrior & Machine type monsters up to this day.

4.1.2 Monster Card Type

Let’s take a look, which are more favourable, is it still Xyz monsters? I will filter out the ordinary Effect monsters, since they are always present in every deck.

card_selection <- dplyr::filter(df_final, Card.type != "Effect")
card_selection$Level.Rank <- factor(card_selection$Level.Rank)

ggplot(card_selection) +
  labs(title="Monster Card Type") +
  geom_bar(aes(Card.type, fill=Level.Rank))

NOTE: I can’t do much about the Link monsters being level 0, the dataset does not provide their Link rating.

Now we see that the Xyz rank 4 is still favorable as always. I remembered the reason was because Rank-4 Xyz has really generic material, and many decks that run Level-4 Monsters can easily utilize them.

To my surprise, fusion monsters are still high. Possibly this is also the side-effects of HERO monsters. Remember, HERO Archetype are fusion-oriented.

The Synchro monsters are still high probably because Synchro based archetype are also very common. During the 5D’s era, KONAMI drops a bombardment of new Archetypes that are synchro related. And recently, I heard recently KONAMI drops another support for Stardust Dragon (Yusei Fudo’s main monster), which means that Synchro oriented decks aren’t going down soon.

And finally, Link vs Pendulum, I see that people actually prefers Link monsters! Glad to hear that, I always find the ruling of Pendulum monsters quite confusing. But there’s also this second reason…

Link monsters are a MUST if your deck relies on Extra Decks (either Xyz, Synchro, and Fusion monsters). This is because if you don’t use Link monsters as per the latest Master Rule, you can only have 1 monster that are summoned from Extra Deck. Read more about this rule

This is probably why KONAMI decides to drop a bombardment of Link monsters, despite Pendulum monsters came first.

4.1.3 Monster Attribute

Now, let’s take a look at the distribution of the Attributes itself.

ggplot(df_final) +
  labs(title="Monster Attributes Distribution") +
  geom_bar(aes(Attribute, fill=Attribute))

As we can see, the number of the Effect monsters are ranked as follows:

  1. DARK
  2. EARTH
  3. LIGHT

There’s only 1 possibility why DARK & LIGHT are at the top 3. It’s because “Chaos” deck playstyle exist. This so-called “Chaos” deck are decks that relies on various DARK & LIGHT monsters, often coming from different Archetype / Type! And what’s more amazing are, the “Chaos” deck is considered to be very versatile, any player with decks that can utilize DARK & LIGHT Monsters, have “Chaos” playstyle as one of their options!

This is why KONAMI did not stop creating supports for DARK & LIGHT monsters.

4.1.4 Monsters Level vs Banlist Relation

theme_set(theme_bw())

level_banlist_density <- ggplot(df_final) +
  geom_density(aes(Level.Rank, fill=Banlist, alpha=0.2)) +
  labs(title="Monsters Density by Banlist")

level_banlist_density

The reason why I visualize the Level / Rank and Banlist together is, because this particular rule: “Monsters of level 5 or higher requires tributing (sacrificing) a monster”.

Think of it, monsters are mostly banned because their effects can easily change the tide of the duel. How scary it is to summon those monsters without tribute?

Look at the density of monsters ranging from Level 2-4. Monsters labeled as Forbidden & Limited are obviously gathering there. This means that the most “favourable” monsters (favourable in YGO can be translated as = soon to be banned LMAO), are those that Can be summoned easily and because they are in the banlist, this means they also have good effects.

Here are some notable monsters on the banlist:

  • Yata-Garasu - L1 (famous for deadlock during the first era of YGO 2004)
  • Dandelion - L3 (easy token abuse)
  • Glow-up Bulb - L1 (this 1 monster can create a deadly combo of 5 high level monsters in 1 turn)

4.2 Conclusion

  • The META is still centered around these types:
    1. Warrior-type
    2. Machine-type
    3. Spellcaster / Dragon
  • The META is still centered around these Attributes:
    1. DARK
    2. EARTH
    3. LIGHT
  • The META is still actively controlled by banlist & level 2-4 monsters, as seen on the monster level density.
  • Pendulum monsters are the least favourable playstyle because of the new Master Rule