Instructions for quadrats

(produced at 21:24 UTC on 2025-02-03)

This task is part of project07 which is due at 23:00 EDT on 2025-03-14.

You have the option to work with a partner on this task if you wish. Working with a partner requires more work to coordinate schedules, but if you work together and make sure that you are both understanding the code you write, you will make progress faster and learn more.

You can download the starter code for this task using this link.

You can submit this task using this link.

Put all of your work for this task into the file quadrats.py
(you will create this file from scratch)

This task will give you practice using nested for loops and indexing on 2D grids. It is ecology-themed, but no understanding of ecology should be required to complete it. The functions here are not necessarily actually used in real ecology, but have been chosen to give you practice with certain important programming concepts.

In ecology, a "quadrat" is a square or rectangular frame used to count things like species abundance or distribution by placing it over an area and then counting organisms within each square of the grid.

A woman
crouches over a quadrat: a device that has twine strung across a square
frame at regular intervals to form a grid. The quadrat is placed over
some dry grass and soil and the woman holds a pointer she is using to
count things in different grid cells. SonoranDesertNPS from Tucson, Arizona, CC BY 2.0, via Wikimedia Commons

We can represent counts of a particular species in a quadrat in Python using a list of lists: the outer list holds multiple "row" lists, and each "row" list holds a series of numbers: the count of that species within each cell in that row. For a 5x5 quadrat like the one pictured above, this might look like:

data = [
    [1, 0, 0, 2, 0],
    [2, 1, 0, 0, 0],
    [1, 1, 0, 3, 0],
    [0, 1, 1, 2, 1],
    [0, 0, 0, 2, 1]
]

Here we have a list of 5 sub-lists, with 5 numbers in each sub-list. We've arranged the code into a square pattern but it could also have been written as:

data = [[1, 0, 0, 2, 0], [2, 1, 0, 0, 0], [1, 1, 0, 3, 0], [0, 1, 1, 2, 1], [0, 0, 0, 2, 1]]

Your job in this task is to write a number of Python functions for processing data of this form. You can assume in all cases that there will be at least 1 row with 1 value in it, and that all values will be non-negative integers. You can also assume that each row within a given example will have the same number of entries, that is, you can assume that the grid is rectangular (but not necessarily square).

Although in some cases these problems could be solved without using a nested loop, the point of this task is to practice using nested loops, so you must use a nested loop. in each function you write.

As usual each function must include a docstring.

There are 6 functions to write:

totalCount

The totalCount function simply returns the total sum of each cell count in the entire grid, given a data list-of-lists as described above. These totalCount examples show how it should work.

Note that normally it would be possible to do this using a single loop and the sum function, but in order to practice writing nested loops, you are not allowed to use sum in this function and you must use a nested loop.

inhabitedCells

The inhabitedCells function returns the total number of cells in which the count is above 0. These inhabitedCells snippets give some examples.

highestCount

The highestCount function must return the highest single entry from the grid. These highestCount snippets give examples.

Note that normally it would be possible to do this using a single loop and the max function, but in order to practice writing nested loops, you are not allowed to use max in this function and you must use a nested loop.

highestThree

The highestThree function works like highestCount except that it returns a list containing 3 elements, which are the 3 highest (or tied-for-highest) counts in the grid. These highestThree snippets provide examples.

Note that even if the grid has fewer than 3 cells, there should always be 3 elements in the list returned, with zeroes added to achieve that.

squaresCount

Species that are more fragmented can be more vulnerable than those that are distributed more evenly. One measure of fragmentation is how many 2x2 squares in the grid include at least one member of the target species in all 4 of their cells. For example, these two grids both have 17 total organisms, but in the first grid there is only one 2x2 square that's got at least 1 organism in every cell and in the second there are five such squares (counting overlaps):

fragmented = [
    [3, 0, 2, 4],
    [2, 1, 2, 3],
    [0, 1, 0, 1],
]
consolidated = [
    [2, 1, 2, 4],
    [2, 1, 1, 3],
    [0, 1, 1, 1],
]

The squaresCount function returns the number of 2x2 squares in which all 4 elements are non-zero, including overlapping squares in the count. For example, on a 3x3 grid, there are four 2x2 squares to check. These squaresCount snippets show how it works.

