<jupyter_start><jupyter_text>![Callysto.ca Banner](https://github.com/callysto/curriculum-notebooks/blob/master/callysto-notebook-banner-top.jpg?raw=true)  Callysto's Weekly Data Visualization Age of Empires II: finding the most expensive structure Recommended Grade Level: 7-12 Instructions Step 1 (your only step): “Run” the cells to see the graphsClick “Cell” and select “Run All.” This will import the data and run all the code to create the data visualizations (scroll back to the top after you’ve run the cells). **You don’t need to do any coding**.The plots generated in this notebook are interactive. You can hover over and click on elements to see more information.   ![instructions](https://github.com/callysto/data-viz-of-the-week/blob/main/images/instructions.png?raw=true)After a code cell runs, a number appears in the top left corner. If the code cell experiences a technical error some red text will appear below the cell. Email <EMAIL> if you experience issues.  About This NotebookCallysto's Weekly Data Visualization is a learning resource that helps Grades 5-12 teachers and students grow and develop data literacy skills. We do this by providing a data visualization, like a graph, and asking teachers and students to interpret it. This companion resource walks learners through how the data visualization is created and interpreted using the data science process. The steps of this process are listed below and applied to each weekly topic.1. Question - What are we trying to answer?2. Gather - Find the data source(s) you will need.3. Organize - Arrange the data so that you can easily explore it.4. Explore - Examine the data to look for evidence to answer our question. This includes creating visualizations.5. Interpret - Explain how the evidence answers our question.6. Communicate - Reflect on the interpretation. AcknowledgmentThe dataset we used in this week’s visualization is obtained via a REST API (application program interface) - an architectural style for an application program interface (API) that uses HTTP requests to access and use data. The API can be found here: https://age-of-empires-2-api.herokuapp.com/docs//   What is an API?An API (application programming interface) is a collection of programming code whose task is to facilitate data transmission between one software product and another. This transmission is bound to terms determined by the code."What exactly IS an API?". Source: https://medium.com/@perrysetgo/what-exactly-is-an-api-69f36968a41f  What is a REST API?* A REST API (also referred to as a RESTful web service or RESTful API)  is an architectural style for an API that uses HTTP requests to access and use data (think url).* Based on representational state transfer (REST). This style and approach to communications often used in web services development.* REST uses less bandwidth $\Rightarrow$ more suitable for efficient internet usage. * REST APIs can also be built with programming languages such as JavaScript or Python (though we won't explore how to do this in this notebook).  1. Question**How different are the costs associated with each of the different structures in Age of Empires II?**        About Age of Empires II * Age of Empires II: The Age of Kings is a real-time strategy video game * Second game in the Age of Empires series* Set in the Middle Ages and contains thirteen playable civilizations* Goal: gather resources (build towns, create armies, and defeat their enemies)* Players conquer rival towns and empires * Advance one of 13 civilizations through four "Ages": the Dark Age, the Feudal Age, the Castle Age, and the Imperial Age* Advancing to a new Age unlocks new units, structures, and technologies  2. GatherFirst we will import the Python libraries we need.<jupyter_code>import requests
from requests.exceptions import HTTPError
import json
import pandas as pd
import plotly.express as px<jupyter_output><empty_output><jupyter_text>We will need to define a function to help us perform a query - this is a request to access information from an API. In our case, from https://age-of-empires-2-api.herokuapp.com/docs//.<jupyter_code>def query_entry_pt(url):
    """This function takes as input a URL entry point and returns the complete JSON response in a REST API
    
    Input:
        - url(string): complete url (or entry point) pointing at server 
        
    Output:
        - jsonResponse(json object): JSON response associated wtih query
    
    """
    try:
        # Using GET command 
        response = requests.get(url)
        # Raise issues if response is different from 200
        response.raise_for_status()
        # access JSOn content
        jsonResponse = response.json()
        print("Success!",response)
        return jsonResponse

    except HTTPError as http_err:
        print(f'HTTP error occurred: {http_err}')
    except Exception as err:
        print(f'Other error occurred: {err}')<jupyter_output><empty_output><jupyter_text>We can then use the function along with Python libraries to get information on structure.<jupyter_code># Age of Empires entry points
entry_point = 'https://age-of-empires-2-api.herokuapp.com/api/v1/'

print("Structures")
# Perform query
aoe_stru = entry_point + 'structures'
jsonResponse_stru=query_entry_pt(aoe_stru)<jupyter_output><empty_output><jupyter_text>Code [200] indicates our request was successful. Let's take a look at a few entries:<jupyter_code>jsonResponse_stru['structures'][0]<jupyter_output><empty_output><jupyter_text>3. OrganizeThe information we obtained is in JSON format - we will format this so it looks more like a table. We will use the pandas library to format it into a data structure called "dataframe" - this is a table with columns and rows that allows us to manipulate the data as needed<jupyter_code># Normalize 
flattened_stru = pd.json_normalize(jsonResponse_stru,record_path='structures')
display(flattened_stru.head())<jupyter_output><empty_output><jupyter_text>4. ExploreLet's take a look at a few of the values in this dataframe.<jupyter_code>flattened_stru['age'].unique()<jupyter_output><empty_output><jupyter_text>Like in the game, the dataset contains information on the 4 main ages: 1. Dark2. Feudal3. Castle4. ImperialLet's take a look at the average build time for each age.<jupyter_code>average_build_dark = flattened_stru[flattened_stru['age']=='Dark']['build_time'].mean()
average_build_feudal = flattened_stru[flattened_stru['age']=='Feudal']['build_time'].mean()
average_build_castle = flattened_stru[flattened_stru['age']=='Castle']['build_time'].mean()
average_build_imperial = flattened_stru[flattened_stru['age']=='Imperial']['build_time'].mean()

print("The average build time for the Dark age is",average_build_dark)
print("The average build time for the Feudal age is",average_build_feudal)
print("The average build time for the Castle age is",average_build_castle)
print("The average build time for the Imperial age is",average_build_imperial)<jupyter_output><empty_output><jupyter_text>Exercise:Change `build_time` in the code above and rerun the cell to explore the average cost for any of the following categories:`cost.Wood`    `cost.Stone`	`cost.Gold`For example:`flattened_stru[flattened_stru['age']=='Dark']['build_time'].mean()` turns into `flattened_stru[flattened_stru['age']=='Dark']['cost.Wood'].mean()`<jupyter_code># ages: 'Dark', 'Feudal', 'Castle', 'Imperial'
subset_dat = flattened_stru[flattened_stru['age']=='Imperial']
fig = px.bar(data_frame=subset_dat,y='build_time',x='name',
            title='Build time per age',hover_name='age',
       labels={"name":"Name of structure","build_time":"Total build time"}).update_xaxes(categoryorder='total descending')
fig.show()<jupyter_output><empty_output><jupyter_text>From the information above we see that the average build time for the first three ages is less than 60 time units, but the average build time for the Imperial age is over 250 time units!Let's explore why this is happening.<jupyter_code>fig2 = px.box(data_frame=flattened_stru,y='build_time',x='age',points='all',hover_name='name',
            title='Build time per age (all structures)',labels={"age":"Age","build_time":"Build time"},
      color='age')
