# Visualizing Avian Biodiversity Using Treemaps¶

Due: Friday, December 4th at 3pm CST.

The purpose of this assignment is to give you practice working with recursive data structures and writing recursive functions.

You must work alone on this assignment.

## Introduction¶

The Cornell Lab of Ornithology manages a website called eBird, which collects bird sighting data from birdwatchers around the world. eBird claims to be one of the world’s largest biodiversity-related projects, collecting over 100 million bird sightings per year. As they explain on their website, the data they collect “inform novel conservation actions, from site-specific information about the occurrence and abundance of birds, to looking at patterns of species abundance across continent-spanning flyways. eBird data contribute to hundreds of conservation decisions and peer-reviewed papers, thousands of student projects, and help inform research worldwide. Applications range from ecological and ornithological research, to conservation application, to advancing research in the fields of socioeconomics, artificial intelligence, and computer science.”

A question we might want to investigate about our Chicago community is, “Which birds are most prevelant in our area, and how does this depend on the time of year?”

We can use the eBird data 1 to address this. eBird makes their basic dataset, which contains raw data of bird observations, available to anyone under their terms of use.

Here is a bar chart showing the number of times each species of bird was seen in Cook County in October 2020:

Top 40 most commonly sighted birds in Cook County, IL, October 2020

From this we can see the most commonly seen 2 bird species in Cook County in October 2020 was the American Robin. But there are some shortcomings to this visualization.

For example, while this figure is fairly large, it still only shows the 40 most commonly sighted bird species in Cook County. In total, there were 230 different species sighted. We cannot determine from this figure even approximately what fraction of bird sightings were of the American Robin.

Additionally, we might notice that there are a number of species of sparrow that appear on this list, including the House Sparrow, the White-throated Sparrow, and the Song Sparrow, among others. All together, sparrows outnumber robins by quite a bit, but that is not clear from this figure.

So, we might appreciate a visualization in which related species are grouped together. The scientific study of how to group together related species is taxonomy. All living organisms are organized into a taxonomic hierarchy. For example, the class Aves of all birds is divided into a number of orders, such as Passeriformes, Anseriformes, and Piciformes. Each order is divided into a number of families, each family is divided into a number of genera (plural of genus), and each genus is divided into a number of species. This hierarchical structure is naturally represented as a tree, where each subdivision is a node in the tree.

The National Center for Biotechnology Information (NCBI) provides a Taxonomy Database.

Below we show a tree containing only the birds species that appear in our data set (further abridged). Each level of the tree represents a taxonomic rank (class, order, family, genus, or species).

• Each node at a particular level represents a taxon at that level — that is, a member of that taxonomic rank.

• Each internal (non-leaf) node has a key consisting of the taxonomic rank and the name of that taxon (for example, “family Passerellidae”), while each leaf has a key which is the the common name of a species. Each node has a value which is the number of times that species (or genus, family, etc.) was sighted.

