Interactive Data Visualization with Altair
18 Jan 2021Through this post, we’ll explore data using altair
, create simple
dashboards, and deploy them for others to use. This was heavily inspired by
Jake VanderPlas’s workshop at PyCon
2018. If this post piques
your interest, definitely check that video out for a longer more in depth
version.
This article provides a summary of the live workshop, which you can watch here. Here is a colab link to run all the code.
We’ll start by importing the libraries we need for now: altair
and pandas
.
pandas
is a data analysis library in python which provides a DataFrame
to
represent tabular data. Don’t worry if you aren’t familiar with it–we’ll cover
what you need as we go.
import altair as alt
import pandas as pd
We’ll use a Pokemon dataset to demonstrate the library. This dataset contains data about Pokemon stats, moves, and competitive tiers from Gen VI. Don’t worry if you don’t understand Pokemon for this workshop!
csv_url = 'https://raw.githubusercontent.com/n2cholas/dsc-workshops/master/Intro_to_Interactive_Data_Viz_with_Altair/pokemon-data-cleaned.csv'
# Turn off na_filter so blanks aren't read as NA
df = pd.read_csv(csv_url, na_filter=False)
Preliminaries
This post was created in an environment called Google Colab, which is a Jupyter notebook hosted by Google. It runs in your browser, and you optionally have access to hardware accelerators like GPUs or TPUs.
A Jupyter Notebook is a web-based application that allows you to create documents of live code, visualizations, equations, and markdown text. Its interactive nature makes it great for data analysis. Before moving on, here are some useful tricks:
?pd.DataFrame # gives you the function/class signature with the description
??pd.DataFrame # two question marks gives you the actual code for that function
Commands prefaced by “%” or “%%” are called magic commands. You can read about more here.
Preprocessing
We’ll start by processing the data a bit to make it easier to work with. Don’t
worry about these steps for this workshop. If you’re curious, check out this
blog post on using
pandas
to clean up this dataset.
On a high level, we are consolidating one of the categorical features so that it is easier to visualize.
# Narrow scope of data
df.loc[df['Tier'] == 'OUBL','Tier'] = 'Uber'
df.loc[df['Tier'] == 'UUBL','Tier'] = 'OU'
df.loc[df['Tier'] == 'RUBL','Tier'] = 'UU'
df.loc[df['Tier'] == 'NUBL','Tier'] = 'RU'
df.loc[df['Tier'] == 'PUBL','Tier'] = 'NU'
df = df[df['Tier'].isin(['Uber', 'OU', 'UU', 'NU', 'RU', 'PU'])]
Visualization
Before visualizing data, make sure your data is in a standard format:
df.sample()
Name | Tier | Num Types | Type 1 | Type 2 | Num Abilities | Ability 1 | Ability 2 | Ability 3 | Has Negative Ability | HP | Attack | Defense | Special Attack | Special Defense | Speed | Base Stat Total | Next Evolution(s) | Evolutionary Stage | Num Evolutionary Stages | Evolutionary Progress | Is Mega Evolution | Is Alternate Form | Num Moves | Moves | Defensive Boost Moves | Offensive Boost Moves | Max Defensive Boost Amount | Max Offensive Boost Amount | Recovery Moves | Priority STAB Attacks | Entry Hazards | Hazard Clearing Moves | Phazing Moves | Switch Attacks | High Prob Side FX Attacks | Constant Damage Attacks | Trapping Moves | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
84 | Bruxish | RU | 2 | Psychic | Water | 3 | Dazzling | Strong Jaw | Wonder Skin | 0 | 68 | 105 | 70 | 70 | 70 | 92 | 475 | [] | 1 | 1 | 1.0 | 0 | 0 | 54 | {'Toxic', 'Light Screen', 'Blizzard', 'Screech... | {'Bulk Up', 'Calm Mind'} | {'Bulk Up', 'Swords Dance', 'Calm Mind'} | 1 | 2 | set() | {'Aqua Jet'} | set() | set() | set() | set() | set() | set() | set() |
Each row should be an observation (here, a Pokemon) and each column should be a feature (i.e. a property of the observation). Features can be continuous variables, a category, etc.
We’ll remove some columns we don’t need for this workshop to make this webpage more lightweight (you’ll see why this matters later):
df.drop([
'Num Types', 'Type 1', 'Type 2', 'Num Abilities', 'Ability 1',
'Ability 2', 'Ability 3', 'Has Negative Ability', 'HP', 'Attack',
'Defense', 'Special Attack', 'Special Defense', 'Speed',
'Next Evolution(s)', 'Evolutionary Stage',
'Num Evolutionary Stages', 'Evolutionary Progress',
'Is Mega Evolution', 'Is Alternate Form', 'Moves',
'Defensive Boost Moves', 'Offensive Boost Moves',
'Max Defensive Boost Amount', 'Max Offensive Boost Amount',
'Recovery Moves', 'Priority STAB Attacks', 'Entry Hazards',
'Hazard Clearing Moves', 'Phazing Moves', 'Switch Attacks',
'High Prob Side FX Attacks', 'Constant Damage Attacks',
'Trapping Moves'], axis=1, inplace=True)
df.sample()
Name | Tier | Base Stat Total | Num Moves | |
---|---|---|---|---|
193 | Drampa | PU | 485 | 65 |
Through this post, we’ll be working towards creating a plot like this:
multi = alt.selection_multi(fields=['Tier'], empty='all')
interval = alt.selection_interval(encodings=['y'])
scatter = alt.Chart(df).mark_point().encode(
x='Base Stat Total',
y='Num Moves',
color=alt.condition(
multi & interval, 'Tier', alt.value('lightgray'))
).properties(
selection=interval
)
bar = alt.Chart(df).mark_bar().encode(
x='average(Base Stat Total)',
y='Tier',
color=alt.condition(multi, 'Tier', alt.value('lightgray'))
).properties(
selection=multi
).transform_filter(
interval
)
plot = scatter & bar
plot
Above, we have a scatter plot above showing the relationship between the base state total of a Pokemon (an indicator of its strength) and the number of of moves it can learn. Below, we have the average base stat total by the tier. We can create a selection of the scatter plot across the y-interval, which controls which data points are used in the bar plot to show the averages. We can click individual bars (or shift-click multiple bars) to control which points are highlighted in the above plot.
Let’s start with something simpler, which is just the scatter plot without interaction:
alt.Chart(df).mark_point().encode(
x='Base Stat Total',
y='Num Moves',
color='Tier',
tooltip='Name'
).properties(
title='Pokemon Data'
).interactive()
Let’s break down how each line works.
Altair produces plots that follow a specification called Vega Lite, which we’ll cover in more detail later. Essentially, it has the ability to create and verify a schema that describes how a plot should be constructed. This schema is used by a JavaScript front-end to render our plot.
alt.Chart(df)
SchemaValidationError: Invalid specification altair.vegalite.v4.api.Chart, validating 'required' 'mark' is a required property
We get a SchemaValidationError
because we need a type of mark, which
represents our data. As mentioned, an alt.Chart
both creates and verifies the
schema.
In our opening example, we saw mark_point()
, which creates a scatter plot.
There are many others, such as mark_bar()
, mark_tick()
, and more. You can
read about them all here.
alt.Chart(df).mark_point(size=10)
We made a scatter plot! There is a point for every row in the dataset, but you
can’t tell because they’re all stacked on top of eachother. We also adjusted
the size of our points by passing in size=10
to mark_point()
. This is not
useful yet, as it doesn’t portray any information. We need to tell Altair how
to encode these points to get a real scatterplot.
alt.Chart(df).mark_point().encode(
x='Base Stat Total'
)
We’re getting there, are now encoding the points in one dimension. To be
precise, we told altair
to encode Base State Total along the x-axis.
It’s a bit hard to see, we can change the type of mark to see things more clearly:
alt.Chart(df).mark_tick().encode(
x='Base Stat Total'
)
But we digress, we wanted a scatter plot. Let’s encode the Number of moves in y-axis too.
alt.Chart(df).mark_point().encode(
x='Base Stat Total',
y='Num Moves',
)
Let’s work towards making a histogram of Base Stat Total
s, starting with the
scatter plot we already make. If you’re used to other plotting frameworks, this
may seem strange to you–scatterplots and histograms are completely different.
But Altair gives us a declarative visualization grammar to build plots. We can
use basic building blocks to make a wide variety of plots. Don’t worry if that
didn’t make sense–let’s see it in action.
First, let’s count the number of Base Stat Total
that take on each value:
alt.Chart(df).mark_point().encode(
x='Base Stat Total',
y='count()',
)
We used the string count()
to indicatate that we want to count the number of
points. If we didn’t encode Base Stat Total in the x-axis, we would just count
the total number of points, like shown below:
alt.Chart(df).mark_point().encode(
y='count()',
)
Pretty interesting that so many pokemon have a Base Stat Total
of 600, but
this plot is not so useful to understand other trends, since there are too many
values the base stat totals take on. Let’s bin the x-axis:
alt.Chart(df).mark_point().encode(
x=alt.X('Base Stat Total', bin=True),
y='count()',
)
Points are not the standard choice to understand the distribution of this variate, let’s use a bar:
alt.Chart(df).mark_bar().encode(
x=alt.X('Base Stat Total', bin=True),
y='count()',
)
Ta-da, a histogram! You can typically rely on Altair to pick good defaults, but if you want more fine-tuned control, you can always tweak things. For example, let’s increase the number of bins:
alt.Chart(df).mark_bar().encode(
x=alt.X('Base Stat Total', bin=alt.Bin(maxbins=30)),
y='count()',
color='Tier',
)
Nice, we have a pretty handsome histogram! Using Altair’s expressive API, we don’t need to worry about specific calls to make histograms or scatterplots: we just need to remember the basic building plots.
We also added the color to show the distribution by Tier, but this is not so clear. Let’s try to make a heatmap to illustrate this instead:
alt.Chart(df).mark_bar().encode(
x=alt.X('Base Stat Total', bin=alt.Bin(maxbins=30)),
color='count()',
y='Tier',
)
Just by switching y
and color
, we were able to make a heatmap using the
expressive building blocks Altair provides. Notice that Altair chooses an
appropriate colour scale for this continuous variable (whereas before it was
choosing a scale for discrete categories).
But this still isn’t great for understanding distributions, let’s split this into multiple histograms by tier:
alt.Chart(df, width=75).mark_bar().encode(
x=alt.X('Base Stat Total', bin=alt.Bin(maxbins=30)),
y='count()',
color='Tier',
column='Tier',
)
Great! Now we can clearly see the distributions of Base Stat Totals by tier.
Let’s move onto a different sort of plot. What if we’re interested in the average BST by tier?
alt.Chart(df).mark_bar().encode(
x='average(Base Stat Total)',
y='Tier',
color='Tier'
)
'count()'
and 'average()'
are examples of data aggregations. Since we’re taking the average by tiers, we can think of it as a split-apply-combine, illustrated below:
.
We split by some key (x
in the diagram, tier
in our example), apply some aggregation (e.g. average
), then combine the data back together.
In pandas, you would do this as follows:
df.groupby('Tier')['Base Stat Total'].mean()
NU 495.132353 OU 565.896104 PU 464.165919 RU 524.486111 UU 538.181818 Uber 657.042553 Name: Base Stat Total, dtype: float64
Before moving onto interaction, let’s briefly discuss datatypes.
Data Type | Shorthand Code | Description |
---|---|---|
quantitative | Q | a continuous real-valued quantity |
ordinal | O | a discrete ordered quantity |
nominal | N | a discrete unordered category |
temporal | T | a time or date value |
geojson | G | a geographic shape |
These are typically inferred from your pandas DataFrame. When you don’t use a DataFrame, however, you need to specify these. It helps Altair choose the appropriate encoding and scales. For example, if you use a color scale for a quantitative variable, you likely want a smooth gradient. For a nominal variable, you likely want dicrete colours. For a an ordinal variable, you probably want discrete colours that increase in intensity to represent the order. You can read more about them here.
I’d recommend always explicitly specifying them, as shown below.
alt.Chart(csv_url).mark_point().encode(
x='Base Stat Total:Q',
y='Num Moves:Q',
color='Tier:N',
tooltip='Name:N'
)
Since we specified the data types, we were able to consume the CSV straight from the web. Without the data types, we get an error:
alt.Chart(csv_url).mark_point().encode(
x='Base Stat Total',
y='Num Moves',
color='Tier',
tooltip='Name'
)
----------------------------------------------------------------- ValueError Traceback (most recent call last) ... ValueError: Tier encoding field is specified without a type; the type cannot be automatically inferred because the data is not specified as a pandas.DataFrame.
Since we’re using pandas
DataFrames, we will skip out on specifying the data
type since they are inferred.
Interaction
So far, we’ve only seen basic panning and zooming within plots (by adding
.interactive()
), but we can build far more interesting interactions. Let’s
start with an interval selection.
interval = alt.selection_interval()
alt.Chart(df).mark_point().encode(
x='Base Stat Total',
y='Num Moves',
color='Tier'
).properties(
selection=interval
)
Now we can make selection rectangles in our plot. Let’s make it highlight the selected points:
interval = alt.selection_interval()
alt.Chart(df).mark_point().encode(
x='Base Stat Total',
y='Num Moves',
color=alt.condition(interval, 'Tier', alt.value('lightgray'))
).properties(
selection=interval
)
For all the types of selections, you can use alt.condition
to control various
aspects of the plot. The first argument is the predicate (here, the selection
object), the second argument is the value the encoded data assumes when the
condition is true (i.e. when selected), the third is the value they assume when
not selected.
When you make a selection, this gives the JavaScript a signal about what points
are inside vs. outside the selection, which drives actions such as
alt.condition
.
Since the selections act on the data points themselves, so we can tie together data on multiple plots:
interval = alt.selection_interval(encodings=['y'])
scatter = alt.Chart(df).mark_point().encode(
x='Base Stat Total',
y='Num Moves',
color=alt.condition(interval, 'Tier', alt.value('lightgray'))
).properties(
selection=interval
)
bar = alt.Chart(df).mark_bar().encode(
x='average(Base Stat Total)',
y='Tier',
color='Tier'
).transform_filter(
interval
)
scatter & bar # vertically concatenates plots
# scatter | bar # horizontally concatenates plots
We did a bunch of things there, let’s break it down. First, we constrained our
selection_interval
to only operate on the y encoding, so we can’t make
freeform boxes. Next, we created a bar chart like above, with a
transform_filter
to only consider points selected by the interval. To be
clear, the transform_filter
takes in a predicate, which in this case is
whether or not a point is in the selection (represented by the selection
object). Finally, we used scatter & bar
to vertically concatenate both plots
(we could also use alt.voncat(scatter, bar)
.
The result is a plot where we can look at the average base stat total by tier where we can filter what data we consider by the number of moves the Pokemon learn.
Let’s try using a multiselect to control what’s shown on the scatterplot with the barplot.
multi = alt.selection_multi(fields=['Tier'])
scatter = alt.Chart(df).mark_point().encode(
x='Base Stat Total',
y='Num Moves',
color=alt.condition(multi, 'Tier', alt.value('lightgray'))
)
bar = alt.Chart(df).mark_bar().encode(
x='average(Base Stat Total)',
y='Tier',
color=alt.condition(multi, 'Tier', alt.value('lightgray'))
).properties(
selection=multi
)
scatter & bar
Now, our selection is on the Tier in the bar plot, and we’re altering the colour of the scatter plot depending on what’s selected. Only the pokemon belonging tot he bars that are selected will be highlighted in our plot. If you hold shift, you can select multiple bars.
Let’s add two-way interaction and finalize our plot:
multi = alt.selection_multi(fields=['Tier'], empty='all')
interval = alt.selection_interval(encodings=['y'])
scatter = alt.Chart(df).mark_point().encode(
x='Base Stat Total',
y='Num Moves',
color=alt.condition(multi & interval, 'Tier', alt.value('lightgray'))
).properties(
selection=interval
)
bar = alt.Chart(df).mark_bar().encode(
x='average(Base Stat Total)',
y='Tier',
color=alt.condition(multi, 'Tier', alt.value('lightgray'))
).properties(
selection=multi
).transform_filter(
interval
)
plot = scatter & bar
plot
Now, we combined the multi-selection and the interval selection in our plots.
We only look a look at one case of using interactions. Read more about the types of selections, selection defaults, how to trigger those selections, and what you can do with those selections here.
Deploying Your Visualization
Altair creates a Vega Lite specification. Let’s take a small subset of our data to understand exactly what that is:
small_df = df.sample(2)
small_df
Name | Tier | Base Stat Total | Num Moves | |
---|---|---|---|---|
600 | Plusle | PU | 405 | 76 |
71 | Blaziken-Mega | Uber | 630 | 107 |
We take a random sample of two points in our dataset.
multi = alt.selection_multi(fields=['Tier'], empty='all')
interval = alt.selection_interval(encodings=['y'])
scatter = alt.Chart(small_df).mark_point().encode(
x='Base Stat Total',
y='Num Moves',
color=alt.condition(multi & interval, 'Tier', alt.value('lightgray'))
).properties(
selection=interval
)
print(scatter.to_json())
{ "$schema": "https://vega.github.io/schema/vega-lite/v4.8.1.json", "config": { "view": { "continuousHeight": 300, "continuousWidth": 400 } }, "data": { "name": "data-5ff54935db2d127b1e7ec247ebc34012" }, "datasets": { "data-5ff54935db2d127b1e7ec247ebc34012": [ { "Base Stat Total": 405, "Name": "Plusle", "Num Moves": 76, "Tier": "PU" }, { "Base Stat Total": 630, "Name": "Blaziken-Mega", "Num Moves": 107, "Tier": "Uber" } ] }, "encoding": { "color": { "condition": { "field": "Tier", "selection": { "and": [ "selector010", "selector011" ] }, "type": "nominal" }, "value": "lightgray" }, "x": { "field": "Base Stat Total", "type": "quantitative" }, "y": { "field": "Num Moves", "type": "quantitative" } }, "mark": "point", "selection": { "selector011": { "encodings": [ "y" ], "type": "interval" } } }
Each plot has a JSON representation containing information about the plot as
well as every data point. For this reason, altair
doesn’t work well with huge
dataasets–the package recommends using datasets with under 5000 points. There
are ways to visualize larger datasets using altair
, described in the
docs.
The Vega Lite specification is becoming a standard on the web (e.g. you can upload a Vega Lite JSON to Wikipedia for interactive plots!). This Vega Lite specification is converted to a more complex Vega representation, which is then converted to D3.js (JavaScript).
This makes Altair plots easy to embed in web-pages independent of python.
print(plot.to_html())
<!DOCTYPE html> <html> <head> <style> .error { color: red; } </style> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm//vega@5"> </script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm//vega-lite@4.8.1"> </script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm//vega-embed@6"> </script> </head> <body> <div id="vis"> </div> <script> (function(vegaEmbed) { var spec = {"config": {"view": {"continuousWidth": 400, "continuousHeight": 300}}, "vconcat": [{"mark": "point", "encoding": {"color": {"condition": {"type": "nominal", "field": "Tier", "selection": {"and": ["selector008", "selector009"]}}, "value": "lightgray"}, "x": {"type": "quantitative", "field": "Base Stat Total"}, "y": {"type": "quantitative", "field": "Num Moves"}}, "selection": {"selector009": {"type": "interval", "encodings": ["y"]}}}, {"mark": "bar", "encoding": {"color": {"condition": {"type": "nominal", "field": "Tier", "selection": "selector008"}, "value": "lightgray"}, "x": {"type": "quantitative", "aggregate": "average", "field": "Base Stat Total"}, "y": {"type": "nominal", "field": "Tier"}}, "selection": {"selector008": {"type": "multi", "fields": ["Tier"], "empty": "all"}}, "transform": [{"filter": {"selection": "selector009"}}]}], "data": {"name": "data-4ba12c371029303f29772324f5d63a54"}, "$schema": "https://vega.github.io/schema/vega-lite/v4.8.1.json", "datasets": {"data-4ba12c371029303f29772324f5d63a54": [{"Name": "Abomasnow", "Tier": "PU", "Base Stat Total": 494, "Num Moves": 75}, {"Name": "Abomasnow-Mega", "Tier": "RU", "Base Stat Total": 594, "Num Moves": 75}, {"Name": "Absol", "Tier": "PU", "Base Stat Total": 465, "Num Moves": 107}, {"Name": "Absol-Mega", "Tier": "UU", "Base Stat Total": 565, "Num Moves": 107}, {"Name": "Accelgor", "Tier": "NU", "Base Stat Total": 495, "Num Moves": 63}, {"Name": "Aegislash", "Tier": "Uber", "Base Stat Total": 520, "Num Moves": 55}, {"Name": "Aegislash-Blade", "Tier": "Uber", "Base Stat Total": 520, "Num Moves": 55}, {"Name": "Aerodactyl", "Tier": "RU", "Base Stat Total": 515, "Num Moves": 90}, {"Name": "Aerodactyl-Mega", "Tier": "UU", "Base Stat Total": 615, "Num Moves": 90}, {"Name": "Aggron", "Tier": "PU", "Base Stat Total": 530, "Num Moves": 115}, {"Name": "Aggron-Mega", "Tier": "UU", "Base Stat Total": 630, "Num Moves": 115}, {"Name": "Alakazam", "Tier": "OU", "Base Stat Total": 500, "Num Moves": 104}, {"Name": "Alakazam-Mega", "Tier": "OU", "Base Stat Total": 600, "Num Moves": 104}, {"Name": "Alomomola", "Tier": "UU", "Base Stat Total": 470, "Num Moves": 57}, {"Name": "Altaria", "Tier": "NU", "Base Stat Total": 490, "Num Moves": 87}, {"Name": "Altaria-Mega", "Tier": "UU", "Base Stat Total": 590, "Num Moves": 87}, {"Name": "Ambipom", "Tier": "NU", "Base Stat Total": 482, "Num Moves": 93}, {"Name": "Amoonguss", "Tier": "OU", "Base Stat Total": 464, "Num Moves": 52}, {"Name": "Ampharos", "Tier": "PU", "Base Stat Total": 510, "Num Moves": 87}, {"Name": "Ampharos-Mega", "Tier": "RU", "Base Stat Total": 610, "Num Moves": 87}, {"Name": "Araquanid", "Tier": "RU", "Base Stat Total": 454, "Num Moves": 46}, {"Name": "Arbok", "Tier": "PU", "Base Stat Total": 448, "Num Moves": 88}, {"Name": "Arcanine", "Tier": "UU", "Base Stat Total": 555, "Num Moves": 83}, {"Name": "Arceus", "Tier": "Uber", "Base Stat Total": 720, "Num Moves": 126}, {"Name": "Archeops", "Tier": "NU", "Base Stat Total": 567, "Num Moves": 75}, {"Name": "Ariados", "Tier": "PU", "Base Stat Total": 400, "Num Moves": 77}, {"Name": "Armaldo", "Tier": "PU", "Base Stat Total": 495, "Num Moves": 78}, {"Name": "Aromatisse", "Tier": "NU", "Base Stat Total": 462, "Num Moves": 64}, {"Name": "Articuno", "Tier": "PU", "Base Stat Total": 580, "Num Moves": 71}, {"Name": "Audino", "Tier": "PU", "Base Stat Total": 445, "Num Moves": 97}, {"Name": "Audino-Mega", "Tier": "NU", "Base Stat Total": 545, "Num Moves": 97}, {"Name": "Aurorus", "Tier": "PU", "Base Stat Total": 521, "Num Moves": 75}, {"Name": "Azelf", "Tier": "UU", "Base Stat Total": 580, "Num Moves": 81}, {"Name": "Azumarill", "Tier": "OU", "Base Stat Total": 420, "Num Moves": 96}, {"Name": "Banette", "Tier": "PU", "Base Stat Total": 455, "Num Moves": 83}, {"Name": "Banette-Mega", "Tier": "RU", "Base Stat Total": 555, "Num Moves": 83}, {"Name": "Barbaracle", "Tier": "RU", "Base Stat Total": 500, "Num Moves": 88}, {"Name": "Basculin", "Tier": "PU", "Base Stat Total": 460, "Num Moves": 57}, {"Name": "Basculin-Blue", "Tier": "PU", "Base Stat Total": 460, "Num Moves": 57}, {"Name": "Basculin-Red", "Tier": "PU", "Base Stat Total": 514, "Num Moves": 64}, {"Name": "Bastiodon", "Tier": "PU", "Base Stat Total": 495, "Num Moves": 75}, {"Name": "Beartic", "Tier": "PU", "Base Stat Total": 505, "Num Moves": 73}, {"Name": "Beautifly", "Tier": "PU", "Base Stat Total": 395, "Num Moves": 63}, {"Name": "Beedrill", "Tier": "PU", "Base Stat Total": 395, "Num Moves": 78}, {"Name": "Beedrill-Mega", "Tier": "UU", "Base Stat Total": 495, "Num Moves": 78}, {"Name": "Beheeyem", "Tier": "PU", "Base Stat Total": 485, "Num Moves": 78}, {"Name": "Bellossom", "Tier": "PU", "Base Stat Total": 490, "Num Moves": 72}, {"Name": "Bewear", "Tier": "RU", "Base Stat Total": 500, "Num Moves": 53}, {"Name": "Bibarel", "Tier": "PU", "Base Stat Total": 410, "Num Moves": 85}, {"Name": "Bisharp", "Tier": "OU", "Base Stat Total": 490, "Num Moves": 78}, {"Name": "Blacephalon", "Tier": "OU", "Base Stat Total": 570, "Num Moves": 43}, {"Name": "Blastoise", "Tier": "NU", "Base Stat Total": 530, "Num Moves": 108}, {"Name": "Blastoise-Mega", "Tier": "RU", "Base Stat Total": 630, "Num Moves": 108}, {"Name": "Blaziken", "Tier": "Uber", "Base Stat Total": 530, "Num Moves": 107}, {"Name": "Blaziken-Mega", "Tier": "Uber", "Base Stat Total": 630, "Num Moves": 107}, {"Name": "Blissey", "Tier": "UU", "Base Stat Total": 540, "Num Moves": 118}, {"Name": "Bouffalant", "Tier": "PU", "Base Stat Total": 490, "Num Moves": 64}, {"Name": "Braviary", "Tier": "NU", "Base Stat Total": 510, "Num Moves": 54}, {"Name": "Breloom", "Tier": "UU", "Base Stat Total": 460, "Num Moves": 85}, {"Name": "Bronzong", "Tier": "RU", "Base Stat Total": 500, "Num Moves": 73}, {"Name": "Bruxish", "Tier": "RU", "Base Stat Total": 475, "Num Moves": 54}, {"Name": "Butterfree", "Tier": "PU", "Base Stat Total": 395, "Num Moves": 80}, {"Name": "Buzzwole", "Tier": "OU", "Base Stat Total": 570, "Num Moves": 49}, {"Name": "Cacturne", "Tier": "PU", "Base Stat Total": 475, "Num Moves": 93}, {"Name": "Camerupt", "Tier": "PU", "Base Stat Total": 460, "Num Moves": 80}, {"Name": "Camerupt-Mega", "Tier": "RU", "Base Stat Total": 560, "Num Moves": 80}, {"Name": "Carbink", "Tier": "PU", "Base Stat Total": 500, "Num Moves": 54}, {"Name": "Carnivine", "Tier": "PU", "Base Stat Total": 454, "Num Moves": 67}, {"Name": "Carracosta", "Tier": "PU", "Base Stat Total": 495, "Num Moves": 70}, {"Name": "Castform", "Tier": "PU", "Base Stat Total": 420, "Num Moves": 69}, {"Name": "Celebi", "Tier": "UU", "Base Stat Total": 600, "Num Moves": 88}, {"Name": "Celesteela", "Tier": "OU", "Base Stat Total": 570, "Num Moves": 50}, {"Name": "Chandelure", "Tier": "UU", "Base Stat Total": 520, "Num Moves": 66}, {"Name": "Chansey", "Tier": "OU", "Base Stat Total": 450, "Num Moves": 125}, {"Name": "Charizard", "Tier": "NU", "Base Stat Total": 534, "Num Moves": 115}, {"Name": "Charizard-Mega-X", "Tier": "OU", "Base Stat Total": 634, "Num Moves": 115}, {"Name": "Charizard-Mega-Y", "Tier": "OU", "Base Stat Total": 634, "Num Moves": 115}, {"Name": "Chatot", "Tier": "PU", "Base Stat Total": 411, "Num Moves": 59}, {"Name": "Cherrim", "Tier": "PU", "Base Stat Total": 450, "Num Moves": 57}, {"Name": "Chesnaught", "Tier": "RU", "Base Stat Total": 530, "Num Moves": 95}, {"Name": "Chimecho", "Tier": "PU", "Base Stat Total": 455, "Num Moves": 86}, {"Name": "Cinccino", "Tier": "NU", "Base Stat Total": 470, "Num Moves": 68}, {"Name": "Clawitzer", "Tier": "NU", "Base Stat Total": 500, "Num Moves": 57}, {"Name": "Claydol", "Tier": "NU", "Base Stat Total": 500, "Num Moves": 81}, {"Name": "Clefable", "Tier": "OU", "Base Stat Total": 483, "Num Moves": 145}, {"Name": "Clefairy", "Tier": "PU", "Base Stat Total": 323, "Num Moves": 142}, {"Name": "Cloyster", "Tier": "RU", "Base Stat Total": 525, "Num Moves": 75}, {"Name": "Cobalion", "Tier": "UU", "Base Stat Total": 580, "Num Moves": 59}, {"Name": "Cofagrigus", "Tier": "RU", "Base Stat Total": 483, "Num Moves": 73}, {"Name": "Comfey", "Tier": "NU", "Base Stat Total": 485, "Num Moves": 54}, {"Name": "Conkeldurr", "Tier": "OU", "Base Stat Total": 505, "Num Moves": 72}, {"Name": "Corsola", "Tier": "PU", "Base Stat Total": 410, "Num Moves": 88}, {"Name": "Crabominable", "Tier": "PU", "Base Stat Total": 478, "Num Moves": 54}, {"Name": "Cradily", "Tier": "PU", "Base Stat Total": 495, "Num Moves": 76}, {"Name": "Crawdaunt", "Tier": "UU", "Base Stat Total": 468, "Num Moves": 85}, {"Name": "Cresselia", "Tier": "RU", "Base Stat Total": 600, "Num Moves": 64}, {"Name": "Crobat", "Tier": "UU", "Base Stat Total": 535, "Num Moves": 79}, {"Name": "Crustle", "Tier": "PU", "Base Stat Total": 485, "Num Moves": 61}, {"Name": "Cryogonal", "Tier": "NU", "Base Stat Total": 515, "Num Moves": 51}, {"Name": "Darkrai", "Tier": "Uber", "Base Stat Total": 600, "Num Moves": 85}, {"Name": "Darmanitan", "Tier": "UU", "Base Stat Total": 480, "Num Moves": 74}, {"Name": "Darmanitan-Zen", "Tier": "UU", "Base Stat Total": 540, "Num Moves": 74}, {"Name": "Decidueye", "Tier": "RU", "Base Stat Total": 530, "Num Moves": 61}, {"Name": "Dedenne", "Tier": "PU", "Base Stat Total": 431, "Num Moves": 58}, {"Name": "Delcatty", "Tier": "PU", "Base Stat Total": 400, "Num Moves": 86}, {"Name": "Delibird", "Tier": "PU", "Base Stat Total": 330, "Num Moves": 72}, {"Name": "Delphox", "Tier": "NU", "Base Stat Total": 534, "Num Moves": 77}, {"Name": "Deoxys", "Tier": "Uber", "Base Stat Total": 600, "Num Moves": 99}, {"Name": "Deoxys-Attack", "Tier": "Uber", "Base Stat Total": 600, "Num Moves": 96}, {"Name": "Deoxys-Defense", "Tier": "Uber", "Base Stat Total": 600, "Num Moves": 98}, {"Name": "Deoxys-Speed", "Tier": "Uber", "Base Stat Total": 600, "Num Moves": 102}, {"Name": "Dewgong", "Tier": "PU", "Base Stat Total": 475, "Num Moves": 77}, {"Name": "Dhelmise", "Tier": "RU", "Base Stat Total": 517, "Num Moves": 52}, {"Name": "Dialga", "Tier": "Uber", "Base Stat Total": 680, "Num Moves": 80}, {"Name": "Diancie", "Tier": "NU", "Base Stat Total": 600, "Num Moves": 61}, {"Name": "Diancie-Mega", "Tier": "OU", "Base Stat Total": 700, "Num Moves": 61}, {"Name": "Diggersby", "Tier": "OU", "Base Stat Total": 423, "Num Moves": 81}, {"Name": "Ditto", "Tier": "PU", "Base Stat Total": 288, "Num Moves": 1}, {"Name": "Dodrio", "Tier": "NU", "Base Stat Total": 470, "Num Moves": 70}, {"Name": "Donphan", "Tier": "RU", "Base Stat Total": 500, "Num Moves": 82}, {"Name": "Doublade", "Tier": "RU", "Base Stat Total": 448, "Num Moves": 47}, {"Name": "Dragalge", "Tier": "RU", "Base Stat Total": 494, "Num Moves": 58}, {"Name": "Dragonite", "Tier": "OU", "Base Stat Total": 600, "Num Moves": 117}, {"Name": "Drampa", "Tier": "PU", "Base Stat Total": 485, "Num Moves": 65}, {"Name": "Drapion", "Tier": "RU", "Base Stat Total": 500, "Num Moves": 87}, {"Name": "Drifblim", "Tier": "PU", "Base Stat Total": 498, "Num Moves": 82}, {"Name": "Druddigon", "Tier": "NU", "Base Stat Total": 485, "Num Moves": 84}, {"Name": "Dugtrio", "Tier": "PU", "Base Stat Total": 425, "Num Moves": 76}, {"Name": "Dugtrio-Alola", "Tier": "PU", "Base Stat Total": 425, "Num Moves": 60}, {"Name": "Dunsparce", "Tier": "PU", "Base Stat Total": 415, "Num Moves": 89}, {"Name": "Durant", "Tier": "UU", "Base Stat Total": 484, "Num Moves": 56}, {"Name": "Dusknoir", "Tier": "PU", "Base Stat Total": 525, "Num Moves": 88}, {"Name": "Dustox", "Tier": "PU", "Base Stat Total": 385, "Num Moves": 63}, {"Name": "Eelektross", "Tier": "PU", "Base Stat Total": 515, "Num Moves": 73}, {"Name": "Electivire", "Tier": "PU", "Base Stat Total": 540, "Num Moves": 81}, {"Name": "Electrode", "Tier": "PU", "Base Stat Total": 490, "Num Moves": 62}, {"Name": "Emboar", "Tier": "RU", "Base Stat Total": 528, "Num Moves": 87}, {"Name": "Emolga", "Tier": "PU", "Base Stat Total": 428, "Num Moves": 59}, {"Name": "Empoleon", "Tier": "UU", "Base Stat Total": 530, "Num Moves": 91}, {"Name": "Entei", "Tier": "RU", "Base Stat Total": 580, "Num Moves": 65}, {"Name": "Escavalier", "Tier": "RU", "Base Stat Total": 495, "Num Moves": 61}, {"Name": "Espeon", "Tier": "RU", "Base Stat Total": 525, "Num Moves": 88}, {"Name": "Excadrill", "Tier": "OU", "Base Stat Total": 508, "Num Moves": 60}, {"Name": "Exeggutor", "Tier": "PU", "Base Stat Total": 530, "Num Moves": 84}, {"Name": "Exeggutor-Alola", "Tier": "PU", "Base Stat Total": 530, "Num Moves": 69}, {"Name": "Exploud", "Tier": "RU", "Base Stat Total": 490, "Num Moves": 99}, {"Name": "Farfetch'd", "Tier": "PU", "Base Stat Total": 377, "Num Moves": 87}, {"Name": "Fearow", "Tier": "PU", "Base Stat Total": 442, "Num Moves": 69}, {"Name": "Feraligatr", "Tier": "RU", "Base Stat Total": 530, "Num Moves": 104}, {"Name": "Ferroseed", "Tier": "PU", "Base Stat Total": 305, "Num Moves": 54}, {"Name": "Ferrothorn", "Tier": "OU", "Base Stat Total": 489, "Num Moves": 67}, {"Name": "Flareon", "Tier": "PU", "Base Stat Total": 525, "Num Moves": 86}, {"Name": "Floatzel", "Tier": "PU", "Base Stat Total": 495, "Num Moves": 79}, {"Name": "Florges", "Tier": "RU", "Base Stat Total": 552, "Num Moves": 61}, {"Name": "Flygon", "Tier": "RU", "Base Stat Total": 520, "Num Moves": 93}, {"Name": "Forretress", "Tier": "RU", "Base Stat Total": 465, "Num Moves": 78}, {"Name": "Froslass", "Tier": "NU", "Base Stat Total": 480, "Num Moves": 78}, {"Name": "Furfrou", "Tier": "PU", "Base Stat Total": 472, "Num Moves": 57}, {"Name": "Furret", "Tier": "PU", "Base Stat Total": 415, "Num Moves": 98}, {"Name": "Gallade", "Tier": "NU", "Base Stat Total": 518, "Num Moves": 126}, {"Name": "Gallade-Mega", "Tier": "OU", "Base Stat Total": 618, "Num Moves": 126}, {"Name": "Galvantula", "Tier": "RU", "Base Stat Total": 472, "Num Moves": 64}, {"Name": "Garbodor", "Tier": "NU", "Base Stat Total": 474, "Num Moves": 64}, {"Name": "Garchomp", "Tier": "OU", "Base Stat Total": 600, "Num Moves": 81}, {"Name": "Garchomp-Mega", "Tier": "OU", "Base Stat Total": 700, "Num Moves": 81}, {"Name": "Gardevoir", "Tier": "RU", "Base Stat Total": 518, "Num Moves": 101}, {"Name": "Gardevoir-Mega", "Tier": "OU", "Base Stat Total": 618, "Num Moves": 101}, {"Name": "Gastrodon", "Tier": "PU", "Base Stat Total": 475, "Num Moves": 74}, {"Name": "Genesect", "Tier": "Uber", "Base Stat Total": 600, "Num Moves": 72}, {"Name": "Gengar", "Tier": "UU", "Base Stat Total": 500, "Num Moves": 109}, {"Name": "Gengar-Mega", "Tier": "Uber", "Base Stat Total": 600, "Num Moves": 109}, {"Name": "Gigalith", "Tier": "NU", "Base Stat Total": 515, "Num Moves": 53}, {"Name": "Girafarig", "Tier": "PU", "Base Stat Total": 455, "Num Moves": 96}, {"Name": "Giratina", "Tier": "Uber", "Base Stat Total": 680, "Num Moves": 85}, {"Name": "Giratina-Origin", "Tier": "Uber", "Base Stat Total": 680, "Num Moves": 86}, {"Name": "Glaceon", "Tier": "PU", "Base Stat Total": 525, "Num Moves": 76}, {"Name": "Glalie", "Tier": "PU", "Base Stat Total": 480, "Num Moves": 68}, {"Name": "Glalie-Mega", "Tier": "RU", "Base Stat Total": 580, "Num Moves": 68}, {"Name": "Gligar", "Tier": "RU", "Base Stat Total": 430, "Num Moves": 90}, {"Name": "Gliscor", "Tier": "UU", "Base Stat Total": 510, "Num Moves": 94}, {"Name": "Gogoat", "Tier": "PU", "Base Stat Total": 531, "Num Moves": 61}, {"Name": "Golbat", "Tier": "NU", "Base Stat Total": 455, "Num Moves": 80}, {"Name": "Golduck", "Tier": "PU", "Base Stat Total": 500, "Num Moves": 108}, {"Name": "Golem", "Tier": "PU", "Base Stat Total": 495, "Num Moves": 97}, {"Name": "Golem-Alola", "Tier": "PU", "Base Stat Total": 495, "Num Moves": 67}, {"Name": "Golisopod", "Tier": "RU", "Base Stat Total": 530, "Num Moves": 62}, {"Name": "Golurk", "Tier": "PU", "Base Stat Total": 483, "Num Moves": 78}, {"Name": "Goodra", "Tier": "RU", "Base Stat Total": 600, "Num Moves": 65}, {"Name": "Gorebyss", "Tier": "PU", "Base Stat Total": 485, "Num Moves": 66}, {"Name": "Gothitelle", "Tier": "PU", "Base Stat Total": 490, "Num Moves": 81}, {"Name": "Gourgeist", "Tier": "PU", "Base Stat Total": 494, "Num Moves": 67}, {"Name": "Gourgeist-Large", "Tier": "PU", "Base Stat Total": 494, "Num Moves": 67}, {"Name": "Gourgeist-Small", "Tier": "PU", "Base Stat Total": 494, "Num Moves": 67}, {"Name": "Gourgeist-Super", "Tier": "PU", "Base Stat Total": 494, "Num Moves": 67}, {"Name": "Granbull", "Tier": "PU", "Base Stat Total": 450, "Num Moves": 107}, {"Name": "Greninja", "Tier": "OU", "Base Stat Total": 530, "Num Moves": 76}, {"Name": "Greninja-Ash", "Tier": "OU", "Base Stat Total": 640, "Num Moves": 63}, {"Name": "Groudon", "Tier": "Uber", "Base Stat Total": 670, "Num Moves": 89}, {"Name": "Groudon-Primal", "Tier": "Uber", "Base Stat Total": 770, "Num Moves": 89}, {"Name": "Grumpig", "Tier": "PU", "Base Stat Total": 470, "Num Moves": 92}, {"Name": "Gumshoos", "Tier": "PU", "Base Stat Total": 418, "Num Moves": 47}, {"Name": "Gurdurr", "Tier": "PU", "Base Stat Total": 405, "Num Moves": 68}, {"Name": "Guzzlord", "Tier": "NU", "Base Stat Total": 570, "Num Moves": 53}, {"Name": "Gyarados", "Tier": "OU", "Base Stat Total": 540, "Num Moves": 89}, {"Name": "Gyarados-Mega", "Tier": "OU", "Base Stat Total": 640, "Num Moves": 89}, {"Name": "Hariyama", "Tier": "NU", "Base Stat Total": 474, "Num Moves": 91}, {"Name": "Haunter", "Tier": "PU", "Base Stat Total": 405, "Num Moves": 87}, {"Name": "Hawlucha", "Tier": "OU", "Base Stat Total": 502, "Num Moves": 81}, {"Name": "Haxorus", "Tier": "UU", "Base Stat Total": 540, "Num Moves": 72}, {"Name": "Heatmor", "Tier": "PU", "Base Stat Total": 484, "Num Moves": 73}, {"Name": "Heatran", "Tier": "OU", "Base Stat Total": 600, "Num Moves": 65}, {"Name": "Heliolisk", "Tier": "NU", "Base Stat Total": 481, "Num Moves": 66}, {"Name": "Heracross", "Tier": "UU", "Base Stat Total": 500, "Num Moves": 83}, {"Name": "Heracross-Mega", "Tier": "OU", "Base Stat Total": 600, "Num Moves": 83}, {"Name": "Hippowdon", "Tier": "UU", "Base Stat Total": 525, "Num Moves": 60}, {"Name": "Hitmonchan", "Tier": "PU", "Base Stat Total": 455, "Num Moves": 85}, {"Name": "Hitmonlee", "Tier": "NU", "Base Stat Total": 455, "Num Moves": 90}, {"Name": "Hitmontop", "Tier": "NU", "Base Stat Total": 455, "Num Moves": 79}, {"Name": "Ho-Oh", "Tier": "Uber", "Base Stat Total": 680, "Num Moves": 87}, {"Name": "Honchkrow", "Tier": "RU", "Base Stat Total": 505, "Num Moves": 82}, {"Name": "Hoopa", "Tier": "RU", "Base Stat Total": 600, "Num Moves": 80}, {"Name": "Hoopa-Unbound", "Tier": "OU", "Base Stat Total": 680, "Num Moves": 78}, {"Name": "Houndoom", "Tier": "NU", "Base Stat Total": 500, "Num Moves": 83}, {"Name": "Houndoom-Mega", "Tier": "UU", "Base Stat Total": 600, "Num Moves": 83}, {"Name": "Huntail", "Tier": "PU", "Base Stat Total": 485, "Num Moves": 66}, {"Name": "Hydreigon", "Tier": "UU", "Base Stat Total": 600, "Num Moves": 88}, {"Name": "Hypno", "Tier": "PU", "Base Stat Total": 483, "Num Moves": 102}, {"Name": "Illumise", "Tier": "PU", "Base Stat Total": 430, "Num Moves": 83}, {"Name": "Incineroar", "Tier": "NU", "Base Stat Total": 530, "Num Moves": 68}, {"Name": "Infernape", "Tier": "UU", "Base Stat Total": 534, "Num Moves": 102}, {"Name": "Jellicent", "Tier": "NU", "Base Stat Total": 480, "Num Moves": 65}, {"Name": "Jirachi", "Tier": "OU", "Base Stat Total": 600, "Num Moves": 86}, {"Name": "Jolteon", "Tier": "RU", "Base Stat Total": 525, "Num Moves": 87}, {"Name": "Jumpluff", "Tier": "PU", "Base Stat Total": 460, "Num Moves": 70}, {"Name": "Jynx", "Tier": "PU", "Base Stat Total": 455, "Num Moves": 114}, {"Name": "Kabutops", "Tier": "PU", "Base Stat Total": 495, "Num Moves": 98}, {"Name": "Kangaskhan", "Tier": "PU", "Base Stat Total": 490, "Num Moves": 114}, {"Name": "Kangaskhan-Mega", "Tier": "Uber", "Base Stat Total": 590, "Num Moves": 114}, {"Name": "Kartana", "Tier": "OU", "Base Stat Total": 570, "Num Moves": 35}, {"Name": "Kecleon", "Tier": "PU", "Base Stat Total": 440, "Num Moves": 110}, {"Name": "Keldeo", "Tier": "OU", "Base Stat Total": 580, "Num Moves": 58}, {"Name": "Kingdra", "Tier": "RU", "Base Stat Total": 540, "Num Moves": 68}, {"Name": "Kingler", "Tier": "PU", "Base Stat Total": 475, "Num Moves": 80}, {"Name": "Klefki", "Tier": "UU", "Base Stat Total": 470, "Num Moves": 55}, {"Name": "Klinklang", "Tier": "NU", "Base Stat Total": 520, "Num Moves": 51}, {"Name": "Komala", "Tier": "PU", "Base Stat Total": 480, "Num Moves": 45}, {"Name": "Kommo-o", "Tier": "UU", "Base Stat Total": 600, "Num Moves": 62}, {"Name": "Kricketune", "Tier": "PU", "Base Stat Total": 384, "Num Moves": 61}, {"Name": "Krookodile", "Tier": "UU", "Base Stat Total": 519, "Num Moves": 89}, {"Name": "Kyogre", "Tier": "Uber", "Base Stat Total": 670, "Num Moves": 67}, {"Name": "Kyogre-Primal", "Tier": "Uber", "Base Stat Total": 770, "Num Moves": 67}, {"Name": "Kyurem", "Tier": "UU", "Base Stat Total": 660, "Num Moves": 64}, {"Name": "Kyurem-Black", "Tier": "OU", "Base Stat Total": 700, "Num Moves": 64}, {"Name": "Kyurem-White", "Tier": "Uber", "Base Stat Total": 700, "Num Moves": 64}, {"Name": "Landorus", "Tier": "Uber", "Base Stat Total": 600, "Num Moves": 60}, {"Name": "Landorus-Therian", "Tier": "OU", "Base Stat Total": 600, "Num Moves": 60}, {"Name": "Lanturn", "Tier": "PU", "Base Stat Total": 460, "Num Moves": 76}, {"Name": "Lapras", "Tier": "PU", "Base Stat Total": 535, "Num Moves": 93}, {"Name": "Latias", "Tier": "UU", "Base Stat Total": 600, "Num Moves": 97}, {"Name": "Latias-Mega", "Tier": "OU", "Base Stat Total": 700, "Num Moves": 97}, {"Name": "Latios", "Tier": "OU", "Base Stat Total": 600, "Num Moves": 92}, {"Name": "Latios-Mega", "Tier": "OU", "Base Stat Total": 700, "Num Moves": 92}, {"Name": "Leafeon", "Tier": "PU", "Base Stat Total": 525, "Num Moves": 81}, {"Name": "Leavanny", "Tier": "PU", "Base Stat Total": 500, "Num Moves": 76}, {"Name": "Ledian", "Tier": "PU", "Base Stat Total": 390, "Num Moves": 78}, {"Name": "Lickilicky", "Tier": "PU", "Base Stat Total": 515, "Num Moves": 97}, {"Name": "Liepard", "Tier": "PU", "Base Stat Total": 446, "Num Moves": 71}, {"Name": "Lilligant", "Tier": "PU", "Base Stat Total": 480, "Num Moves": 61}, {"Name": "Linoone", "Tier": "RU", "Base Stat Total": 420, "Num Moves": 89}, {"Name": "Lopunny", "Tier": "PU", "Base Stat Total": 480, "Num Moves": 95}, {"Name": "Lopunny-Mega", "Tier": "OU", "Base Stat Total": 580, "Num Moves": 95}, {"Name": "Lucario", "Tier": "UU", "Base Stat Total": 525, "Num Moves": 103}, {"Name": "Lucario-Mega", "Tier": "Uber", "Base Stat Total": 625, "Num Moves": 103}, {"Name": "Ludicolo", "Tier": "PU", "Base Stat Total": 480, "Num Moves": 91}, {"Name": "Lugia", "Tier": "Uber", "Base Stat Total": 680, "Num Moves": 101}, {"Name": "Lumineon", "Tier": "PU", "Base Stat Total": 460, "Num Moves": 63}, {"Name": "Lunala", "Tier": "Uber", "Base Stat Total": 680, "Num Moves": 59}, {"Name": "Lunatone", "Tier": "PU", "Base Stat Total": 460, "Num Moves": 80}, {"Name": "Lurantis", "Tier": "PU", "Base Stat Total": 480, "Num Moves": 49}, {"Name": "Luvdisc", "Tier": "PU", "Base Stat Total": 330, "Num Moves": 57}, {"Name": "Luxray", "Tier": "PU", "Base Stat Total": 523, "Num Moves": 64}, {"Name": "Lycanroc", "Tier": "PU", "Base Stat Total": 487, "Num Moves": 44}, {"Name": "Lycanroc-Dusk", "Tier": "RU", "Base Stat Total": 487, "Num Moves": 43}, {"Name": "Lycanroc-Midnight", "Tier": "PU", "Base Stat Total": 487, "Num Moves": 43}, {"Name": "Machamp", "Tier": "RU", "Base Stat Total": 505, "Num Moves": 98}, {"Name": "Magcargo", "Tier": "PU", "Base Stat Total": 430, "Num Moves": 79}, {"Name": "Magearna", "Tier": "OU", "Base Stat Total": 600, "Num Moves": 52}, {"Name": "Magmortar", "Tier": "NU", "Base Stat Total": 540, "Num Moves": 80}, {"Name": "Magneton", "Tier": "UU", "Base Stat Total": 465, "Num Moves": 66}, {"Name": "Magnezone", "Tier": "OU", "Base Stat Total": 535, "Num Moves": 63}, {"Name": "Malamar", "Tier": "NU", "Base Stat Total": 482, "Num Moves": 70}, {"Name": "Mamoswine", "Tier": "OU", "Base Stat Total": 530, "Num Moves": 76}, {"Name": "Manaphy", "Tier": "OU", "Base Stat Total": 600, "Num Moves": 65}, {"Name": "Mandibuzz", "Tier": "RU", "Base Stat Total": 510, "Num Moves": 64}, {"Name": "Manectric", "Tier": "PU", "Base Stat Total": 475, "Num Moves": 66}, {"Name": "Manectric-Mega", "Tier": "UU", "Base Stat Total": 575, "Num Moves": 66}, {"Name": "Mantine", "Tier": "UU", "Base Stat Total": 485, "Num Moves": 80}, {"Name": "Maractus", "Tier": "PU", "Base Stat Total": 461, "Num Moves": 57}, {"Name": "Marowak", "Tier": "PU", "Base Stat Total": 425, "Num Moves": 99}, {"Name": "Marowak-Alola", "Tier": "UU", "Base Stat Total": 425, "Num Moves": 74}, {"Name": "Marshadow", "Tier": "Uber", "Base Stat Total": 600, "Num Moves": 56}, {"Name": "Masquerain", "Tier": "PU", "Base Stat Total": 454, "Num Moves": 80}, {"Name": "Mawile", "Tier": "PU", "Base Stat Total": 380, "Num Moves": 101}, {"Name": "Mawile-Mega", "Tier": "OU", "Base Stat Total": 480, "Num Moves": 101}, {"Name": "Medicham", "Tier": "NU", "Base Stat Total": 410, "Num Moves": 96}, {"Name": "Medicham-Mega", "Tier": "OU", "Base Stat Total": 510, "Num Moves": 96}, {"Name": "Meganium", "Tier": "PU", "Base Stat Total": 525, "Num Moves": 79}, {"Name": "Meloetta", "Tier": "RU", "Base Stat Total": 600, "Num Moves": 89}, {"Name": "Meloetta-Pirouette", "Tier": "RU", "Base Stat Total": 600, "Num Moves": 89}, {"Name": "Meowstic-F", "Tier": "PU", "Base Stat Total": 466, "Num Moves": 73}, {"Name": "Meowstic-M", "Tier": "PU", "Base Stat Total": 466, "Num Moves": 74}, {"Name": "Mesprit", "Tier": "PU", "Base Stat Total": 580, "Num Moves": 76}, {"Name": "Metagross", "Tier": "UU", "Base Stat Total": 600, "Num Moves": 80}, {"Name": "Metagross-Mega", "Tier": "Uber", "Base Stat Total": 700, "Num Moves": 80}, {"Name": "Mew", "Tier": "OU", "Base Stat Total": 600, "Num Moves": 246}, {"Name": "Mewtwo", "Tier": "Uber", "Base Stat Total": 680, "Num Moves": 136}, {"Name": "Mewtwo-Mega-X", "Tier": "Uber", "Base Stat Total": 780, "Num Moves": 136}, {"Name": "Mewtwo-Mega-Y", "Tier": "Uber", "Base Stat Total": 780, "Num Moves": 136}, {"Name": "Mienshao", "Tier": "UU", "Base Stat Total": 510, "Num Moves": 73}, {"Name": "Mightyena", "Tier": "PU", "Base Stat Total": 420, "Num Moves": 71}, {"Name": "Milotic", "Tier": "RU", "Base Stat Total": 540, "Num Moves": 72}, {"Name": "Miltank", "Tier": "NU", "Base Stat Total": 490, "Num Moves": 92}, {"Name": "Mimikyu", "Tier": "OU", "Base Stat Total": 476, "Num Moves": 59}, {"Name": "Minior", "Tier": "NU", "Base Stat Total": 500, "Num Moves": 50}, {"Name": "Minior-Meteor", "Tier": "NU", "Base Stat Total": 440, "Num Moves": 50}, {"Name": "Minun", "Tier": "PU", "Base Stat Total": 405, "Num Moves": 77}, {"Name": "Mismagius", "Tier": "NU", "Base Stat Total": 495, "Num Moves": 86}, {"Name": "Moltres", "Tier": "RU", "Base Stat Total": 580, "Num Moves": 68}, {"Name": "Mothim", "Tier": "PU", "Base Stat Total": 424, "Num Moves": 63}, {"Name": "Mr. Mime", "Tier": "PU", "Base Stat Total": 460, "Num Moves": 123}, {"Name": "Mudsdale", "Tier": "PU", "Base Stat Total": 500, "Num Moves": 41}, {"Name": "Muk", "Tier": "PU", "Base Stat Total": 500, "Num Moves": 101}, {"Name": "Muk-Alola", "Tier": "UU", "Base Stat Total": 500, "Num Moves": 76}, {"Name": "Musharna", "Tier": "PU", "Base Stat Total": 487, "Num Moves": 71}, {"Name": "Naganadel", "Tier": "Uber", "Base Stat Total": 540, "Num Moves": 48}, {"Name": "Necrozma", "Tier": "RU", "Base Stat Total": 600, "Num Moves": 61}, {"Name": "Necrozma-Dawn Wings", "Tier": "Uber", "Base Stat Total": 680, "Num Moves": 61}, {"Name": "Necrozma-Dawn Wings-Ultra", "Tier": "Uber", "Base Stat Total": 754, "Num Moves": 61}, {"Name": "Necrozma-Dusk Mane", "Tier": "Uber", "Base Stat Total": 680, "Num Moves": 61}, {"Name": "Necrozma-Dusk Mane-Ultra", "Tier": "Uber", "Base Stat Total": 754, "Num Moves": 61}, {"Name": "Nidoking", "Tier": "UU", "Base Stat Total": 505, "Num Moves": 129}, {"Name": "Nidoqueen", "Tier": "RU", "Base Stat Total": 505, "Num Moves": 128}, {"Name": "Nihilego", "Tier": "UU", "Base Stat Total": 570, "Num Moves": 49}, {"Name": "Ninetales", "Tier": "PU", "Base Stat Total": 505, "Num Moves": 92}, {"Name": "Ninetales-Alola", "Tier": "OU", "Base Stat Total": 505, "Num Moves": 69}, {"Name": "Ninjask", "Tier": "PU", "Base Stat Total": 456, "Num Moves": 69}, {"Name": "Noctowl", "Tier": "PU", "Base Stat Total": 452, "Num Moves": 79}, {"Name": "Noivern", "Tier": "RU", "Base Stat Total": 535, "Num Moves": 72}, {"Name": "Octillery", "Tier": "PU", "Base Stat Total": 480, "Num Moves": 80}, {"Name": "Omastar", "Tier": "NU", "Base Stat Total": 495, "Num Moves": 85}, {"Name": "Oranguru", "Tier": "PU", "Base Stat Total": 490, "Num Moves": 55}, {"Name": "Oricorio", "Tier": "PU", "Base Stat Total": 476, "Num Moves": 45}, {"Name": "Oricorio-Pa'u", "Tier": "PU", "Base Stat Total": 476, "Num Moves": 43}, {"Name": "Oricorio-Pom-Pom", "Tier": "PU", "Base Stat Total": 476, "Num Moves": 43}, {"Name": "Oricorio-Sensu", "Tier": "PU", "Base Stat Total": 476, "Num Moves": 43}, {"Name": "Pachirisu", "Tier": "PU", "Base Stat Total": 405, "Num Moves": 70}, {"Name": "Palkia", "Tier": "Uber", "Base Stat Total": 680, "Num Moves": 83}, {"Name": "Palossand", "Tier": "NU", "Base Stat Total": 480, "Num Moves": 48}, {"Name": "Pangoro", "Tier": "RU", "Base Stat Total": 495, "Num Moves": 100}, {"Name": "Parasect", "Tier": "PU", "Base Stat Total": 405, "Num Moves": 85}, {"Name": "Passimian", "Tier": "NU", "Base Stat Total": 490, "Num Moves": 58}, {"Name": "Pelipper", "Tier": "OU", "Base Stat Total": 440, "Num Moves": 76}, {"Name": "Persian", "Tier": "PU", "Base Stat Total": 440, "Num Moves": 101}, {"Name": "Persian-Alola", "Tier": "PU", "Base Stat Total": 440, "Num Moves": 68}, {"Name": "Pheromosa", "Tier": "Uber", "Base Stat Total": 570, "Num Moves": 47}, {"Name": "Phione", "Tier": "PU", "Base Stat Total": 480, "Num Moves": 53}, {"Name": "Pidgeot", "Tier": "PU", "Base Stat Total": 479, "Num Moves": 65}, {"Name": "Pidgeot-Mega", "Tier": "UU", "Base Stat Total": 579, "Num Moves": 65}, {"Name": "Piloswine", "Tier": "NU", "Base Stat Total": 450, "Num Moves": 73}, {"Name": "Pinsir", "Tier": "PU", "Base Stat Total": 500, "Num Moves": 80}, {"Name": "Pinsir-Mega", "Tier": "OU", "Base Stat Total": 600, "Num Moves": 80}, {"Name": "Plusle", "Tier": "PU", "Base Stat Total": 405, "Num Moves": 76}, {"Name": "Politoed", "Tier": "PU", "Base Stat Total": 500, "Num Moves": 84}, {"Name": "Poliwrath", "Tier": "PU", "Base Stat Total": 510, "Num Moves": 97}, {"Name": "Porygon-Z", "Tier": "OU", "Base Stat Total": 535, "Num Moves": 69}, {"Name": "Porygon2", "Tier": "RU", "Base Stat Total": 515, "Num Moves": 69}, {"Name": "Primarina", "Tier": "UU", "Base Stat Total": 530, "Num Moves": 57}, {"Name": "Primeape", "Tier": "PU", "Base Stat Total": 455, "Num Moves": 114}, {"Name": "Probopass", "Tier": "PU", "Base Stat Total": 525, "Num Moves": 74}, {"Name": "Purugly", "Tier": "PU", "Base Stat Total": 452, "Num Moves": 79}, {"Name": "Pyroar", "Tier": "PU", "Base Stat Total": 507, "Num Moves": 60}, {"Name": "Pyukumuku", "Tier": "PU", "Base Stat Total": 410, "Num Moves": 36}, {"Name": "Quagsire", "Tier": "RU", "Base Stat Total": 430, "Num Moves": 91}, {"Name": "Qwilfish", "Tier": "NU", "Base Stat Total": 440, "Num Moves": 78}, {"Name": "Raichu", "Tier": "PU", "Base Stat Total": 485, "Num Moves": 107}, {"Name": "Raichu-Alola", "Tier": "PU", "Base Stat Total": 485, "Num Moves": 68}, {"Name": "Raikou", "Tier": "UU", "Base Stat Total": 580, "Num Moves": 66}, {"Name": "Rampardos", "Tier": "PU", "Base Stat Total": 495, "Num Moves": 91}, {"Name": "Rapidash", "Tier": "PU", "Base Stat Total": 500, "Num Moves": 71}, {"Name": "Raticate", "Tier": "PU", "Base Stat Total": 413, "Num Moves": 93}, {"Name": "Raticate-Alola", "Tier": "PU", "Base Stat Total": 413, "Num Moves": 67}, {"Name": "Rayquaza", "Tier": "Uber", "Base Stat Total": 680, "Num Moves": 92}, {"Name": "Regice", "Tier": "PU", "Base Stat Total": 580, "Num Moves": 76}, {"Name": "Regigigas", "Tier": "PU", "Base Stat Total": 670, "Num Moves": 69}, {"Name": "Regirock", "Tier": "PU", "Base Stat Total": 580, "Num Moves": 76}, {"Name": "Registeel", "Tier": "RU", "Base Stat Total": 580, "Num Moves": 77}, {"Name": "Relicanth", "Tier": "PU", "Base Stat Total": 485, "Num Moves": 71}, {"Name": "Reshiram", "Tier": "Uber", "Base Stat Total": 680, "Num Moves": 67}, {"Name": "Reuniclus", "Tier": "UU", "Base Stat Total": 490, "Num Moves": 81}, {"Name": "Rhydon", "Tier": "NU", "Base Stat Total": 485, "Num Moves": 125}, {"Name": "Rhyperior", "Tier": "RU", "Base Stat Total": 535, "Num Moves": 110}, {"Name": "Ribombee", "Tier": "RU", "Base Stat Total": 464, "Num Moves": 51}, {"Name": "Roserade", "Tier": "RU", "Base Stat Total": 515, "Num Moves": 73}, {"Name": "Rotom", "Tier": "NU", "Base Stat Total": 440, "Num Moves": 54}, {"Name": "Rotom-Fan", "Tier": "PU", "Base Stat Total": 520, "Num Moves": 55}, {"Name": "Rotom-Frost", "Tier": "PU", "Base Stat Total": 520, "Num Moves": 55}, {"Name": "Rotom-Heat", "Tier": "RU", "Base Stat Total": 520, "Num Moves": 55}, {"Name": "Rotom-Mow", "Tier": "RU", "Base Stat Total": 520, "Num Moves": 55}, {"Name": "Rotom-Wash", "Tier": "OU", "Base Stat Total": 520, "Num Moves": 55}, {"Name": "Sableye", "Tier": "PU", "Base Stat Total": 380, "Num Moves": 108}, {"Name": "Sableye-Mega", "Tier": "OU", "Base Stat Total": 480, "Num Moves": 108}, {"Name": "Salamence", "Tier": "OU", "Base Stat Total": 600, "Num Moves": 83}, {"Name": "Salamence-Mega", "Tier": "Uber", "Base Stat Total": 700, "Num Moves": 83}, {"Name": "Salazzle", "Tier": "RU", "Base Stat Total": 480, "Num Moves": 53}, {"Name": "Samurott", "Tier": "NU", "Base Stat Total": 528, "Num Moves": 75}, {"Name": "Sandslash", "Tier": "PU", "Base Stat Total": 450, "Num Moves": 97}, {"Name": "Sandslash-Alola", "Tier": "PU", "Base Stat Total": 450, "Num Moves": 71}, {"Name": "Sawk", "Tier": "NU", "Base Stat Total": 465, "Num Moves": 61}, {"Name": "Sawsbuck", "Tier": "PU", "Base Stat Total": 475, "Num Moves": 63}, {"Name": "Sceptile", "Tier": "NU", "Base Stat Total": 530, "Num Moves": 104}, {"Name": "Sceptile-Mega", "Tier": "UU", "Base Stat Total": 630, "Num Moves": 104}, {"Name": "Scizor", "Tier": "UU", "Base Stat Total": 500, "Num Moves": 80}, {"Name": "Scizor-Mega", "Tier": "OU", "Base Stat Total": 600, "Num Moves": 80}, {"Name": "Scolipede", "Tier": "OU", "Base Stat Total": 485, "Num Moves": 64}, {"Name": "Scrafty", "Tier": "NU", "Base Stat Total": 488, "Num Moves": 83}, {"Name": "Scyther", "Tier": "NU", "Base Stat Total": 500, "Num Moves": 73}, {"Name": "Seaking", "Tier": "PU", "Base Stat Total": 450, "Num Moves": 69}, {"Name": "Seismitoad", "Tier": "UU", "Base Stat Total": 509, "Num Moves": 71}, {"Name": "Serperior", "Tier": "UU", "Base Stat Total": 528, "Num Moves": 76}, {"Name": "Seviper", "Tier": "PU", "Base Stat Total": 458, "Num Moves": 79}, {"Name": "Sharpedo", "Tier": "UU", "Base Stat Total": 460, "Num Moves": 80}, {"Name": "Sharpedo-Mega", "Tier": "UU", "Base Stat Total": 560, "Num Moves": 80}, {"Name": "Shaymin", "Tier": "RU", "Base Stat Total": 600, "Num Moves": 54}, {"Name": "Shaymin-Sky", "Tier": "Uber", "Base Stat Total": 600, "Num Moves": 53}, {"Name": "Shedinja", "Tier": "PU", "Base Stat Total": 236, "Num Moves": 67}, {"Name": "Shiftry", "Tier": "PU", "Base Stat Total": 480, "Num Moves": 104}, {"Name": "Shiinotic", "Tier": "PU", "Base Stat Total": 405, "Num Moves": 44}, {"Name": "Shuckle", "Tier": "PU", "Base Stat Total": 505, "Num Moves": 74}, {"Name": "Sigilyph", "Tier": "NU", "Base Stat Total": 490, "Num Moves": 72}, {"Name": "Silvally", "Tier": "PU", "Base Stat Total": 570, "Num Moves": 68}, {"Name": "Simipour", "Tier": "PU", "Base Stat Total": 498, "Num Moves": 81}, {"Name": "Simisage", "Tier": "PU", "Base Stat Total": 498, "Num Moves": 79}, {"Name": "Simisear", "Tier": "PU", "Base Stat Total": 498, "Num Moves": 79}, {"Name": "Skarmory", "Tier": "OU", "Base Stat Total": 465, "Num Moves": 78}, {"Name": "Skuntank", "Tier": "PU", "Base Stat Total": 479, "Num Moves": 78}, {"Name": "Slaking", "Tier": "PU", "Base Stat Total": 670, "Num Moves": 100}, {"Name": "Slowbro", "Tier": "NU", "Base Stat Total": 490, "Num Moves": 119}, {"Name": "Slowbro-Mega", "Tier": "UU", "Base Stat Total": 590, "Num Moves": 119}, {"Name": "Slowking", "Tier": "NU", "Base Stat Total": 490, "Num Moves": 111}, {"Name": "Slurpuff", "Tier": "RU", "Base Stat Total": 480, "Num Moves": 56}, {"Name": "Smeargle", "Tier": "PU", "Base Stat Total": 250, "Num Moves": 1}, {"Name": "Sneasel", "Tier": "NU", "Base Stat Total": 430, "Num Moves": 98}, {"Name": "Snorlax", "Tier": "RU", "Base Stat Total": 540, "Num Moves": 118}, {"Name": "Solgaleo", "Tier": "Uber", "Base Stat Total": 680, "Num Moves": 58}, {"Name": "Solrock", "Tier": "PU", "Base Stat Total": 460, "Num Moves": 83}, {"Name": "Spinda", "Tier": "PU", "Base Stat Total": 360, "Num Moves": 99}, {"Name": "Spiritomb", "Tier": "PU", "Base Stat Total": 485, "Num Moves": 68}, {"Name": "Stakataka", "Tier": "UU", "Base Stat Total": 570, "Num Moves": 38}, {"Name": "Stantler", "Tier": "PU", "Base Stat Total": 465, "Num Moves": 86}, {"Name": "Staraptor", "Tier": "OU", "Base Stat Total": 485, "Num Moves": 64}, {"Name": "Starmie", "Tier": "UU", "Base Stat Total": 520, "Num Moves": 89}, {"Name": "Steelix", "Tier": "NU", "Base Stat Total": 510, "Num Moves": 86}, {"Name": "Steelix-Mega", "Tier": "RU", "Base Stat Total": 610, "Num Moves": 86}, {"Name": "Stoutland", "Tier": "PU", "Base Stat Total": 500, "Num Moves": 66}, {"Name": "Stunfisk", "Tier": "PU", "Base Stat Total": 471, "Num Moves": 69}, {"Name": "Sudowoodo", "Tier": "PU", "Base Stat Total": 410, "Num Moves": 82}, {"Name": "Suicune", "Tier": "UU", "Base Stat Total": 580, "Num Moves": 71}, {"Name": "Sunflora", "Tier": "PU", "Base Stat Total": 425, "Num Moves": 62}, {"Name": "Swalot", "Tier": "PU", "Base Stat Total": 467, "Num Moves": 78}, {"Name": "Swampert", "Tier": "UU", "Base Stat Total": 535, "Num Moves": 99}, {"Name": "Swampert-Mega", "Tier": "OU", "Base Stat Total": 635, "Num Moves": 99}, {"Name": "Swanna", "Tier": "PU", "Base Stat Total": 473, "Num Moves": 53}, {"Name": "Swellow", "Tier": "RU", "Base Stat Total": 455, "Num Moves": 63}, {"Name": "Swoobat", "Tier": "PU", "Base Stat Total": 425, "Num Moves": 81}, {"Name": "Sylveon", "Tier": "UU", "Base Stat Total": 525, "Num Moves": 73}, {"Name": "Talonflame", "Tier": "UU", "Base Stat Total": 499, "Num Moves": 54}, {"Name": "Tangrowth", "Tier": "OU", "Base Stat Total": 535, "Num Moves": 82}, {"Name": "Tapu Bulu", "Tier": "OU", "Base Stat Total": 570, "Num Moves": 60}, {"Name": "Tapu Fini", "Tier": "OU", "Base Stat Total": 570, "Num Moves": 52}, {"Name": "Tapu Koko", "Tier": "OU", "Base Stat Total": 570, "Num Moves": 58}, {"Name": "Tapu Lele", "Tier": "OU", "Base Stat Total": 570, "Num Moves": 53}, {"Name": "Tauros", "Tier": "NU", "Base Stat Total": 490, "Num Moves": 79}, {"Name": "Tentacruel", "Tier": "UU", "Base Stat Total": 515, "Num Moves": 82}, {"Name": "Terrakion", "Tier": "UU", "Base Stat Total": 580, "Num Moves": 56}, {"Name": "Throh", "Tier": "PU", "Base Stat Total": 465, "Num Moves": 64}, {"Name": "Thundurus", "Tier": "OU", "Base Stat Total": 580, "Num Moves": 70}, {"Name": "Thundurus-Therian", "Tier": "OU", "Base Stat Total": 580, "Num Moves": 69}, {"Name": "Togedemaru", "Tier": "PU", "Base Stat Total": 435, "Num Moves": 53}, {"Name": "Togekiss", "Tier": "UU", "Base Stat Total": 545, "Num Moves": 107}, {"Name": "Togetic", "Tier": "PU", "Base Stat Total": 405, "Num Moves": 114}, {"Name": "Torkoal", "Tier": "PU", "Base Stat Total": 470, "Num Moves": 73}, {"Name": "Tornadus", "Tier": "UU", "Base Stat Total": 580, "Num Moves": 64}, {"Name": "Tornadus-Therian", "Tier": "OU", "Base Stat Total": 580, "Num Moves": 64}, {"Name": "Torterra", "Tier": "PU", "Base Stat Total": 525, "Num Moves": 84}, {"Name": "Toucannon", "Tier": "PU", "Base Stat Total": 485, "Num Moves": 46}, {"Name": "Toxapex", "Tier": "OU", "Base Stat Total": 495, "Num Moves": 45}, {"Name": "Toxicroak", "Tier": "NU", "Base Stat Total": 490, "Num Moves": 96}, {"Name": "Trevenant", "Tier": "PU", "Base Stat Total": 474, "Num Moves": 76}, {"Name": "Tropius", "Tier": "PU", "Base Stat Total": 460, "Num Moves": 78}, {"Name": "Tsareena", "Tier": "RU", "Base Stat Total": 510, "Num Moves": 53}, {"Name": "Turtonator", "Tier": "PU", "Base Stat Total": 485, "Num Moves": 58}, {"Name": "Type: Null", "Tier": "PU", "Base Stat Total": 534, "Num Moves": 46}, {"Name": "Typhlosion", "Tier": "NU", "Base Stat Total": 534, "Num Moves": 95}, {"Name": "Tyranitar", "Tier": "OU", "Base Stat Total": 600, "Num Moves": 115}, {"Name": "Tyranitar-Mega", "Tier": "OU", "Base Stat Total": 700, "Num Moves": 115}, {"Name": "Tyrantrum", "Tier": "RU", "Base Stat Total": 521, "Num Moves": 66}, {"Name": "Umbreon", "Tier": "RU", "Base Stat Total": 525, "Num Moves": 87}, {"Name": "Unfezant", "Tier": "PU", "Base Stat Total": 488, "Num Moves": 49}, {"Name": "Unown", "Tier": "PU", "Base Stat Total": 336, "Num Moves": 1}, {"Name": "Ursaring", "Tier": "PU", "Base Stat Total": 500, "Num Moves": 105}, {"Name": "Uxie", "Tier": "NU", "Base Stat Total": 580, "Num Moves": 78}, {"Name": "Vanilluxe", "Tier": "NU", "Base Stat Total": 535, "Num Moves": 50}, {"Name": "Vaporeon", "Tier": "NU", "Base Stat Total": 525, "Num Moves": 91}, {"Name": "Venomoth", "Tier": "UU", "Base Stat Total": 450, "Num Moves": 81}, {"Name": "Venusaur", "Tier": "NU", "Base Stat Total": 525, "Num Moves": 88}, {"Name": "Venusaur-Mega", "Tier": "OU", "Base Stat Total": 625, "Num Moves": 88}, {"Name": "Vespiquen", "Tier": "PU", "Base Stat Total": 474, "Num Moves": 65}, {"Name": "Victini", "Tier": "OU", "Base Stat Total": 600, "Num Moves": 79}, {"Name": "Victreebel", "Tier": "PU", "Base Stat Total": 490, "Num Moves": 81}, {"Name": "Vikavolt", "Tier": "NU", "Base Stat Total": 500, "Num Moves": 52}, {"Name": "Vileplume", "Tier": "NU", "Base Stat Total": 490, "Num Moves": 73}, {"Name": "Virizion", "Tier": "RU", "Base Stat Total": 580, "Num Moves": 59}, {"Name": "Vivillon", "Tier": "NU", "Base Stat Total": 411, "Num Moves": 59}, {"Name": "Volbeat", "Tier": "PU", "Base Stat Total": 430, "Num Moves": 79}, {"Name": "Volcanion", "Tier": "UU", "Base Stat Total": 600, "Num Moves": 57}, {"Name": "Volcarona", "Tier": "OU", "Base Stat Total": 550, "Num Moves": 67}, {"Name": "Wailord", "Tier": "PU", "Base Stat Total": 500, "Num Moves": 70}, {"Name": "Walrein", "Tier": "PU", "Base Stat Total": 530, "Num Moves": 74}, {"Name": "Watchog", "Tier": "PU", "Base Stat Total": 420, "Num Moves": 83}, {"Name": "Weavile", "Tier": "OU", "Base Stat Total": 510, "Num Moves": 98}, {"Name": "Weezing", "Tier": "PU", "Base Stat Total": 490, "Num Moves": 73}, {"Name": "Whimsicott", "Tier": "NU", "Base Stat Total": 480, "Num Moves": 67}, {"Name": "Whiscash", "Tier": "PU", "Base Stat Total": 468, "Num Moves": 66}, {"Name": "Wigglytuff", "Tier": "PU", "Base Stat Total": 435, "Num Moves": 127}, {"Name": "Wishiwashi", "Tier": "PU", "Base Stat Total": 175, "Num Moves": 43}, {"Name": "Wishiwashi-School", "Tier": "PU", "Base Stat Total": 620, "Num Moves": 43}, {"Name": "Wobbuffet", "Tier": "PU", "Base Stat Total": 405, "Num Moves": 7}, {"Name": "Wormadam", "Tier": "PU", "Base Stat Total": 424, "Num Moves": 58}, {"Name": "Wormadam-Sandy", "Tier": "PU", "Base Stat Total": 424, "Num Moves": 59}, {"Name": "Wormadam-Trash", "Tier": "PU", "Base Stat Total": 424, "Num Moves": 57}, {"Name": "Xatu", "Tier": "NU", "Base Stat Total": 470, "Num Moves": 93}, {"Name": "Xerneas", "Tier": "Uber", "Base Stat Total": 680, "Num Moves": 58}, {"Name": "Xurkitree", "Tier": "OU", "Base Stat Total": 570, "Num Moves": 49}, {"Name": "Yanmega", "Tier": "RU", "Base Stat Total": 515, "Num Moves": 68}, {"Name": "Yveltal", "Tier": "Uber", "Base Stat Total": 680, "Num Moves": 57}, {"Name": "Zangoose", "Tier": "PU", "Base Stat Total": 458, "Num Moves": 104}, {"Name": "Zapdos", "Tier": "OU", "Base Stat Total": 580, "Num Moves": 69}, {"Name": "Zebstrika", "Tier": "PU", "Base Stat Total": 497, "Num Moves": 54}, {"Name": "Zekrom", "Tier": "Uber", "Base Stat Total": 680, "Num Moves": 70}, {"Name": "Zoroark", "Tier": "UU", "Base Stat Total": 510, "Num Moves": 76}, {"Name": "Zygarde", "Tier": "OU", "Base Stat Total": 600, "Num Moves": 58}, {"Name": "Zygarde-10%", "Tier": "RU", "Base Stat Total": 486, "Num Moves": 43}, {"Name": "Zygarde-Complete", "Tier": "Uber", "Base Stat Total": 708, "Num Moves": 43}]}}; var embedOpt = {"mode": "vega-lite"}; function showError(el, error){ el.innerHTML = (' <div class="error" style="color:red;">' + ' <p>JavaScript Error: ' + error.message + ' </p>' + " <p>This usually means there's a typo in your chart specification. " + "See the javascript console for the full traceback. </p>" + ' </div>'); throw error; } const el = document.getElementById('vis'); vegaEmbed("#vis", spec, embedOpt) .catch(error => showError(el, error)); })(vegaEmbed); </script> </body> </html>
Paste that into a text file, save as file.html
, then open in your browser.
Viola!
Exercises
We’ve only just scratched the surface with what we can do with Altair. In this next portion of the workshop, we’ll try to create some other types of visualizations.
For data, you can use the pokemon data at this URL:
csv_url
'https://raw.githubusercontent.com/n2cholas/dsc-workshops/master/Intro_to_Interactive_Data_Viz_with_Altair/pokemon-data-cleaned.csv'
Or load data from vega_datasets
, which is a package with datasets well suited
for Altair:
from vega_datasets import data
cars_df = data.cars()
cars_df.sample()
Name | Miles_per_Gallon | Cylinders | Displacement | Horsepower | Weight_in_lbs | Acceleration | Year | Origin | |
---|---|---|---|---|---|---|---|---|---|
362 | honda prelude | 33.7 | 4 | 107.0 | 75.0 | 2210 | 14.4 | 1982-01-01 | Japan |
You can find a full list of datasets here. Check out the Altair example gallery for inspiration, or Altair’s tutorial notebooks for more in depth explanations.
Conclusion & Next Steps
We covered the basics of building interactive visualizations in Altair. For a more in depth treatment of everything we covered today, check out Jake VanderPlas’s workshop at PyCon 2018. As linked before, the example gallery is a great place to explore what’s possible with Altair. And of course, the best way to learn is by doing, so enjoy!