API reference · assignments/hw3.py · 2574 lines
+API reference · assignments/hw3.py · 2645 lines
assignments.hw3
Assignment 3 autograder — evictions, code violations and the NDVI.
Graded against assignment_template/assignment-3.ipynb.
Where the evidence comes from
The matplotlib figures (the hex bin map, the two tree plots) have no such structure, only a picture, so those reuse grader/charts.py exactly as Assignment 2 does — which already merges the executed and submitted notebooks — and the one item the assignment marks on styling comes back for a person to confirm.
The two readings are combined in grade() by taking the better of them: a step counts as done if either shows the right answer. Neither is reliably better, so neither is ranked above the other; what differs is the confidence attached, and whether a TA is told which one carried the item.
Summary
-| Name | Kind | Description |
|---|---|---|
HW3Grader | class |
| Name | Kind | Description |
|---|---|---|
HW3Grader | class | |
keys_cover() | function | Every expected key is present, reading "e-03", "2003" and 2003 as one year. |
Constants
-| Name | Value | Notes |
|---|---|---|
WIDGET_CONTAINERS | ('HoloMap', 'GridSpace', 'NdLayout') | holoviews container types that mean "one chart per value of a dimension". |
LAZY_CONTAINERS | ('DynamicMap',) | The same thing, but never evaluated: hvplot returns this when dynamic=False is left out, and it holds no frames at all. |
LAYOUT_CONTAINERS | ('Layout', 'NdLayout') |
| Name | Value | Notes |
|---|---|---|
WIDGET_CONTAINERS | ('HoloMap', 'GridSpace', 'NdLayout') | holoviews container types that mean "one chart per value of a dimension". |
LAZY_CONTAINERS | ('DynamicMap',) | The same thing, but never evaluated: hvplot returns this when dynamic=False is left out, and it holds no frames at all. |
LAYOUT_CONTAINERS | ('Layout', 'NdLayout') | |
OVERLAY_CONTAINERS | ('Overlay', 'NdOverlay') | Layers drawn on one set of axes (line * scatter): one chart, not several. |
MAP_ELEMENTS | {'Polygons', 'Path', 'Shape', 'Image', 'QuadMesh', 'Tiles', 'WMTS'} | Layers that make an overlay a map rather than a chart of yearly totals. |
YEAR_KEY_RE | re.compile('(?:e-)?(\\d{2}|\\d{4})(?:\\.0+)?') | Eviction Lab names its year columns "e-03" to "e-16", and many students relabel them as 2003..2016 after the melt. Both name the same fourteen years. |
Classes
No description.
@@ -115,27 +115,27 @@Classes
Class attributes
| Name | Type | Default | Notes |
|---|---|---|---|
assignment_id | | 'hw3' | |
SUBMITTED_CONFIDENCE | | 0.9 | Confidence for a step seen only in the student's own saved output: above the review threshold, so it does not flag a correct submission on its own, but below the 1.0 of a result this grading run produced itself. A saved output is the student's claim about their run — it can be stale, and it can be edited by hand. |
NARROW_FRAME_COLUMNS | | 12 | An Eviction Lab frame carries 399 numeric columns, and across 3,217 tracts one of them will sum to almost any total by coincidence. A result of a group-by or a merge is narrow, so width is the guard: it keeps the raw file out of searches that look for a frame by what its numbers add up to. |
assignment_id | | 'hw3' | |
SUBMITTED_CONFIDENCE | | 0.9 | Confidence for a step seen only in the student's own saved output: above the review threshold, so it does not flag a correct submission on its own, but below the 1.0 of a result this grading run produced itself. A saved output is the student's claim about their run — it can be stale, and it can be edited by hand. |
NARROW_FRAME_COLUMNS | | 12 | An Eviction Lab frame carries 399 numeric columns, and across 3,217 tracts one of them will sum to almost any total by coincidence. A result of a group-by or a merge is narrow, so width is the guard: it keeps the raw file out of searches that look for a frame by what its numbers add up to. |
PLOT_RECEIVER_RE | | re.compile('([A-Za-z_][\\w\\.\\[\\]\'\\"]*)\\s*\\.\\s*plot\\s*\\(') | Two different frames plotted onto one map: the trees, and the city limits drawn under or over them — filled as a background layer or as an outline. |
Methods
- answers(ctx: GradingContext) -> answerlib.NotebookAnswers - #[source] + #[source]
What the notebooks show, read once per submission and reused.
- grade(ctx: GradingContext) -> list[RubricItemResult] - #[source] + #[source]
Grade on the objects this run produced and on what the file shows.
The two readings are independent and neither is reliably better, so they are combined by taking the better of them rather than by ranking them: a step counts as done if the executed run shows the right answer or the submitted notebook does. That is what stops a data path the grader could not resolve from turning a correct submission into a column of zeros.
- charts(ctx: GradingContext) -> chartlib.ChartEvidence - #[source] + #[source]
matplotlib figures, read once per submission and reused.
Rubric checks 22
@@ -143,145 +143,152 @@Classes
- check_eviction_data_loading(ctx: GradingContext, item: RubricItem) -> RubricItemResult - #[source] + #[source]
3 ptsstructural1.1.1 Eviction data loaded with geopandasrubrics/hw3.yaml
PA-tracts.geojson is read with geopandas from a relative path.
Config keys:
absolute_path_penaltyexpected_rowsmissing_read_penaltynot_geodataframe_penaltyrequired_importsrow_tolerancewrong_size_penalty
- check_philadelphia_tracts(ctx: GradingContext, item: RubricItem) -> RubricItemResult - #[source] + #[source]
5 ptsdeterministic1.1.2 Data trimmed to Philadelphiarubrics/hw3.yaml
The Pennsylvania tracts are trimmed to the 384 tracts of Philadelphia County. The file has no city column — the county lives in
pl("Philadelphia County, Pennsylvania") and in the 42101 prefix of GEOID.Config keys:
expected_rowsgeoid_prefiximpure_credit_ratioincomplete_credit_ratiomin_purity
- check_tidy_evictions(ctx: GradingContext, item: RubricItem) -> RubricItemResult - #[source] + #[source]
6 ptsdeterministic1.1.3 Melted to tidy formatrubrics/hw3.yaml
pd.melt() over the fourteen eviction columns e-03 to e-16, keeping GEOID and geometry as id_vars — 384 tracts x 14 years = 5,376 rows.
Config keys:
expected_rowsexpected_value_sumexpected_year_labelsmissing_geometry_credit_ratiorequired_id_columnsvalue_column_hintsvalue_sum_rel_tolwrong_year_span_credit_ratio
- check_evictions_by_year_plot(ctx: GradingContext, item: RubricItem) -> RubricItemResult - #[source] + #[source]
5 ptsdeterministic1.1.4 Yearly eviction trend plotted with hvplotrubrics/hw3.yaml
A groupby over the year column, summed, plotted with hvplot — any chart type. Graded on the numbers actually plotted.
Config keys:
accepted_elementsexpected_maxexpected_pointsexpected_totaltotal_rel_tolwrong_library_credit_ratiowrong_values_credit_ratio
- check_evictions_choropleth(ctx: GradingContext, item: RubricItem) -> RubricItemResult - #[source] + #[source]
5 ptsdeterministic1.1.5 Year-by-year eviction choropleth with a widgetrubrics/hw3.yaml
An hvplot choropleth of the tidy frame with
groupby=on the year column anddynamic=False, producing a HoloMap with one frame per year.Config keys:
dynamic_map_credit_ratioexpected_framesexpected_keyskey_dimension_hintsno_widget_credit_ratiopolygon_elementswrong_dimension_credit_ratio
- check_violations_choropleth(ctx: GradingContext, item: RubricItem) -> RubricItemResult - #[source] + #[source]
5 ptsdeterministic1.2.7 Choropleth per violation type with a widgetrubrics/hw3.yaml
An hvplot choropleth with
groupby=on the violation type anddynamic=False— a HoloMap with one frame per type.Config keys:
dynamic_map_credit_ratioexpected_frameskey_dimension_hintsno_widget_credit_ratiopolygon_elementswrong_dimension_credit_ratio
- check_violations_loading(ctx: GradingContext, item: RubricItem) -> RubricItemResult - #[source] + #[source]
4 ptsdeterministic1.2.1 Violations loaded and converted to a GeoDataFramerubrics/hw3.yaml
li_violations.csv read with pandas and converted to a GeoDataFrame of 434,052 points built from the lng/lat columns, with a CRS set.
Config keys:
bounds_toleranceexpected_boundsexpected_crs_epsgexpected_geometryexpected_rowsmissing_crs_credit_rationot_geodataframe_credit_ratiorow_toleranceswapped_coordinates_credit_ratio
- check_violations_subset(ctx: GradingContext, item: RubricItem) -> RubricItemResult - #[source] + #[source]
4 ptsdeterministic1.2.2 Trimmed to the fifteen maintenance violation typesrubrics/hw3.yaml
The listed fifteen violation types, which cover 34,108 of the 434,052 rows.
Config keys:
expected_rowsexpected_type_countpartial_types_credit_ratioviolation_types
- check_hexbin_map(ctx: GradingContext, item: RubricItem) -> RubricItemResult - #[source] + #[source]
4 ptsdeterministic1.2.3 Hex bin map with census tracts overlaidrubrics/hw3.yaml
matplotlib's hexbin() over the violation points with the tract boundaries drawn on top, in a projected CRS rather than EPSG:4326.
Config keys:
hexbin_patternsmissing_overlay_credit_rationot_rendered_credit_ratiooverlay_patternsreprojection_patternsunprojected_credit_ratio
- check_spatial_join(ctx: GradingContext, item: RubricItem) -> RubricItemResult - #[source] + #[source]
5 ptsdeterministic1.2.4 Violations spatially joined to census tractsrubrics/hw3.yaml
geopandas.sjoin() attaches a GEOID to each of the 34,108 violations. Every one of them falls inside a Philadelphia tract, so a correct join loses no rows and leaves no GEOID missing.
Config keys:
expected_rowsgeoid_prefixlossy_join_credit_ratiorequires_columnsrow_toleranceunfiltered_expected_rowsunfiltered_join_credit_ratio
- check_violations_by_tract(ctx: GradingContext, item: RubricItem) -> RubricItemResult - #[source] + #[source]
5 ptsdeterministic1.2.5 Violations counted per type and tractrubrics/hw3.yaml
A groupby over violation type and GEOID giving a three-column frame (violationdescription, GEOID, N) whose counts sum to 34,108.
Config keys:
allowed_row_countscount_column_hintsexpected_count_sumexpected_type_countpartial_grouping_credit_ratiorequired_columns
- check_merge_geometries(ctx: GradingContext, item: RubricItem) -> RubricItemResult - #[source] + #[source]
3 ptsdeterministic1.2.6 Counts merged back onto tract geometriesrubrics/hw3.yaml
pandas.merge() with the tract geometries first, so the result is a GeoDataFrame and not a plain DataFrame.
Config keys:
allowed_row_countsnot_geodataframe_credit_ratiorequire_geodataframe
- check_side_by_side(ctx: GradingContext, item: RubricItem) -> RubricItemResult - #[source] + #[source]
4 ptsdeterministic1.3 Evictions and violations side by siderubrics/hw3.yaml
2016 evictions and a single violation type, drawn as two choropleths and composed into one row of two columns.
Config keys:
eviction_total_rel_tolexpected_columnsexpected_eviction_year_totalnot_composed_credit_ratiopolygon_elementsstill_grouped_credit_ratiowrong_orientation_credit_ratiowrong_subset_credit_ratio
- check_landsat_loading(ctx: GradingContext, item: RubricItem) -> RubricItemResult - #[source] + #[source]
3 ptsdeterministic2.1.1 Landsat scene opened with rasteriorubrics/hw3.yaml
The ten-band Landsat 8 scene is opened with rasterio.
Config keys:
expected_array_shapeexpected_band_countexpected_crs_epsgexpected_heightexpected_widthwrong_raster_credit_ratio
- check_city_suburb_polygons(ctx: GradingContext, item: RubricItem) -> RubricItemResult - #[source] + #[source]
5 ptsdeterministic2.1.2 City and suburb polygons builtrubrics/hw3.yaml
The city limits polygon, and a suburbs polygon made by subtracting it from its own envelope. Both must be in the raster's CRS for the mask to work.
Config keys:
area_rel_tolenvelope_not_differenced_credit_ratioexpected_city_areaexpected_suburb_areaexpected_suburb_geom_typesmissing_suburbs_credit_ratiounprojected_credit_ratio
- check_ndvi_masking(ctx: GradingContext, item: RubricItem) -> RubricItemResult - #[source] + #[source]
6 ptsdeterministic2.1.3 Raster masked and NDVI computed for each arearubrics/hw3.yaml
rasterio.mask.mask() applied with each polygon, and NDVI computed from the red and near-infrared bands of the masked array.
Config keys:
expected_city_medianexpected_city_pixelsexpected_suburb_medianexpected_suburb_pixelsmask_patternsmedian_abs_tolone_area_credit_ratiopixel_rel_tolvalid_rangewrong_bands_credit_ratio
- check_ndvi_medians(ctx: GradingContext, item: RubricItem) -> RubricItemResult - #[source] + #[source]
4 ptsdeterministic2.1.4 Median NDVI compared between city and suburbsrubrics/hw3.yaml
np.nanmedian() over each NDVI array, printed, with the suburbs coming out greener than the city.
Config keys:
expect_suburbs_greaterexpected_city_medianexpected_suburb_medianmedian_abs_tolnan_result_credit_ratioprint_patternsright_direction_credit_ratio
- check_tree_data_loading(ctx: GradingContext, item: RubricItem) -> RubricItemResult - #[source] + #[source]
3 ptsdeterministic2.2.1 Street tree data loadedrubrics/hw3.yaml
The 2,480 street tree points are loaded with geopandas.
Config keys:
expected_geometryexpected_rowsraster_crs_epsgrow_toleranceunprojected_credit_ratio
- check_tree_ndvi(ctx: GradingContext, item: RubricItem) -> RubricItemResult - #[source] + #[source]
4 ptsdeterministic2.2.2 NDVI sampled at the tree locationsrubrics/hw3.yaml
rasterstats used to read the NDVI at each of the 2,480 points, giving one value per tree.
Config keys:
alternative_patternscount_toleranceexpected_countexpected_meanexpected_medianmax_nan_fractionmedian_abs_tolrasterstats_patternsvalid_rangewrong_values_credit_ratio
- check_tree_plots(ctx: GradingContext, item: RubricItem) -> RubricItemResult - #[source] + #[source]
4 ptsdeterministic2.2.3 Histogram and mapped tree pointsrubrics/hw3.yaml
A matplotlib histogram of the NDVI values with a vertical line at zero, and a geopandas plot of the tree points coloured by NDVI with the city limits drawn on it.
Config keys:
boundary_patternsdetail_sharehistogram_patternshistogram_sharenot_rendered_credit_ratiopoint_plot_patternszero_line_patterns
- check_tree_plot_styling(ctx: GradingContext, item: RubricItem) -> RubricItemResult - #[source] + #[source]
3 ptsqualitative2.2.3 Figure styling (labels, legend, colour choices)rubrics/hw3.yaml
The one item on this assignment that a person has to look at.
The assignment asks for figures that are "clear and well-styled", which is a statement about a picture. The signals below say where to look; they cannot say whether the picture reads, so this always comes back as manual review with a provisional score rather than a silent mark.
Config keys:
provisional_ceiling_ratioweighted_signals
- check_common_violations_extra_credit(ctx: GradingContext, item: RubricItem) -> RubricItemResult - #[source] + #[source]
0 ptsdeterministicExtra credit: the twenty most common violation typesrubrics/hw3.yaml
The twenty most common violation types over 2012-2016 mapped as a second set of interactive choropleths, with three types named as having little spatial overlap with evictions. Worth no points in the rubric total; when it is found, the item is flagged so a TA can award the bonus by hand.
Config keys:
expected_count_sumexpected_framesmin_discussion_wordssuggested_points