class Aves: 67629
│
├──order Anseriformes: 7188
│  │
│  └──family Anatidae: 7188
│     │
│     ├──genus Anas: 2337
│     │  │
│     │  ├──American Black Duck: 47
│     │  │
│     │  ├──Blue-winged Teal: 84
│     │  │
│     │  ├──Green-winged Teal: 158
│     │  │
│     │  ├──Mallard (Domestic type): 1779
│     │  │
│     │  ├──Northern Pintail: 101
│     │  │
│     │  └──Northern Shoveler: 168
│     │
│     ├──genus Mareca: 297
│     │  │
│     │  ├──American Wigeon: 83
│     │  │
│     │
│     ├──genus Melanitta: 128
│     │  │
│     │  ├──Black Scoter: 32
│     │  │
│     │  ├──Surf Scoter: 81
│     │  │
│     │  └──White-winged Scoter: 15
│     │
│     ├──genus Bucephala: 102
│     │  │
│     │  │
│     │  └──Common Goldeneye: 20
│     │
│     ├──genus Branta: 2329
│     │  │
│     │  ├──Cackling Goose: 31
│     │  │
│     │
│     ├──genus Aythya: 522
│     │  │
│     │  ├──Canvasback: 32
│     │  │
│     │  ├──Greater Scaup: 89
│     │  │
│     │  ├──Lesser Scaup: 69
│     │  │
│     │  │
│     │  └──Ring-necked Duck: 125
│     │
│     ├──genus Mergus: 238
│     │  │
│     │  ├──Common Merganser: 31
│     │  │
│     │  └──Red-breasted Merganser: 207
│     │
│     ├──genus Anser: 62
│     │  │
│     │  ├──no rank unclassified Anser: 2
│     │  │  │
│     │  │  └──Domestic goose sp. (Domestic type): 2
│     │  │
│     │  ├──Greater White-fronted Goose: 10
│     │  │
│     │  └──Snow Goose: 50
│     │
│     ├──genus Lophodytes: 174
│     │  │
│     │  └──Hooded Merganser: 174
│     │
│     ├──genus Clangula: 47
│     │  │
│     │  └──Long-tailed Duck: 47
│     │
│     ├──genus Cygnus: 120
│     │  │
│     │  ├──Mute Swan: 82
│     │  │
│     │  ├──Trumpeter Swan: 36
│     │  │
│     │  └──Tundra Swan: 2
│     │
│     ├──genus Oxyura: 172
│     │  │
│     │  └──Ruddy Duck: 172
│     │
│     └──genus Aix: 660
│        │
│        └──Wood Duck: 660
│
...


While this tree representation helps us to make comparisons, it is only practical to view a portion of the tree at any given time. It would be useful see a visual representation of the data, which is the role information visualization plays in computing and in data science.

How can we visualize this information in an effective way? We can use treemaps, which are an excellent tool for visualizing hierarchical data. Here, for example, is a treemap corresponding to the tree above.

Treemap of bird sightings in Cook County, IL in October 2020

Each rectangle represents a single species. The area of the rectangle is proportional to the number of sightings of that species. Species in the same genus are grouped together into a larger rectangle, genera in the same family are grouped together, etc.

Looking at the data in this form, we can immediately see that, for instance, the American Robin (near the center of the treemap) is more prevalent in the data than other species, but that sparrows (the group of boxes in the upper-left corner) as a whole are three to four times more prevalent than robins.

In general, treemaps are a space-constrained method for visualizing hierarchical structures that present a sense of “mass” and proportionality in a way that the typical tree diagram does not. Treemaps allow the viewer to compare leaves and sub-trees even at varying depths in the tree, and to spot patterns and exceptions. Ben Shneiderman designed treemaps during the 1990s as a way to visualize the contents of a file system. This technique has since been used to visualize many different types of data, including stock portfolios, oil production, a gene ontology, stimulus spending, and more. The original idea has been extended in many interesting ways.

In this assignment, you will write code to generate treemaps to visualize this avian biodiversity data from samples taken throughout the year.

1

eBird. 2017. eBird: An online database of bird distribution and abundance [web application]. eBird, Cornell Lab of Ornithology, Ithaca, New York. Available: http://www.ebird.org. (Accessed: Date November 21, 2020).

2

We are relying on just the number of sightings recorded on eBird. There may be other factors to adjust for to determine the actual prevalence of each species, such as how easy a bird is to see, whether someone reports it, and how much effort a person put in to find it. eBird does also provide data on “effort”, but we will not use that for this assignment.

## Getting started¶