Hint: In general, for a grid of height X and width Y, there are (X - 1) times (Y - 1) total 2x2 squares.

Note that for this function, you will need to use an index loop and so you are required to use the range function (or enumerate as an alternative).

isolatedCount

Another way of looking at fragmentation is the number of isolated cells: cells where at least one organism is counted, but where none of the four orthogonally adjacent cells have any. Counting the number of isolated cells helps assess how fragmented the distribution is. For example, both of these grids have a total population of 11, but the first grid has two isolated cells, while the second grid here has none:

isolated = [
    [1, 0, 3],
    [0, 2, 0],
    [1, 1, 3]
]
joined = [
    [1, 0, 2],
    [1, 1, 1],
    [0, 2, 3]
]

Note that we don't check how many individuals are in an isolated cell, just that the counts are zero in all four neighbors.

The isolatedCount function must return the count of isolated cells in a grid. These isolatedCount snippets give examples of how it should work.

Notes:

  1. You will need to use index loops for this problem. You are required to use the range function (or enumerate as an alternative).
  2. Cells at the edge of the grid can more easily become isolated because they have fewer neighbors. Your code needs to check for whether or not you are at an edge of the grid before checking the contents of a neighboring cell.
  3. Using a helper function can simplify this problem considerably.

Examples

totalCount Examples

Examples of how totalCount works.

In []:
totalCount([[1, 3], [0, 5]])
Out[]:
9
In []:
totalCount([[0, 2, 0], [0, 1, 2], [1, 1, 1]])
Out[]:
8
In []:
totalCount([[0, 5, 3, 3], [2, 3, 4, 6], [6, 4, 2, 1], [3, 0, 4, 2]])
Out[]:
48
In []:
totalCount( [[0, 1, 1, 0], [2, 0, 0, 4], [0, 0, 2, 0], [2, 0, 0, 0], [0, 1, 2, 0]] )
Out[]:
15

inhabitedCells Examples

Examples of how inhabitedCells works.

In []:
inhabitedCells([[1, 3], [0, 5]])
Out[]:
3
In []:
inhabitedCells([[0, 2, 0], [0, 1, 2], [1, 1, 1]])
Out[]:
6
In []:
inhabitedCells([[0, 5, 3, 3], [2, 3, 4, 6], [6, 4, 2, 1], [3, 0, 4, 2]])
Out[]:
14
In []:
inhabitedCells( [[0, 1, 1, 0], [2, 0, 0, 4], [0, 0, 2, 0], [2, 0, 0, 0], [0, 1, 2, 0]] )
Out[]:
8

highestCount Examples

Examples of how highestCount works.

In []:
highestCount([[1, 3], [0, 5]])
Out[]:
5
In []:
highestCount([[0, 2, 0], [0, 1, 2], [1, 1, 1]])
Out[]:
2
In []:
highestCount([[0, 5, 3, 3], [2, 3, 4, 6], [6, 4, 2, 1], [3, 0, 4, 2]])
Out[]:
6
In []:
highestCount( [[0, 1, 1, 0], [2, 0, 0, 4], [0, 0, 2, 0], [2, 0, 0, 0], [0, 1, 2, 0]] )
Out[]:
4

highestThree Examples

Examples of how highestThree works. Note that if there are fewer than 3 total values, zeroes are added to make the result always have length 3

In []:
highestThree([[1, 3], [0, 5]])
Out[]:
[5, 3, 1]
In []:
highestThree([[3, 0, 2, 4], [2, 1, 2, 3], [0, 1, 0, 1]])
Out[]:
[4, 3, 3]
In []:
highestThree( [ [1, 1, 0, 0, 0, 0, 4, 1, 3, 3, 5], [0, 0, 0, 1, 0, 2, 0, 3, 2, 3, 3], [3, 2, 3, 3, 0, 3, 3, 0, 1, 4, 2], [4, 3, 3, 2, 3, 3, 0, 2, 0, 5, 4], [3, 4, 4, 1, 0, 1, 2, 0, 3, 4, 7], [2, 0, 3, 2, 1, 1, 0, 0, 3, 6, 8], [0, 0, 2, 0, 1, 1, 2, 3, 3, 5, 6], [0, 0, 0, 0, 0, 2, 1, 1, 3, 4, 5], [0, 3, 0, 1, 0, 0, 1, 2, 3, 0, 0], [1, 2, 0, 0, 0, 2, 3, 0, 0, 0, 0], [1, 0, 2, 0, 1, 0, 2, 1, 0, 0, 12], ] )
Out[]:
[12, 8, 7]
In []:
highestThree([[0, 0, 0], [0, 0, 0], [0, 0, 0]])
Out[]:
[0, 0, 0]
In []:
highestThree([[1], [1]])
Out[]:
[1, 1, 0]
In []:
highestThree([[1]])
Out[]:
[1, 0, 0]