fig2.show()<jupyter_output><empty_output><jupyter_text>Visualizing the build time reveals that the Wonder - a structure found in the Imperial age - takes 3500 build units. This "skews" the data, making it appear as though the average build time for the imperial age is much higher. Let's remove that datapoint.<jupyter_code>no_wonder = flattened_stru[flattened_stru['name']!='Wonder']
print("Average build time average for Imperial age",no_wonder[no_wonder['age']=="Imperial"]['build_time'].mean())
fig3 = px.box(data_frame=no_wonder,y='build_time',x='age',points='all',hover_name='name',
            title='Build time per age (without Wonder)',labels={"age":"Age","build_time":"Build time"},
      color='age')
fig3.show()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.0884945876576275, 'fraction_numerical': 0.0063608972212922665, 'mean_word_length': 5.267132867132867, 'pattern_counts': {'":': 7, '<': 42, '<?xml version=': 0, '>': 42, 'https://': 6, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '10', 'dir': 'jupyter-structured-clean-dedup', 'id': '53224', 'license': 'CC-BY-4.0', 'n_tokens_mistral': 2553, 'n_tokens_neox': 2439, 'n_words': 1000, 'path': 'age-of-empires-ii/age-of-empires-ii.ipynb', 'repo_name': 'callysto/Data-Viz-of-the-Week', 'size': '8973'}