Before you start working on the assignment’s tasks, please take a moment to follow the steps described in Coursework Basics page to get the files for this assignment (these steps will be the same as the ones you followed to get the files previous assignments. Please note that you will not be able to start working on the assignment until you fetch the assignment files (which will appear in a pa6/ directory in your repository).

The pa6/ directory contains several Python files, but you will only be modifying one of them: treemap.py. The rest of the files are described throughout the assignment. You can also consult the README for a description of each file.

As usual, you will be able to test your code from IPython and by using py.test. When using IPython, make sure to enable autoreload before importing the Python files we will use in this assignment:

In [1]: %load_ext autoreload

In [3]: import treemap, drawing

In [4]: import tree


The drawing module

As we’ll describe later on, we provide a drawing module for visualizing treemaps. This module is not required for completing the assignment, and will only work on the virtual desktop. If you are working on the assignment through SSH, the above import commands may produce the following messages:

Failed to connect to Mir: Failed to connect to server socket: No such file or directory
Unable to init server: Could not connect: Connection refused
Failed to connect to Mir: Failed to connect to server socket: No such file or directory
Unable to init server: Could not connect: Connection refused

(ipython3:35882): Gdk-CRITICAL **: gdk_cursor_new_for_display: assertion 'GDK_IS_DISPLAY (display)' failed

(ipython3:35882): Gdk-CRITICAL **: gdk_cursor_new_for_display: assertion 'GDK_IS_DISPLAY (display)' failed


You can safely ignore these messages. Because SSH is a text-only interface, these messages are simply warning you that the drawing module will not work.

Like previous assignments, we also include a series of automated tests. These tests require an additional set of files, which you can download by running the following from the Linux command-line inside the pa6/ directory:



## Task 2: Values and paths¶

We now turn our attention to trees with multiple levels and more complex structure, like the ones in birds.json.

The trees in birds.json only include a value for the value attribute in the leaf nodes (in all other nodes, the value is set to None). Furthermore, all nodes are lacking the path attribute mentioned earlier, which will be used for assigning colors to our treemap. In Task 2, you will complete the following two recursive functions in treemap.py to respectively compute the value for all internal nodes and to set the path for all nodes.

Your solution to each must be recursive. Non-recursive solutions (i.e., functions that do not call themselves with an input that is in some way smaller) will not receive credit for this task. Furthermore, each function should be generalizable, working for a tree of any height. As a guideline, in our implementation each of these functions requires fewer than ten lines of code.

def compute_internal_values(t):
'''
Assign a value to the internal nodes. The value of the leaves should
already be set. The value of an internal node is the sum of the value
of its children.

Inputs:
t (Tree): a tree

Returns:
The input tree t should be modified so that every internal node's
value is set to be the sum of the values of its children.

The return value will be the value of the root of t (that is,
t.value)
'''

def compute_paths(t, prefix=()):
'''
Assign the path attribute of all nodes to be the full path through the
tree from the root down to, but not including, that node. For example,
following the path
"class Aves" --> "order Passeriformes" --> "family Passerellidae"
from the root to a node should result in the path atttribute
("class Aves", "order Passeriformes")
being assigned to the node with key "family Passerellidae".
For the root node, the path attribute should be an empty tuple.

Inputs:
t (Tree): a tree
prefix (tuple of strings): Prefix to add to verbose label

Outputs:
Nothing. The input tree t should be modified to contain a path
attribute for all nodes.
'''


You can informally test these functions from IPython by printing out the tree after calling one of these functions, and checking whether whether the value or path are set correctly. You can use the trees provided in birds.json We suggest you start with the smaller, sample trees, which are easier to check manually.

For example, you can check compute_internal_values as follows:

In [22]: data = treemap.load_trees("data/birds.json")

In [23]: treemap.compute_internal_values(data['s1'])
Out[23]: 100

In [24]: data['s1'].print()

A100: 100
│
├──B40: 40
│  │
│  ├──E10: 10
│  │
│  ├──F10: 10
│  │  │
│  │  ├──J6: 6
│  │  │  │
│  │  │  ├──L1: 1
│  │  │  │
│  │  │  ├──M3: 3
│  │  │  │
│  │  │  └──N2: 2
│  │  │
│  │  └──K4: 4
│  │
│  └──G20: 20
│
├──C40: 40
│  │
│  ├──H30: 30
│  │
│  └──I10: 10
│
└──D20: 20


Caution

Note that compute_internal_values modifies the tree in place (as does compute_paths). If you want a fresh copy of the tree for further testing, you need to re-load the data.

You can check compute_paths as follows (note how the print method has an optional argument to print the paths)

In [25]: data = treemap.load_trees("data/birds.json")

In [26]: treemap.compute_paths(data['s1'])

In [27]: data['s1'].print(paths=True)

A100: ()
│
├──B40: ('A100',)
│  │
│  ├──E10: ('A100', 'B40')
│  │
│  ├──F10: ('A100', 'B40')
│  │  │
│  │  ├──J6: ('A100', 'B40', 'F10')
│  │  │  │
│  │  │  ├──L1: ('A100', 'B40', 'F10', 'J6')
│  │  │  │
│  │  │  ├──M3: ('A100', 'B40', 'F10', 'J6')
│  │  │  │
│  │  │  └──N2: ('A100', 'B40', 'F10', 'J6')
│  │  │
│  │  └──K4: ('A100', 'B40', 'F10')
│  │
│  └──G20: ('A100', 'B40')
│
├──C40: ('A100',)
│  │
│  ├──H30: ('A100', 'C40')
│  │
│  └──I10: ('A100', 'C40')
│
└──D20: ('A100',)


To run the automated tests for this task only, you can run the following:

$py.test -x -v -k values$ py.test -x -v -k paths


## Drawing multiple-level treemaps¶

We now want to draw a treemap for a tree that may have multiple levels, like the trees in our original data set. In a finished treemap, there should be one rectangle per leaf of the tree (e.g. one rectangle per bird species), and each rectangle should have area proportional to the corresponding leaf’s value (e.g. proportional to the number of sightings of that bird).

One approach would be to put all of these leaves into one big list, and use the one-level treemap algorithm described earlier. But, the resulting visualization would abandon the structure of the tree. We would prefer that the rectangles of closely-related leaves appear close together in the treemap.

Here is an algorithm to accomplish this.

Example

We will use this tree as a running example:

Root: None
│
├──Three: None
│  │
│  ├──Three ~ Two: 2
│  │
│  └──Three ~ One: 1
│
├──Two: 2
│
├──Six (a): None
│  │
│  ├──Six (a) ~ Four: None
│  │  │
│  │  ├──Six (a) ~ Four ~ Three: 3
│  │  │
│  │  └──Six (a) ~ Four ~ One: 1
│  │
│  └──Six (a) ~ Two: 2
│
├──Six (b): 6
│
├──One (a): 1
│
├──One (b): 1
│
└──One (c): 1


Step 1: Compute the values of the internal nodes (the values of the leaves should already be set). Each internal node should have its value set equal to the sum of the values of its children.

We will additionally set the path attribute of each node at this time.

Example

After computing the internal values, our example tree is as follows.

Root: 20
│
├──Three: 3
│  │
│  ├──Three ~ Two: 2
│  │
│  └──Three ~ One: 1
│
├──Two: 2
│
├──Six (a): 6
│  │
│  ├──Six (a) ~ Four: 4
│  │  │
│  │  ├──Six (a) ~ Four ~ Three: 3
│  │  │
│  │  └──Six (a) ~ Four ~ One: 1
│  │
│  └──Six (a) ~ Two: 2
│
├──Six (b): 6
│
├──One (a): 1
│
├──One (b): 1
│
└──One (c): 1


Step 2: Focus on the root and its children. Lay out rectangles for the children according to the one-level algorithm.

Example

Ignoring all nodes other than the root and its children, our example tree looks like the following.

Root: 20
│
├──Three: 3
│
├──Two: 2
│
├──Six (a): 6
│
├──Six (b): 6
│
├──One (a): 1
│
├──One (b): 1
│
└──One (c): 1


The child nodes are the same as in our example for the one-level algorithm. Recall that the rectangles generated were as follows.

label

x

y

width

height

Six (a)

0.000

0.000

0.600

0.400

Six (b)

0.000

0.400

0.600

0.400

Three

0.600

0.000

0.400

0.300

Two

0.600

0.300

0.400

0.200

One (a)

0.600

0.500

0.267

0.150

One (b)

0.600

0.650

0.267

0.150

One (c)

0.867

0.500

0.133

0.300

And, this is visualized as the following.

Note that each rectangle created in this step corresponds not necessarily to a leaf, but to a subtree.

Step 3 Deal with each rectangle created in Step 2. What we do depends on whether or not the subtree associated with the rectangle is a leaf.

1. If the subtree is a leaf, then we are done with this rectangle: this is one of the rectangles we will draw.

2. If the subtree is not a leaf, then we further subdivide it. We lay out the sub-rectangles within this rectangle recursively: this rectangle serves as the bounding rectangle, and we apply the (multiple-level) treemap algorithm to the corresponding subtree.

Example

In our example, the children of the root are the following:

Six (a): 6
Six (b): 6
Three: 3
Two: 2
One (a): 1
One (b): 1
One (c): 1


Of these, Six (b): 6, Two: 2, One (a): 1, One (b): 1, and One (c): 1 are leaves. So, the corresponding rectangles can be added to the list of rectangles to draw:

label

x

y

width

height

Six (b)

0.000

0.400

0.600

0.400

Two

0.600

0.300

0.400

0.200

One (a)

0.600

0.500

0.267

0.150

One (b)

0.600

0.650

0.267

0.150

One (c)

0.867

0.500

0.133

0.300

Here they are visualized:

The other children are not leaves and need to be dealt with recursively. The first of these children is the following subtree:

Six (a): 6
│
├──Six (a) ~ Four: 4
│  │
│  ├──Six (a) ~ Four ~ Three: 3
│  │
│  └──Six (a) ~ Four ~ One: 1
│
└──Six (a) ~ Two: 2


It corresponds to the rectangle with origin (0., 0.), width 0.6 and height 0.4. Instead of drawing this rectangle, we fill it with sub-rectangles corresponding to the leaves of the subtree. To do that, we recursively use the (multiple-level) treemap algorithm on this subtree, using this rectangle as the bounding rectangle. This results in the following rectangles getting added to the list of rectangles to draw:

label

x

y

width

height

Six (a) ~ Four ~ Three

0.000

0.000

0.400

0.300

Six (a) ~ Four ~ One

0.000

0.300

0.400

0.100

Six (a) ~ Two

0.400

0.000

0.200

0.400

Visualizing all the rectangles we have listed for drawing so far:

There is one other child we haven’t dealt with, the following subtree.

Three: 3
│
├──Three ~ Two: 2
│
└──Three ~ One: 1


It corresponds to the rectangle with origin (0.6, 0.), width 0.4, and height 0.3. Again, we do not draw this rectangle, but instead recursively fill it with sub-rectangles corresponding to the leaves of the subtree. These are the resulting rectangles that get added to the list of rectangles to draw:

label

x

y

width

height

Three ~ Two

0.600

0.000

0.267

0.300

Three ~ One

0.867

0.000

0.133

0.300

The full list of rectangles to draw is as follows.

label

x

y

width

height

Six (a) ~ Four ~ Three

0.000

0.000

0.400

0.300

Six (a) ~ Four ~ One

0.000

0.300

0.400

0.100

Six (a) ~ Two

0.400

0.000

0.200

0.400

Six (b)

0.000

0.400

0.600

0.400

Three ~ Two

0.600

0.000

0.267

0.300

Three ~ One

0.867

0.000

0.133

0.300

Two

0.600

0.300

0.400

0.200

One (a)

0.600

0.500

0.267

0.150

One (b)

0.600

0.650

0.267

0.150

One (c)

0.867

0.500

0.133

0.300

Here is a visualization of the full list of rectangles.

Note: The order in which you compute the rectangles to draw may be different from the order in which they are described here; that is okay.

Your final task is to modify the compute_rectangles function from Task 1 to work with a tree of any shape.

First, add the following two lines of code in compute_rectangles immediately after the docstring.

compute_internal_values(t)
compute_paths(t)


Since we are including code to call compute_internal_counts and compute_verbose_labels within compute_rectangles, you do not need to call them before calling compute_rectangles.

In Task 1, you set the label attribute of each rectangle to be the key of the corresponding node. In this task, you must also set the color_code attribute of each rectangle to equal the path attribute of the corresponding node. This attribute is used by our drawing function to determine the color of each rectangle. Rectangles with the same color code are drawn in the same color. Rectangles that have nearby color codes (color codes that start the same way) are given similar colors. Since we are setting the color code of a rectangle to be the path attribute of the corresponding node, this means that nodes that are closely related in the tree will give rise to rectangles that are close in color.

Update your code to implement the multiple-level treemap algorithm from the previous section.

You must use recursion to accomplish this task. Also, you may not make any assumptions about the number of levels in the tree.

The implementation of this task is not too complex (our solution adds less than 10 lines to our code from Task 1), but again figuring out the recursion can be challenging. Make sure to think through the design of the recursion.

We again suggest you start by testing this function informally from IPython by printing out the rectangles produced by the function. You can use the trees provided in birds.json, which have multiple levels. Your updated function should also still work on the trees in sparrows.json. Here are some sample calls using the birds.json dataset.

In [14]: data = treemap.load_trees('data/birds.json')

In [15]: recs = treemap.compute_rectangles(data['s1'])

In [16]: for r in recs:
...:     print(r)
...:
RECTANGLE 0.0000 0.0000 0.4000 0.5000 G20
RECTANGLE 0.4000 0.0000 0.4000 0.2500 E10
RECTANGLE 0.4000 0.2500 0.2400 0.1250 M3
RECTANGLE 0.4000 0.3750 0.1600 0.1250 N2
RECTANGLE 0.5600 0.3750 0.0800 0.1250 L1
RECTANGLE 0.6400 0.2500 0.1600 0.2500 K4
RECTANGLE 0.0000 0.5000 0.6000 0.5000 H30
RECTANGLE 0.6000 0.5000 0.2000 0.5000 I10
RECTANGLE 0.8000 0.0000 0.2000 1.0000 D20

In [17]: recs = treemap.compute_rectangles(data['s2'])

In [18]: for r in recs:
...:     print(r)
...:
RECTANGLE 0.0000 0.0000 0.4000 0.3750 Six (a) ~ Four ~ Three
RECTANGLE 0.0000 0.3750 0.4000 0.1250 Six (a) ~ Four ~ One
RECTANGLE 0.4000 0.0000 0.2000 0.5000 Six (a) ~ Two
RECTANGLE 0.0000 0.5000 0.6000 0.5000 Six (b)
RECTANGLE 0.6000 0.0000 0.2667 0.3750 Three ~ Two
RECTANGLE 0.8667 0.0000 0.1333 0.3750 Three ~ One
RECTANGLE 0.6000 0.3750 0.4000 0.2500 Two
RECTANGLE 0.6000 0.6250 0.2667 0.1875 One (a)
RECTANGLE 0.6000 0.8125 0.2667 0.1875 One (b)
RECTANGLE 0.8667 0.6250 0.1333 0.3750 One (c)

In [19]: recs = treemap.compute_rectangles(data['s3'])

In [20]: for r in recs:
...:     print(r)
...:
RECTANGLE 0.0000 0.0000 0.2500 0.5000 3
RECTANGLE 0.2500 0.0000 0.2500 0.3333 2
RECTANGLE 0.2500 0.3333 0.2500 0.1667 1
RECTANGLE 0.0000 0.5000 0.4167 0.5000 5
RECTANGLE 0.4167 0.5000 0.0833 0.5000 1
RECTANGLE 0.5000 0.0000 0.2500 0.3333 2a
RECTANGLE 0.7500 0.0000 0.2500 0.3333 2b
RECTANGLE 0.5000 0.3333 0.5000 0.2500 3
RECTANGLE 0.5000 0.5833 0.4000 0.2083 2x
RECTANGLE 0.5000 0.7917 0.4000 0.2083 2y
RECTANGLE 0.9000 0.5833 0.1000 0.4167 **1**


Note that data['s2'] is the tree that we used in the explanation of the multiple-level treemap algorithm. Note also that if you were to keep only the root node and its children in the tree data['s1'], data['s2'], or data['s3'], you would get the tree data_flat['s1'], data_flat['s2'], or data_flat['s3'], respectively. You can compare the rectangles you get from each of these to help you debug.

Once you are confident that you’re producing the correct rectangles, you can again try visualizing them using the draw_rectangles function:

In [21]: drawing.draw_rectangles(recs)


To run the automated tests that are new for this task, you can run the following:

$py.test -x -v -k rectangles_full  To run all the automated tests for drawing rectangles, including the tests from Task 1, you can run the following: $ py.test -x -v -k rectangles


## Putting it all together¶

Once you have completed all the tasks, you will be able to easily generate any treemap you want from the command-line. In particular, you can run the following:

$python3 treemap.py data/birds.json KEY  Where KEY is the key for the tree for which you would like to produce a treemap. Here are some examples and the expected treemap. Take into account that running treemap.py will open a new window with the treemap; you will need to close that window before you can return to the command line. NOTE: If you are passing all the tests, do not worry if the produced treemaps do not match ours exactly! Your completeness grade will depend on the result of the tests, not on the exact graphics you produce. $ python3 treemap.py data/birds.json Oct


Treemap of bird sightings in Cook County, IL in October 2020

$python3 treemap.py data/birds.json Jul  Treemap of bird sightings in Cook County, IL in July 2020 $ python3 treemap.py data/birds.json Apr


Treemap of bird sightings in Cook County, IL in April 2020

$python3 treemap.py data/birds.json Feb  Treemap of bird sightings in Cook County, IL in February 2020 $ python3 treemap.py data/birds.json Year


Treemap of bird sightings in Cook County, IL from November 2019 to October 2020

You can use the data/sparrows.json data file as well:

\$ python3 treemap.py data/sparrows.json Oct


Treemap of species in family Passerellidae sighted in Cook County, IL in October 2020

Programming assignments will be graded according to a general rubric. Specifically, we will assign points for completeness, correctness, design, and style. (For more details on the categories, see our PA Rubric page.)

The exact weights for each category will vary from one assignment to another. For this assignment, the weights will be:

• Completeness: 50%

• Correctness: 20%

• Design: 15%

• Style: 15%

In this assignment, the Design grade will largely be based on how you designed your recursive functions.

Please note that if you do not use recursion in a given task, you can expect a considerable deduction in both the Correctness and Design portions of the rubric. If you do not use recursion at all in your code, you will receive a zero in both Correctness and Design.

Like previous assignments, you can obtain your test score by running py.test followed by ../common/grader.py.

## Submission¶

You must submit your work through Gradescope (linked from our Canvas site). In the “Programming Assignment #6” assignment, simply upload the file treemap.py (do not upload any other files!). Please note:

• You are allowed to make as many submissions as you want before the deadline.

• Please make sure you have read and understood our Late Submission Policy

• Your completeness score is determined solely based on the automated tests, but we may adjust your score if you attempt to pass tests by rote (e.g., by writing code that hard-codes the expected output for each possible test input).

• Gradescope will report the test score it obtains when running your code. If there is a discrepancy between the score you get when running our grader script, and the score reported by Gradescope, please let us know so we can take a look at it.

Acknowledgments: Gordon Kindlmann originally recommended drawing treemaps as good topic for an assignment.