squaresCount Examples

Examples of how squaresCount works. Note how overlapping squares are counted separately.

In []:
squaresCount([[1, 3], [0, 5]])
Out[]:
0
In []:
squaresCount([[0, 2, 0], [0, 1, 2], [1, 1, 1]])
Out[]:
1
In []:
squaresCount([[0, 5, 3, 3], [2, 3, 4, 6], [6, 4, 2, 1], [3, 0, 4, 2]])
Out[]:
6
In []:
squaresCount( [[0, 1, 1, 0], [2, 0, 0, 4], [0, 0, 2, 0], [2, 0, 0, 0], [0, 1, 2, 0]] )
Out[]:
0

isolatedCount Examples

Examples of how isolatedCount works. Note how it counts cells on the edge as isolated as long as their neighbors in the grid are zeroes.

In []:
isolatedCount([[1, 3], [0, 5]])
Out[]:
0
In []:
isolatedCount([[3, 0, 2, 4], [2, 1, 2, 3], [0, 1, 0, 1]])
Out[]:
0
In []:
isolatedCount( [ [1, 1, 0, 0, 0, 0, 4, 1, 3, 3, 5], [0, 0, 0, 1, 0, 2, 0, 3, 2, 3, 3], [3, 2, 3, 3, 0, 3, 3, 0, 1, 4, 2], [4, 3, 3, 2, 3, 3, 0, 2, 0, 5, 4], [3, 4, 4, 1, 0, 1, 2, 0, 3, 4, 7], [2, 0, 3, 2, 1, 1, 0, 0, 3, 6, 8], [0, 0, 2, 0, 1, 1, 2, 3, 3, 5, 6], [0, 0, 0, 0, 0, 2, 1, 1, 3, 4, 5], [0, 3, 0, 1, 0, 0, 1, 2, 3, 0, 0], [1, 2, 0, 0, 0, 2, 3, 0, 0, 0, 0], [1, 0, 2, 0, 1, 0, 2, 1, 0, 0, 12], ] )
Out[]:
5
In []:
isolatedCount([[1, 0, 3], [0, 2, 0], [1, 1, 3]])
Out[]:
2
In []:
isolatedCount([[1, 0, 2], [1, 1, 1], [0, 2, 3]])
Out[]:
0
In []:
isolatedCount([[0, 0, 0], [0, 0, 0], [0, 0, 0]])
Out[]:
0
In []:
isolatedCount([[1]])
Out[]:
1
In []:
isolatedCount([[0, 5, 0], [5, 0, 5], [0, 5, 0]])
Out[]:
4

Rubric

Group goals:
 
unknown All functions are documented
Each function you define must include a non-empty documentation string as the very first thing in the function.
 
unknown totalCount must return the correct result
The result returned when your totalCount function is run must match the solution result.
 
unknown inhabitedCells must return the correct result
The result returned when your inhabitedCells function is run must match the solution result.
 
unknown highestCount must return the correct result
The result returned when your highestCount function is run must match the solution result.
 
unknown highestThree must return the correct result
The result returned when your highestThree function is run must match the solution result.
 
unknown squaresCount must return the correct result
The result returned when your squaresCount function is run must match the solution result.
 
unknown isolatedCount must return the correct result
The result returned when your isolatedCount function is run must match the solution result.
 
unknown Define totalCount with 1 parameter
Use def to define totalCount with 1 parameter
 
unknown Use any kind of loop
Within the definition of totalCount with 1 parameter, use any kind of loop in at least one place.
 
