Interactive Data Visualization with Altair

Through 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 Totals, 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!


Related posts