library(tidyverse)
solar_path <- "https://bcdanl.github.io/data/nyserda_solar_county_year_2026-06-30.csv"
solar <- read_csv(
solar_path,
show_col_types = FALSE
) |>
mutate(
data_through_date = as.Date(data_through_date),
interconnection_year = as.integer(interconnection_year)
)Classwork 2
Audit Solar Adoption Before Comparing Places
🎯 Research studio goal
This classwork applies Lecture 3: Solar Adoption Data.
Your final artifact is:
- one audited statewide annual summary;
- one ten-county table and figure for 2025;
- one defensible descriptive sentence; and
- one explicit statement of the missing denominator.
Work with a partner. One person drives the code while the other predicts the row grain and audits each denominator; switch roles before Part 5.
By the end, you should be able to:
- distinguish observed solar deployment from technical potential;
- audit a geographic aggregate before comparing places;
- separate project counts, project shares, capacity, and adoption rates;
- recognize a partial year and an unexpected geography; and
- write a claim that stays within the available evidence.
📥 Data and provenance
We will use the course extract nyserda_solar_county_year_2026-06-30.csv.
It is a county-by-year aggregation of NYSERDA’s open Statewide Distributed Solar Projects: Beginning 2000. The source describes interconnected distributed-solar projects; it does not measure every eligible roof or household.
The extract:
- is based on data through June 30, 2026;
- has no project IDs, addresses, ZIP codes, developers, substations, or coordinates;
- contains 1,283 county-year rows; and
- does not contain Google Solar API data.
| Variable | Meaning |
|---|---|
data_through_date |
NYSERDA snapshot date |
interconnection_year |
Calendar year of final grid interconnection |
county |
County label reported by the source |
source_rows |
Project-level source rows contributing to the aggregate |
projects |
Sum of number_of_projects in those source rows |
capacity_records |
Source rows with reported estimated capacity |
capacity_kwdc |
Sum of estimated system size, in kWdc |
production_records |
Source rows with reported estimated production |
estimated_annual_kwh |
Sum of estimated annual production, in kWh |
projects is an observed deployment count, not a household or building adoption rate. The extract contains no denominator for eligible roofs, buildings, households, or firms. Also, 2026 is incomplete because the source stops on June 30.
Setup
Run this code once.
Before summarizing, complete the sentence:
One row represents one __________________ × __________________ aggregate.
🪜 Part 1 · Name the outcome before analyzing it
Classify each measure as technical potential, observed deployment, context, or measurement coverage.
| Measure | Classification |
|---|---|
| Modeled maximum panel count on a roof | __________________ |
| Interconnected NYSERDA project count | __________________ |
| Number of owner-occupied households | __________________ |
| Valid Google API building-match rate | __________________ |
Write: Why is projects closer to observed deployment than to an adoption rate?
🧪 Part 2 · Audit before comparing places
2A. Produce one audit row
Predict the output grain, replace the blanks, and run the audit.
solar |>
summarise(
rows = ___,
counties = n_distinct(___),
first_year = min(___),
last_year = max(___),
source_rows = sum(___),
projects = sum(___),
capacity_missing_records = sum(___ - ___),
production_missing_records = sum(___ - ___)
)Discuss:
- Why are
source_rowsandprojectsnot exactly equal? - Which result warns you that one capacity value is absent?
- Why does one audit row not change the observational unit of
solaritself?
2B. Check the intended key
If one row should be one county × interconnection year, complete a pipeline that returns repeated keys only.
solar |>
count(___, ___) |>
filter(___ > 1)Write: What can an empty result establish, and what can it not establish?
2C. Inspect geography and completeness
List every county label, then inspect aggregates with incomplete capacity or production records.
solar |>
distinct(___) |>
arrange(___)
solar |>
filter(
___ < ___ |
___ < ___
)Identify:
- the county label that is not a New York county; and
- the county-year with one missing capacity record.
🗓️ Part 3 · Define a comparable analysis window
Create solar_complete for New York counties through the last complete calendar year. Document both exclusions in the code.
solar_complete <- solar |>
filter(
interconnection_year <= ___, # exclude partial 2026
county != ___ # flag the out-of-state label
)Answer before continuing:
- Why would comparing all of 2025 with January–June 2026 be misleading?
- Why should the unexpected geography be documented rather than silently removed?
📈 Part 4 · Change the grain to one statewide year
Build one row per year. Keep the number of represented counties and the missing-capacity audit visible.
state_year <- solar_complete |>
group_by(___) |>
summarise(
projects = sum(___),
capacity_mw = sum(___) / 1000,
counties = n_distinct(___),
capacity_missing_records = sum(___ - ___),
.groups = "drop"
) |>
mutate(
average_kwdc_per_project = 1000 * ___ / ___
)
state_yearBefore plotting, define the row grain:
One row in
state_yearrepresents ____________________________________________.
Then create a trend figure through 2025.
ggplot(state_year, aes(x = ___, y = ___)) +
geom_col(fill = "#1c5d99") +
labs(
title = "New York distributed-solar projects by interconnection year",
subtitle = "Observed project counts; 2026 excluded as a partial year",
x = NULL,
y = "Interconnected projects",
caption = "Source: NYSERDA statewide distributed-solar projects; data through 2026-06-30"
) +
theme_minimal(base_size = 13)Write: What changed—the data’s row grain, the underlying project records, or both?
🗺️ Part 5 · Compare counties without inventing an adoption rate
The input already contains one row per county-year. Filter to 2025, compute each county’s share of statewide projects, and order counties by project count.
county_2025 <- solar_complete |>
filter(___ == ___) |>
mutate(
statewide_projects = sum(___),
project_share = ___ / ___,
capacity_mw = ___ / 1000,
average_kwdc_per_project = ___ / ___
) |>
arrange(desc(___))
final_table <- county_2025 |>
slice_head(n = ___) |>
transmute(
county,
projects,
project_share_percent = round(100 * ___, 1),
capacity_mw = round(___, 1),
average_kwdc_per_project = round(___, 1)
)
final_tableAnswer in words:
- What is the denominator of
project_share? - Why is
project_sharenot a county adoption rate? - Why can a county rank differently on projects and capacity?
📊 Part 6 · Produce the county figure and claim
Create a horizontal bar chart from final_table.
ggplot(
final_table,
aes(x = reorder(___, ___), y = ___)
) +
geom_col(fill = "#2e7d5b") +
coord_flip() +
labs(
title = "Ten counties with the most interconnected projects in 2025",
subtitle = "Counts, not rates; no eligible-building denominator is available",
x = NULL,
y = "Projects",
caption = "Source: NYSERDA statewide distributed-solar projects; data through 2026-06-30"
) +
theme_minimal(base_size = 13)Repair this claim:
Suffolk had the highest solar-adoption rate in New York in 2025.
Use this structure:
In the NYSERDA extract, ________ recorded ________ interconnected distributed-solar projects in 2025 (________% of the statewide project total). This describes ____________________, not ____________________, because ____________________.
🛡️ Part 7 · Peer audit
Exchange the table, figure, and sentence with another pair. Identify the exact code or wording that addresses each item.
Revise one line of code, one label, or one phrase after the audit. Record what changed and why.
🔭 Optional extension · Ask what an adoption rate would require
Choose one possible denominator:
- households;
- owner-occupied households;
- residential buildings;
- technically suitable roofs; or
- electricity customers.
Write a proposed rate and list the additional source, geography, year, and join key you would need. Then explain one selection problem that would remain.
✅ Exit ticket
Submit or save:
- the audit row;
final_table;- the county figure;
- the revised one-sentence claim; and
- this completion:
The denominator changes the meaning of my result because ________________________________.