CSc 120: Pokemon data analysis
Introduction
This problem involves some simple data analysis and aims to give you some more
practice with combining Python data structures in interesting ways: in this case,
using two-level dictionaries (i.e., a dictionary of dictionaries). The data, as it happens,
is about Pokemon
(source: www.kaggle.com).
You are to write a program to read in Pokemon data from a file and organize it
according to Pokemon type (we will only consider Type 1 for this
assignment), then repeatedly read in queries from the user and print out
solutions to those queries.
Input Format
The input file, Pokemon.csv, is in
CSV format
("comma-separated values"). This is a simple file format typically used
for tabular data such as that for spreadsheets, and if you want you can open this
file in a program like Excel or libreoffice to view the data in an easier-to-read
form.
Any line in the input file that begins with the character '#' or "!" (without quotes)
is a comment line that should be ignored for data analysis.
The first line of the input file, which is a comment line, gives the meaning of
the various data columns (in this table, the number at the top of each entry
gives its position in a row of comma-separated values, e.g.,
"Attack" is at position 6):
0
No.
|
1
Name
|
2
Type 1
|
3
Type 2
|
4
Total strength
|
5
HP
|
6
Attack
|
7
Defense
|
8
Special Attack
|
9
Special Defense
|
10
Speed
|
11
Generation
|
12
Legendary?
|
Expected Behavior
Write a program, in a file pokemon.py, that behaves as follows.
-
Read in the name of a data file. Use the input() function to prompt the user for the file name, but do not supply an argument to input(). (We call this a silent prompt.)
This file will be
a CSV file containing data about Pokemon in the format described
above. It can be the full Pokemon.csv
data file, but you can also specify other input files that contain
more or less information (e.g., a smaller file may be useful for
testing or debugging).
-
Read the data file specified above. Do not use the Python csv module.
Organize the data into a data structure
that collects together information about different Pokemon types.
For this assignment, for the Type, we will consider only the Type 1 field and ignore Type 2 since this is not defined for all Pokemon.
-
Repeatedly read and process queries from the user (see Queries below)
until the user enters an empty line.
Some examples are given here.
Queries
Your program will read in queries from the user, and for
each query, analyze the Pokemon data based on the query and print
out the results (see Output Format below).
The queries and the corresponding analyses are as follows:
| User query | Program action |
|
Total
|
Compute the Pokemon type(s) that have highest average Total strength.
|
|
HP
|
Compute the Pokemon type(s) that have highest average HP.
|
|
Attack
|
Compute the Pokemon type(s) that have highest average Attack.
|
|
Defense
|
Compute the Pokemon type(s) that have highest average Defense.
|
|
SpecialAttack
|
Compute the Pokemon type(s) that have highest average Special Attack.
|
|
SpecialDefense
|
Compute the Pokemon type(s) that have highest average Special Defense.
|
|
Speed
|
Compute the Pokemon type(s) that have highest average Speed.
|
(empty line) |
Terminate query processing |
|
anything else
|
Ignore the query |
Note that, in each case, there may be more than one type of Pokemon with the
highest average value computed. You should print out information about
each of them according to the output format given below.
Matching the queries entered by the user's with the User query column
shown above should be case-insensitive. For example, the user inputs
Attack,
attack,
ATTACK, and
AtTaCk
should all be processed the same way.
In order to get full credit, you should normalize the casing using the new "string.title()"" method covered in class!
Output Format
For each Pokemon type identified by your analysis, print out the result as follows:
print("{}: {}".format(pokemon_type, max_average))
where pokemon_type is the type of Pokemon, and max_average
is the average value computed for that Pokemon type for that query (e.g., average total,
average HP, average Attack, etc.), which should be equal to the maximum
value for that query across all types. If more than one Pokemon type has the same maximum average for a property, then print each one out, one per line, in alphabetical order of the Pokemon type.
Programming Requirements
-
Follow the style guidelines for this class.
-
Your code should not repeatedly and unnecessarily traverse all the
data about all the Pokemon when processing queries. To this end, organize your
code and data as follows.
-
A. Data organization.
-
You are required to use a two-level dictionary (i.e., a dictionary of dictionaries) to implement your
Pokemon database, as explained below:
-
At the top level, information should be grouped by Pokemon type:
i.e., all of the information about Pokemon belonging to a particular
type should be grouped together. A data structure that will do
this efficiently is the dictionary.
-
For each Pokemon type, we have to store information about all of the
different Pokemon that belong to that type. Again, this can be done
efficiently using a dictionary that maps the Pokemon's name to its
properties (Total strength, Attack, Defense, etc.).
Additionally, for each Pokemon type you must pre-compute the average values for
all of its properties (see Code Organization below). These average values
must also be organized as a dictionary keyed by Pokemon type.
-
B. Code organization.
-
Notice that the Pokemon properties you read in do not change during the computation.
This means that the average value for any property for any given Pokemon type will
remain the same as well. This, in turn, means that the maximum average values will
also not change. Therefore, the maximum average values can all be computed once
and saved, with
query processing simply looking up the saved values as needed.
Your program must pre-compute the maximum average values.
This approach is
closely related to an speedup technique called
memoization.
Your code should be organized as follows:
-
After reading in all the Pokemon data: for each Pokemon type, compute the average
value for each of its properties across all of the Pokemon that belong to that type.
Save this result into a dictionary indexed by Pokemon type.
-
Next, process the average values
obtained in the previous step to compute the maximum average value for each
property. Optionally, at this point you can also compute which Pokemon types have the
maximum average value for each property.
-
Use these data to help process user queries until there are no queries to process.
-
Additionally, you are required to include a debugging function named "debug_dict(dict, type)" that takes the dictionary and a type, and returns the inner dictionary of pokemon of a specific type. This will be used in unit tests.
Examples
Some examples of query processing, on different datasets, are shown
Pokemon program input/output examples.