unknown Use any kind of loop
Within the loop within the definition of totalCount with 1 parameter, use any kind of loop in at least one place.
 
unknown Do not call sum
Within the definition of totalCount with 1 parameter, do not call sum.
 
unknown Define inhabitedCells with 1 parameter
Use def to define inhabitedCells with 1 parameter
 
unknown Use any kind of loop
Within the definition of inhabitedCells with 1 parameter, use any kind of loop in at least one place.
 
unknown Use any kind of loop
Within the loop within the definition of inhabitedCells with 1 parameter, use any kind of loop in at least one place.
 
unknown Define highestCount with 1 parameter
Use def to define highestCount with 1 parameter
 
unknown Use any kind of loop
Within the definition of highestCount with 1 parameter, use any kind of loop in at least one place.
 
unknown Use any kind of loop
Within the loop within the definition of highestCount with 1 parameter, use any kind of loop in at least one place.
 
unknown Do not call max
Within the definition of highestCount with 1 parameter, do not call max.
 
unknown Define highestThree with 1 parameter
Use def to define highestThree with 1 parameter
 
unknown Use any kind of loop
Within the definition of highestThree with 1 parameter, use any kind of loop in at least one place.
 
unknown Use any kind of loop
Within the loop within the definition of highestThree with 1 parameter, use any kind of loop in at least one place.
 
unknown Define squaresCount with 1 parameter
Use def to define squaresCount with 1 parameter
 
unknown Use any kind of loop
Within the definition of squaresCount with 1 parameter, use any kind of loop in at least one place.
 
unknown Use any kind of loop
Within the loop within the definition of squaresCount with 1 parameter, use any kind of loop in at least one place.
 
unknown Call range
Within the definition of squaresCount with 1 parameter, call range or enumerate in at least one place.
 
unknown Define isolatedCount with 1 parameter
Use def to define isolatedCount with 1 parameter
 
unknown Use any kind of loop
Within the definition of isolatedCount with 1 parameter, use any kind of loop in at least one place.
 
unknown Use any kind of loop
Within the loop within the definition of isolatedCount with 1 parameter, use any kind of loop in at least one place.
 
unknown Call range
Within the definition of isolatedCount with 1 parameter, call range or enumerate in at least one place.
 
unknown Do not ignore the results of any fruitful function calls
According to the "Don't waste fruit" principle, every place you call a fruitful function (built-in or custom) you must store the result in a variable, or that function call must be part of a larger expression that uses its return value.
 
unknown Do not create any variables that you never make use of
According to the "Don't waste boxes" principle, every time you create a variable (using = or by defining a parameter for a function) you must also later use that variable as part of another expression. If you need to create a variable that you won't use, it must have the name _, but you should only do this if absolutely necessary.
 
unknown totalCount must return the correct result
The result returned when your totalCount function is run must match the solution result.
 
unknown inhabitedCells must return the correct result
The result returned when your inhabitedCells function is run must match the solution result.
 
unknown highestCount must return the correct result
The result returned when your highestCount function is run must match the solution result.
 
unknown highestThree must return the correct result
The result returned when your highestThree function is run must match the solution result.
 
unknown squaresCount must return the correct result
The result returned when your squaresCount function is run must match the solution result.
 
unknown isolatedCount must return the correct result
The result returned when your isolatedCount function is run must match the solution result.
 
unknown Define highestCount with 1 parameter
Use def to define highestCount with 1 parameter
 
unknown Use any kind of loop
Within the definition of highestCount with 1 parameter, use any kind of loop in exactly 2 places.
 
unknown Define highestThree with 1 parameter
Use def to define highestThree with 1 parameter
 
unknown Use any kind of loop
Within the definition of highestThree with 1 parameter, use any kind of loop in exactly 2 places.
 
unknown Define squaresCount with 1 parameter
Use def to define squaresCount with 1 parameter
 
unknown Use any kind of loop
Within the definition of squaresCount with 1 parameter, use any kind of loop in exactly 2 places.
 
unknown Define isolatedCount with 1 parameter
Use def to define isolatedCount with 1 parameter
 
unknown Use any kind of loop
Within the definition of isolatedCount with 1 parameter, use any kind of loop in exactly 2 places.