The last deck in the course, and a practical one. Everything so far assumed the data was already sitting on your machine. This is about getting it, without a browser and without clicking through a download form, which matters for two reasons. Downloading by hand does not scale past a few files, and it is not reproducible, so nobody can rerun your work and get your dataset. We cover four sources that between them cover a great deal of applied agricultural economics: administrative boundaries, USDA survey statistics, weather, and satellite-derived land cover.
The learning objective on screen is therefore deliberately direct: you should leave knowing how to download public agriculture-related data from inside R. The numbered table of contents previews the four workflows and each item is a link you can use to jump straight to that section. Before you start, make sure the ggplot2 and dplyr material in the prerequisite links is familiar. We will repeatedly filter downloaded objects, pipe operations together, and turn spatial results into maps, so those two skills are the foundation underneath the new download functions.
Learn how to download publicly available agriculture-related data from within R.
One important difference on this deck. Most of the code here is shown but not run, because it downloads from servers that will not answer a request coming from inside your browser, and some of it needs an API key that is yours rather than mine. So this deck is closer to a reference you will come back to than a set of cells to click through. Read the syntax, then try it in RStudio on your own machine, where it genuinely works. The pieces that do run in the browser are marked by having a Run button that does something.
For navigation, use the three-line menu at the bottom left to open the table of contents, or press the letter o to see an overview of all slides and jump visually. In a runnable cell, the pale blue area is the code area and Run Code evaluates everything in it. To run only part, highlight that part and press Command Enter on a Mac or Control Enter on Windows. The two-sheet icon copies the cell so you can paste it into RStudio, and the reload icon beside it restores the original code after you experiment. Those controls let you test examples without permanently changing the lecture notes. One more, at the right end of the toolbar: the eye icon hides that cell’s output and a second click brings it back. Some of these results run long, and once you have read one it is just pushing the rest of the slide out of view.
Click on the three horizontally stacked lines at the bottom left corner of the slide, then you will see table of contents, and you can jump to the section you want
Hit letter “o” on your keyboard and you will have a panel view of all the slides
County and state boundaries are the most reused spatial data in applied economics, since almost every administrative dataset is reported by county. The tigris package fetches them straight from the Census Bureau. It does far more than boundaries too, offering roads, water, tracts, block groups and a good deal else, so the link is worth following when you need something specific. The important thing is that a boundary file becomes one line of code rather than a download, an unzip, and a folder you have to remember the location of.
U.S. county and state boundary data are commonly used in many scientific studies.
The tigris package is one of the packages that let you download them from within R.
It lets you download much more than just county and state boundaries. See the other types of data here
One function call. But look closely at the cb argument, because it matters more than it looks. Without it you get the full-detail boundary, which is very large and slow to draw, since it traces every inlet and river bend. With cb set to TRUE you get a generalised version, and for almost any map you will make, that is the right choice. Full detail is worth its cost only if you are doing precise spatial operations near a coastline. Note also progress bar set to FALSE, which just quiets the output.
The namespace in tigris colon-colon states means you can call the function without attaching the whole package. The assignment saves the returned sf object as states underscore s-f, so it contains state attributes together with the geometry needed for mapping. The second code block passes that object to ggplot, geom s-f draws its state polygons, and theme void removes axes and background decoration that do not help on a boundary map. The result beneath the code is your quick visual check that the download succeeded and the generalized state shapes are ready to use.
You can use tigris::states() to download the state boundary data as an sf object.
By default, the most detailed boundary data is downloaded, which can be quite large
ggplot() can take significantly more timeBy adding cb = TRUE, you will get generalized (less detailed) boundary data, which is usually sufficient.
The counties function works the same way, with a state argument that takes abbreviations or names and accepts several at once. The map at the end is worth a moment: county boundaries drawn underneath, then the two state outlines over the top in blue and thicker. That is the layering from the mapping deck used for something practical, checking that two datasets you downloaded separately actually line up. If the state outlines had not sat exactly on the county edges, you would know something was wrong immediately, usually because the boundary vintages differ or the CRS metadata is incorrect or missing.
Here the state argument receives a vector containing Illinois and Indiana, cb asks for generalized county boundaries, and progress bar false keeps the download quiet. The result is stored as I-L underscore I-N underscore county. In the map, the empty ggplot call is useful because each geometry layer supplies its own data. The first geom s-f draws every downloaded county. The second filters the earlier state object so the name is Illinois or Indiana, sets fill to N-A so those polygons stay transparent, and uses blue with linewidth one to make only their outer borders prominent. Theme void then removes the non-map furniture.
You can use tigris::counties() to download the county boundary data as an sf object.
You can specify states by the state option.
Quick Stats is the USDA’s statistical warehouse, and it holds a great deal: acres planted and harvested, yields, prices, livestock counts, by state and by county, from both the annual surveys and the five-yearly census. The tidyUSDA package wraps its API. The feature that makes it worth using over the raw API is the geometry argument, which returns the data already joined to boundaries as an sf object, so you can map it or spatially join it immediately, with no separate boundary download and no merge to get wrong. That saves a surprising amount of tedious work.
USDA NASS Quick Stats provides a wealth of agriculture-related datasets such as harvested acres or irrigated acres by crop at different spatial resolutions (e.g., state, county) from both survey and census.
We use the tidyUSDA package to download data from USDA NASS Quick Stats.
A nice thing about tidyUSDA is that it gives you an option to download data as an sf object, which means you can immediately visualize the data or spatially interact it with other spatial objects.
Three tabs, and the middle one is the reference you will keep coming back to. First, the API key, which you get free from the Quick Stats site. Read the callout about it carefully, because a key is a credential and pasting it into your script publishes it to anyone who sees the file. The third tab solves a real problem: variable names in Quick Stats are long, exact and unguessable, so rather than trying to remember them you search the full list with grep, narrowing by keyword, and copy exactly what you find into your query.
On the API key tab, follow the link, choose Obtain an API key, and save the result somewhere secure because every tidyUSDA request needs it. On Download data, getQuickstat is the central function. Key supplies that credential. Program chooses Survey or Census. Data item is the exact variable label. Geographic level chooses units such as County or State, state and year can each be vectors, and year must be character data. Geometry true asks for an sf result with boundaries; geometry false returns an ordinary data frame. The help callout matters because these are the common arguments, not the complete interface, so question-mark getQuickstat is where you look for less common filters.
On Identify data item, tidyUSDA colon-colon allDataItem supplies the complete name list and head shows a small preview. The pipe then sends that list through three grep calls. Value true returns the matching names themselves, while the dot means use the piped result as the text to search. Keeping names containing CORN, then YIELD, then IRRIGATED progressively narrows the list to the requested irrigated grain-corn yield measure. Copy the exact result into data item rather than retyping it. The next tab uses that complete name in a real query.
tidyUSDA.You can download data using tidyUSDA::getQuickstat().
Syntax
key: API keyprogram: either “Survey” or “Census”data_item: name of the variable to downloadgeographic_level: set the level of geographical unit (“County”, “State”)state: vector of statesyear: vector of years in charactergeometry: if TRUE, then the downloaded data will be sf with geometry included. If false, a data.frame without geometry is returned.Note
?getQuickstat to see all the options.Sometimes you know what you would like to download, but not the name of the variable for it. In such a case, you can first get a list of all the data item names with this:
You can then narrow down the list using keywords. Suppose you are interested in getting irrigated grain corn yield measured in bu/acre.
You can now copy the first entry of the results and paste it for data_item option.
A concrete query, and it reads much like the syntax page: the variable, the geography, the states, the years, and geometry set to TRUE. What comes back has a great many columns, most of which you will never use, so the second tab shows the handful that matter. The one to know is Value, which is where the number you actually asked for lives, whatever variable you requested. That naming is a quirk of the API rather than anything sensible, and it catches everyone exactly once. Select the handful of columns you need straight after downloading.
Start with the important callout. Nass underscore api underscore key is read from outside the document, commonly from NASS_API_KEY in your dot Renviron. The alternative shown is keyring colon-colon key_get. Either approach prevents the credential from being copied into the rendered HTML, committed to git history, or published with the deck.
The query requests the Survey program, the exact irrigated grain-corn yield item, county geography, Illinois and Nebraska, and the years 2000 through 2005. As character converts that numeric sequence to the form the API expects, and geometry true requests county shapes along with the records. The outer parentheses around the assignment make R print the returned object when you run this locally, which lets you inspect it immediately. The displayed copy below the code shows why cleanup follows.
On Select variables, the pipe passes the downloaded object into dplyr select. Year identifies the observation date. County name and county code identify the county, while state name and state FIPS code identify its state. Short description records the requested statistic, and Value holds the measurement. Keeping these fields makes the result easier to inspect and work with while preserving the information needed to identify, map, and interpret each observation.
The code below download county-level irrigated grain corn yield (bu/acre) in Illinois and Nebraska from 2000 through 2005.
Do not paste your key into the script
An API key is a credential. Written into a .qmd it ends up in the rendered HTML, in your git history, and on any website you publish the deck to. Keep it outside the document and read it in:
Sys.getenv("NASS_API_KEY"), with the key set in your .Renviron, orkeyring::key_get("usda_nass_qs_api"), which is what the Caveat 2 tab uses.Both leave nothing quotable in the file you share.
(
IL_NE_ir_corn_yield <-
tidyUSDA::getQuickstat(
key = nass_api_key, # from .Renviron — never the key itself
program = "SURVEY",
data_item = "CORN, GRAIN, IRRIGATED - YIELD, MEASURED IN BU / ACRE",
geographic_level = "COUNTY",
state = c("ILLINOIS", "NEBRASKA"),
year = as.character(2000:2005),
geometry = TRUE
)
)Simple feature collection with 546 features and 57 fields (with 6 geometries empty)
Geometry type: MULTIPOLYGON
Dimension: XY
Bounding box: xmin: -104.0535 ymin: 39.99993 xmax: -95.30829 ymax: 43.00171
Geodetic CRS: NAD83
First 10 features:
GEOID year ALAND unit_desc
1 31007 2005 1932231568 BU / ACRE
2 31007 2004 1932231568 BU / ACRE
3 31007 2003 1932231568 BU / ACRE
4 31007 2002 1932231568 BU / ACRE
5 31007 2001 1932231568 BU / ACRE
6 31007 2000 1932231568 BU / ACRE
7 31013 2005 2784618473 BU / ACRE
8 31013 2004 2784618473 BU / ACRE
9 31013 2003 2784618473 BU / ACRE
10 31013 2002 2784618473 BU / ACRE
short_desc Value region_desc
1 CORN, GRAIN, IRRIGATED - YIELD, MEASURED IN BU / ACRE 165
2 CORN, GRAIN, IRRIGATED - YIELD, MEASURED IN BU / ACRE 160
3 CORN, GRAIN, IRRIGATED - YIELD, MEASURED IN BU / ACRE 142
4 CORN, GRAIN, IRRIGATED - YIELD, MEASURED IN BU / ACRE 127
5 CORN, GRAIN, IRRIGATED - YIELD, MEASURED IN BU / ACRE 143
6 CORN, GRAIN, IRRIGATED - YIELD, MEASURED IN BU / ACRE 125
7 CORN, GRAIN, IRRIGATED - YIELD, MEASURED IN BU / ACRE 177
8 CORN, GRAIN, IRRIGATED - YIELD, MEASURED IN BU / ACRE 151
9 CORN, GRAIN, IRRIGATED - YIELD, MEASURED IN BU / ACRE 168
10 CORN, GRAIN, IRRIGATED - YIELD, MEASURED IN BU / ACRE 147
prodn_practice_desc county_code statisticcat_desc state_alpha asd_code
1 IRRIGATED 007 YIELD NE 10
2 IRRIGATED 007 YIELD NE 10
3 IRRIGATED 007 YIELD NE 10
4 IRRIGATED 007 YIELD NE 10
5 IRRIGATED 007 YIELD NE 10
6 IRRIGATED 007 YIELD NE 10
7 IRRIGATED 013 YIELD NE 10
8 IRRIGATED 013 YIELD NE 10
9 IRRIGATED 013 YIELD NE 10
10 IRRIGATED 013 YIELD NE 10
country_name zip_5 agg_level_desc state_ansi CV (%) county_name
1 UNITED STATES COUNTY 31 BANNER
2 UNITED STATES COUNTY 31 BANNER
3 UNITED STATES COUNTY 31 BANNER
4 UNITED STATES COUNTY 31 BANNER
5 UNITED STATES COUNTY 31 BANNER
6 UNITED STATES COUNTY 31 BANNER
7 UNITED STATES COUNTY 31 BOX BUTTE
8 UNITED STATES COUNTY 31 BOX BUTTE
9 UNITED STATES COUNTY 31 BOX BUTTE
10 UNITED STATES COUNTY 31 BOX BUTTE
watershed_code reference_period_desc domaincat_desc country_code week_ending
1 00000000 YEAR NOT SPECIFIED 9000
2 00000000 YEAR NOT SPECIFIED 9000
3 00000000 YEAR NOT SPECIFIED 9000
4 00000000 YEAR NOT SPECIFIED 9000
5 00000000 YEAR NOT SPECIFIED 9000
6 00000000 YEAR NOT SPECIFIED 9000
7 00000000 YEAR NOT SPECIFIED 9000
8 00000000 YEAR NOT SPECIFIED 9000
9 00000000 YEAR NOT SPECIFIED 9000
10 00000000 YEAR NOT SPECIFIED 9000
class_desc state_name state_fips_code watershed_desc congr_district_code
1 ALL CLASSES NEBRASKA 31
2 ALL CLASSES NEBRASKA 31
3 ALL CLASSES NEBRASKA 31
4 ALL CLASSES NEBRASKA 31
5 ALL CLASSES NEBRASKA 31
6 ALL CLASSES NEBRASKA 31
7 ALL CLASSES NEBRASKA 31
8 ALL CLASSES NEBRASKA 31
9 ALL CLASSES NEBRASKA 31
10 ALL CLASSES NEBRASKA 31
location_desc load_time end_code source_desc
1 NEBRASKA, NORTHWEST, BANNER 2012-01-01 00:00:00.000 00 SURVEY
2 NEBRASKA, NORTHWEST, BANNER 2012-01-01 00:00:00.000 00 SURVEY
3 NEBRASKA, NORTHWEST, BANNER 2012-01-01 00:00:00.000 00 SURVEY
4 NEBRASKA, NORTHWEST, BANNER 2012-01-01 00:00:00.000 00 SURVEY
5 NEBRASKA, NORTHWEST, BANNER 2012-01-01 00:00:00.000 00 SURVEY
6 NEBRASKA, NORTHWEST, BANNER 2012-01-01 00:00:00.000 00 SURVEY
7 NEBRASKA, NORTHWEST, BOX BUTTE 2012-01-01 00:00:00.000 00 SURVEY
8 NEBRASKA, NORTHWEST, BOX BUTTE 2012-01-01 00:00:00.000 00 SURVEY
9 NEBRASKA, NORTHWEST, BOX BUTTE 2012-01-01 00:00:00.000 00 SURVEY
10 NEBRASKA, NORTHWEST, BOX BUTTE 2012-01-01 00:00:00.000 00 SURVEY
county_ansi freq_desc sector_desc group_desc util_practice_desc begin_code
1 007 ANNUAL CROPS FIELD CROPS GRAIN 00
2 007 ANNUAL CROPS FIELD CROPS GRAIN 00
3 007 ANNUAL CROPS FIELD CROPS GRAIN 00
4 007 ANNUAL CROPS FIELD CROPS GRAIN 00
5 007 ANNUAL CROPS FIELD CROPS GRAIN 00
6 007 ANNUAL CROPS FIELD CROPS GRAIN 00
7 013 ANNUAL CROPS FIELD CROPS GRAIN 00
8 013 ANNUAL CROPS FIELD CROPS GRAIN 00
9 013 ANNUAL CROPS FIELD CROPS GRAIN 00
10 013 ANNUAL CROPS FIELD CROPS GRAIN 00
asd_desc domain_desc commodity_desc COUNTYKEY STATEFP COUNTYFP COUNTYNS
1 NORTHWEST TOTAL CORN 31007 31 007 00835826
2 NORTHWEST TOTAL CORN 31007 31 007 00835826
3 NORTHWEST TOTAL CORN 31007 31 007 00835826
4 NORTHWEST TOTAL CORN 31007 31 007 00835826
5 NORTHWEST TOTAL CORN 31007 31 007 00835826
6 NORTHWEST TOTAL CORN 31007 31 007 00835826
7 NORTHWEST TOTAL CORN 31013 31 013 00835991
8 NORTHWEST TOTAL CORN 31013 31 013 00835991
9 NORTHWEST TOTAL CORN 31013 31 013 00835991
10 NORTHWEST TOTAL CORN 31013 31 013 00835991
NAME NAMELSAD LSAD CLASSFP MTFCC CSAFP CBSAFP METDIVFP FUNCSTAT
1 Banner Banner County 06 H1 G4020 <NA> <NA> <NA> A
2 Banner Banner County 06 H1 G4020 <NA> <NA> <NA> A
3 Banner Banner County 06 H1 G4020 <NA> <NA> <NA> A
4 Banner Banner County 06 H1 G4020 <NA> <NA> <NA> A
5 Banner Banner County 06 H1 G4020 <NA> <NA> <NA> A
6 Banner Banner County 06 H1 G4020 <NA> <NA> <NA> A
7 Box Butte Box Butte County 06 H1 G4020 <NA> <NA> <NA> A
8 Box Butte Box Butte County 06 H1 G4020 <NA> <NA> <NA> A
9 Box Butte Box Butte County 06 H1 G4020 <NA> <NA> <NA> A
10 Box Butte Box Butte County 06 H1 G4020 <NA> <NA> <NA> A
AWATER INTPTLAT INTPTLON geometry
1 392073 +41.5397495 -103.7262626 MULTIPOLYGON (((-104.0529 4...
2 392073 +41.5397495 -103.7262626 MULTIPOLYGON (((-104.0529 4...
3 392073 +41.5397495 -103.7262626 MULTIPOLYGON (((-104.0529 4...
4 392073 +41.5397495 -103.7262626 MULTIPOLYGON (((-104.0529 4...
5 392073 +41.5397495 -103.7262626 MULTIPOLYGON (((-104.0529 4...
6 392073 +41.5397495 -103.7262626 MULTIPOLYGON (((-104.0529 4...
7 7150304 +42.2103804 -103.0817795 MULTIPOLYGON (((-103.4444 4...
8 7150304 +42.2103804 -103.0817795 MULTIPOLYGON (((-103.4444 4...
9 7150304 +42.2103804 -103.0817795 MULTIPOLYGON (((-103.4444 4...
10 7150304 +42.2103804 -103.0817795 MULTIPOLYGON (((-103.4444 4...
As you saw earlier, it has 58 columns, most of which are not necessary.
Here is the list of only variables you will probably need:
IL_NE_ir_corn_yield %>%
dplyr::select(
year, county_name, county_code, state_name,
state_fips_code, short_desc, Value
)Simple feature collection with 546 features and 7 fields (with 6 geometries empty)
Geometry type: MULTIPOLYGON
Dimension: XY
Bounding box: xmin: -104.0535 ymin: 39.99993 xmax: -95.30829 ymax: 43.00171
Geodetic CRS: NAD83
First 10 features:
year county_name county_code state_name state_fips_code
1 2005 BANNER 007 NEBRASKA 31
2 2004 BANNER 007 NEBRASKA 31
3 2003 BANNER 007 NEBRASKA 31
4 2002 BANNER 007 NEBRASKA 31
5 2001 BANNER 007 NEBRASKA 31
6 2000 BANNER 007 NEBRASKA 31
7 2005 BOX BUTTE 013 NEBRASKA 31
8 2004 BOX BUTTE 013 NEBRASKA 31
9 2003 BOX BUTTE 013 NEBRASKA 31
10 2002 BOX BUTTE 013 NEBRASKA 31
short_desc Value
1 CORN, GRAIN, IRRIGATED - YIELD, MEASURED IN BU / ACRE 165
2 CORN, GRAIN, IRRIGATED - YIELD, MEASURED IN BU / ACRE 160
3 CORN, GRAIN, IRRIGATED - YIELD, MEASURED IN BU / ACRE 142
4 CORN, GRAIN, IRRIGATED - YIELD, MEASURED IN BU / ACRE 127
5 CORN, GRAIN, IRRIGATED - YIELD, MEASURED IN BU / ACRE 143
6 CORN, GRAIN, IRRIGATED - YIELD, MEASURED IN BU / ACRE 125
7 CORN, GRAIN, IRRIGATED - YIELD, MEASURED IN BU / ACRE 177
8 CORN, GRAIN, IRRIGATED - YIELD, MEASURED IN BU / ACRE 151
9 CORN, GRAIN, IRRIGATED - YIELD, MEASURED IN BU / ACRE 168
10 CORN, GRAIN, IRRIGATED - YIELD, MEASURED IN BU / ACRE 147
geometry
1 MULTIPOLYGON (((-104.0529 4...
2 MULTIPOLYGON (((-104.0529 4...
3 MULTIPOLYGON (((-104.0529 4...
4 MULTIPOLYGON (((-104.0529 4...
5 MULTIPOLYGON (((-104.0529 4...
6 MULTIPOLYGON (((-104.0529 4...
7 MULTIPOLYGON (((-103.4444 4...
8 MULTIPOLYGON (((-103.4444 4...
9 MULTIPOLYGON (((-103.4444 4...
10 MULTIPOLYGON (((-103.4444 4...
Note
Value column.Two ways this API will refuse you, both worth knowing before they happen. The first is a hard limit of fifty thousand rows per query, so a request spanning many states and many years simply fails, and the answer is to break it into smaller pieces. The second is subtler: asking for a combination that exists in general but has no observations for your particular state and year returns an error rather than an empty result. That is why an apparently reasonable query can fail, and it is not your syntax.
In Caveat 1, the broad commodity filter asks for corn rather than one exact data item, at county level, across five states and every year from 1995 through 2018. That combination is intentionally too large. As character again supplies the years in the required form, and geometry true would add county geometry to the result, but the server rejects the request before returning it. The code is displayed without evaluation because you need your own key and there is no useful reason to repeat a known failing request from the slide.
In Caveat 2, keyring retrieves the credential without exposing it in the script. The remaining arguments request one exact Survey item, at county level, only for Illinois in 2018, with geometry. The item is valid, but that state-year combination has no matching observation, so this query also errors. Distinguishing an absent combination from a misspelled item is why it helps to search allDataItem first and then check the state and year coverage separately.
You cannot retrieve more than 50,000 (the limit is set by QuickStat) rows of data. The query below requests far more than 50,000 observations, and fails. In this case, you need to narrow the search and chop the task into smaller tasks.
Replace nass_api_key with your own API key and run the code on your computer.
A query returns an error when no observation satisfies your query criteria. For example, even though “CORN, GRAIN, IRRIGATED - YIELD, MEASURED IN BU / ACRE” does exists as a data_item, there is no entry for the statistic in Illinois in 2018. Therefore, the following query fails.
The direct consequence of the row limit. If one query is too big, split it along a dimension and run several, then stack the results. Year is the natural choice here, though state would work as well. The code uses lapply over a vector of years, which is Chapter 5 material applied to something real, and then bind_rows to stitch the pieces back into a single sf object. Note that this is also a courtesy to the server: several modest requests are treated far more kindly than one enormous one.
The Motivation tab points back to the fifty-thousand-row caveat so you can see what this pattern solves. On How, as character turns 2015 through 2018 into the character vector required by Quick Stats. The short range keeps the demonstration manageable; you can extend year underscore list for your actual study period.
Lapply takes that vector one element at a time. Inside the anonymous function, x is the current year, while your external API key, the Survey program, broad corn commodity, county geography, five-state vector, and geometry true stay fixed for every request. The result of lapply is therefore a list with one downloaded spatial object per year. The pipe passes that list to dplyr bind_rows, which stacks matching columns into one sf object. You have split only the request, not the final dataset, so the combined result can be handled just like a single successful download. The exercise next gives you a smaller end-to-end query to build yourself.
There are two dimensions that seem easy to loop over: state and year. Here, let’s loop over year.
We first create a sequence of years to loop over one by one:
We now download the data year by year with lapply():
lapply(
year_list, # list of objects to loop over
\(x) {
getQuickstat(
key = nass_api_key,
program = "SURVEY",
commodity = "CORN",
geographic_level = "COUNTY",
state = c("ILLINOIS", "COLORADO", "NEBRASKA", "IOWA", "KANSAS"),
year = x, # use the year
geometry = TRUE
)
}
) %>%
# combine a list of sf into a single sf
dplyr::bind_rows()Three tabs that walk the whole workflow end to end, which is the point of doing them in order. Find the variable name by searching, then download it for two states across five years, then map the result faceted by year. That is the complete arc from not knowing what a thing is called to having a figure. Do run these on your own machine rather than reading the answers, because the searching step in particular feels awkward until you have done it once yourself. After that it is routine, and you will use it every time you touch this API.
In the first answer, allDataItem is piped through grep for SOY and then HARVESTED, with value true so you see the actual candidate names. That leads to SOYBEANS minus ACRES HARVESTED as the exact data item in the next tab. The download uses your external API key, the Survey program, county geography, Kansas and Colorado, character years 1990 through 1994, and geometry true. It immediately selects the year, county and state identifiers, the short description, and Value, leaving you a focused mapping dataset rather than the full API response.
In the final answer, ggplot starts without global data and geom s-f supplies K-S underscore C-O underscore soy h-acres directly. Mapping Value to fill turns the county measurement into color. Facet wrap creates a separate panel for each year, so you can compare the geographic pattern across the five dates, and theme void removes axes and background decoration. The answers are folded on screen so attempt each step first, then open the code to compare the structure and the argument choices with your own.
You are interested in getting soybean harvested acres data. Search for the data_item name for this variable from tidyUSDA::allDataItem
Answer
Now, using the data_item name you got earlier, download the county-level data for Colorado and Kansas from 1990 through 1994 as an sf object.
Answer
KS_CO_soy_hacres <-
getQuickstat(
key = nass_api_key,
program = "SURVEY",
data_item = "SOYBEANS - ACRES HARVESTED",
geographic_level = "COUNTY",
state = c("KANSAS", "COLORADO"),
year = as.character(1990:1994),
geometry = TRUE
) %>%
dplyr::select(
year, county_name, county_code, state_name,
state_fips_code, short_desc, Value
)PRISM is the standard source for United States weather in applied work: daily precipitation and maximum and minimum temperature, modelled onto a four kilometre grid covering the whole country. It is raster data, so everything from the raster deck applies, and the map on this slide is a single day of maximum temperature. If you need weather as a control variable, or you are estimating anything at all about climate and agriculture, this is very likely where your data will come from. It is free, and it covers several decades at daily resolution.
The source description also includes daily mean temperature, alongside precipitation and minimum and maximum temperature. The displayed example reads the BIL raster for maximum temperature on August first, 2012. Geom spatraster draws the grid cells, the continuous viridis fill scale turns their values into an ordered color gradient, and theme void removes axes and background elements so the spatial temperature pattern is the focus. This output is also a reminder that a PRISM download is not a table with one row per county. It is a surface made of cells, which is why you read and process it with terra.
The PRISM dataset provides model-based estimates of daily precipitation, maximum temperature, and minimum temperature for the U.S. at the 4km by 4km spatial resolution.

One function fetches a date range, and the four arguments are all it takes. Type is the variable, using the abbreviations listed. The two dates bound the range, in year-month-day order. And keepZip decides whether the compressed downloads are kept after unpacking, which you usually do not want. Read the note about setting the download directory first, and note the modern spelling of it, since older code you find online will use an options call instead. Both do the same job, but the function is the current spelling and is easier to remember.
You can use get_prism_dailys() from the prism package to download PRISM data.
Syntax
type: you can select from “ppt” (precipitation), “tmean” (mean temperature), “tmin” (minimum temperature), and “tmax” (maximum temperature).minDate: starting date specified in format YYYY-MM-DDmaxDate: end date specified in format YYYY-MM-DDkeepZip: if FALSE, the zipped folders of the downloaded files will not be kept; otherwise, they will be kept.Before you download PRISM data, set the folder the files should be stored in:
You will see the old form in older code
Older material sets this with options(prism.path = "path"). The prism package replaced that with prism_set_dl_dir(), together with prism_get_dl_dir() and prism_check_dl_dir(), because remembering a function name is easier than remembering an option name. If you meet the options() form in someone else’s script, it is the same idea.
This one you genuinely have to run yourself, since it writes files to disk. Five days of precipitation is enough to see the shape of the thing. Pay attention to what you end up with, because it is the reason the next tab exists: one folder per day, each holding several files that share a name and differ only by extension. That is the BIL format from the raster deck, where the values, the header describing the grid, and the projection information all live in separate files that have to stay together in the same folder.
The first block attaches prism and sets lectures slash Data slash PRISM as the download directory. Change that path to a location on your own computer before running it. The second block asks get_prism_dailys for ppt, the package abbreviation for precipitation, from January first through January fifth of 2024. KeepZip false tells prism not to retain the downloaded zip archives after unpacking them, so you keep the usable files without duplicate compressed copies. These chunks have evaluation turned off in the deck because the download needs a normal local R session and a real place to write the folders.
First set the path:
Now, download:
This will create a single folder for each day of the specified date range. Inside each folder you will see a bunch of files sharing one name and differing only in extension.
The practical problem: a year of daily data is three hundred and sixty-five folders, and you are not going to type those paths. So list them programmatically. The code recurses through the folder, uses an end-anchored pattern to keep only paths ending in bil, and drops the auxiliary ones, which would otherwise sneak in. Then terra reads the whole vector in a single call and gives you one multi-layer object with a layer per day. That last step is the one worth remembering: terra’s rast function accepts a whole vector of paths, not just a single one, and stacks them for you.
Read the pipeline from left to right. List files starts at Data slash PRISM. Recursive true searches the daily subfolders, and full names true returns complete paths relative to that starting point so rast can locate each file. In the first grep, backslash-dot means a literal period and the dollar sign anchors bil at the end; value true keeps the matching paths instead of their positions. The next line uses grepl to mark names containing aux, the exclamation mark reverses that test, and the bracket keeps only the non-auxiliary files. The parentheses around the assignment print the resulting path vector for inspection. Replace the starting folder with your own, then pass the checked vector to terra colon-colon rast as shown.
As you have seen, we would have many files to open unless the specified date range is very short. Rather than naming them one by one, list them programmatically.
First, the following code gives you the names of all the PRISM files with the .bil extension.
(
prism_files_list <-
list.files("Data/PRISM", recursive = TRUE, full.names = TRUE) %>%
grep(pattern = "\\.bil$", ., value = TRUE) %>%
.[!grepl("aux", .)]
)[1] "Data/PRISM/PRISM_ppt_provisional_4kmD2_20240101_bil/PRISM_ppt_provisional_4kmD2_20240101_bil.bil"
[2] "Data/PRISM/PRISM_ppt_provisional_4kmD2_20240102_bil/PRISM_ppt_provisional_4kmD2_20240102_bil.bil"
[3] "Data/PRISM/PRISM_ppt_provisional_4kmD2_20240103_bil/PRISM_ppt_provisional_4kmD2_20240103_bil.bil"
[4] "Data/PRISM/PRISM_ppt_provisional_4kmD2_20240104_bil/PRISM_ppt_provisional_4kmD2_20240104_bil.bil"
[5] "Data/PRISM/PRISM_ppt_provisional_4kmD2_20240105_bil/PRISM_ppt_provisional_4kmD2_20240105_bil.bil"
[6] "Data/PRISM/PRISM_tmax_stable_4kmD2_20120801_bil/PRISM_tmax_stable_4kmD2_20120801_bil.bil"
[7] "Data/PRISM/tmax/PRISM_tmax_stable_4kmD2_20230601_bil/PRISM_tmax_stable_4kmD2_20230601_bil.bil"
[8] "Data/PRISM/tmax/PRISM_tmax_stable_4kmD2_20230602_bil/PRISM_tmax_stable_4kmD2_20230602_bil.bil"
[9] "Data/PRISM/tmax/PRISM_tmax_stable_4kmD2_20230603_bil/PRISM_tmax_stable_4kmD2_20230603_bil.bil"
Just replace "Data/PRISM" with your folder path to the PRISM files.
We can now read them using terra::rast() like below:
class : SpatRaster
size : 621, 1405, 9 (nrow, ncol, nlyr)
resolution : 0.04166667, 0.04166667 (x, y)
extent : -125.0208, -66.47917, 24.0625, 49.9375 (xmin, xmax, ymin, ymax)
coord. ref. : lon/lat NAD83
sources : PRISM_ppt_provisional_4kmD2_20240101_bil.bil
PRISM_ppt_provisional_4kmD2_20240102_bil.bil
PRISM_ppt_provisional_4kmD2_20240103_bil.bil
... and 6 more sources
names : PRISM~1_bil, PRISM~2_bil, PRISM~3_bil, PRISM~4_bil, PRISM~5_bil, PRISM~1_bil, ...
min values : 0.000, 0.0000, 0.0000, 0.0000, 0.0000, 7.408, ...
max values : 42.917, 10.2093, 87.7497, 37.0813, 90.0984, 46.303, ...
The same three-step arc as the NASS exercise, now for weather: download three days of maximum temperature, read them all back with the listing trick, and facet a map by layer. Note the third answer uses facet_wrap on lyr, which is the raster faceting from the mapping deck. Worth noticing how little of this is new. By this point in the course you are combining pieces you already know rather than learning anything fresh, which is roughly the definition of having learned something. That is a fair note to end the course on.
In Download PRISM data, change the folder passed to prism_set_dl_dir, then request type tmax for June first through June third of 2023. KeepZip false discards the archives after extraction. In Read the downloaded files, list files searches that tmax folder recursively and returns full paths, the anchored bil filter keeps the raster data files, and the auxiliary-name filter removes unwanted companions. Terra rast reads the remaining vector into prism underscore max underscore temp, with one layer for each downloaded day.
In Create a map, geom spatraster draws that multi-layer object. Facet wrap with lyr makes a panel for each layer name, so the three daily temperature surfaces appear separately rather than being drawn on top of one another. As in the NASS exercise, the folded answers let you attempt each operation before revealing the working code.
Download PRISM maximum temperature data from “06-01-2023” to “06-03-2023”.
Answer
Read all the maximum temperature data files you just downloaded using terra::rast().
Answer
The Cropland Data Layer is a remarkable satellite-derived dataset. Coverage began on a limited basis in 1997 and expanded to the entire continental United States in 2008. Its resolution is thirty metres from 2008 through 2023 and ten metres from 2024 onward, classifying pixels by what crop is growing on them. For agricultural economics it is close to indispensable, since it tells you what was actually growing on a given field rather than what somebody reported at the county level. CropScape is the interactive front end you may have seen in a browser, and the CropScapeR package talks to exactly the same service from R, which is what we use here.
The resolution history on screen also includes fifty-six metres in 2006 and 2007, before the move to thirty metres in 2008. This is a georeferenced, crop-specific land-cover product from USDA NASS, so each raster cell records a land-cover class at a known location. That cell-level detail is the reason the dataset supports field-scale and landscape analysis that county summaries cannot. The CropScape link is useful for exploring the product visually, while the package workflow makes the same kind of request reproducible in a script.
The Cropland Data Layer (CDL) is a data product produced by the National Agricultural Statistics Service of U.S. Department of Agriculture.
CDL provides geo-referenced, high-accuracy, crop-specific cropland land cover information. Coverage began on a limited basis in 1997 and expanded to the entire continental United States in 2008. Resolution was 56 meters in 2006 and 2007, 30 meters from 2008 through 2023, and 10 meters from 2024 onward.
This data product has been extensively used in agricultural research. CropScape is an interactive Web CDL exploring system, and it was developed to query, visualize, disseminate, and analyze CDL data geospatially through standard geospatial web services in a publicly accessible on-line environment (Han et al., 2012).
This section shows how to use the CropScapeR package (Chen 2020) to download and explore the CDL data.
The package offers four functions, but one of them does the work. GetCDLData downloads the raw layer. The other three create preview images, summary statistics, and between-year change comparisons. Learn that one properly and treat the other three as a bonus you may never need. One thing to note before you go looking for it: what comes back is a RasterLayer from the old raster package, not a terra SpatRaster, so you will usually pass it straight through terra’s rast function to convert it, exactly as the final exercise does.
The package implements some of the most useful geospatial processing services provided by the CropScape, and it allows users to efficiently process the CDL data within the R environment.
Specifically, the CropScapeR package provides four functions that implement different kinds of geospatial processing services provided by the CropScape.
GetCDLData() in particular is the most important function as it lets you download the raw CDL data.
The other functions provide the users with the CDL data summarized or transformed in particular manners that may suit the need of some users.
Three arguments, and the fiddly one is type, which tells the service how to interpret the area you gave it. An f means you passed a FIPS code, for a state or a county. A b means a bounding box, and that includes handing it an sf object, since only the box gets used. A p is a single point and ps a polygon of your own coordinates. Read the callout about the class of the returned object too, because forgetting it produces confusing errors two steps later, when some terra function refuses what you hand it.
Aoi is the area you want covered, and year chooses the CDL vintage. Match the type to the form of that area. An sf or s-f-c object uses letter b, as does a bounding box defined by four corner points. A county uses its five-digit FIPS code with letter f, while a state uses its two-digit FIPS code with the same type. A polygon described by at least three coordinates uses p-s, and one coordinate uses p for a point. These are service codes, so choosing the wrong one changes how the server reads the same value. The result is a raster-package RasterLayer, as the callout emphasizes, so convert it before using a workflow that expects terra’s SpatRaster class.
GetCDLData() allows us to obtain CDL data for any Area of Interest (AOI) in a given year. It requires three parameters to make a valid data request:
aoi: Area of Interest (AOI).year: Year of the data to request.type: Type of AOI.The following AOI-type combinations are accepted:
type = "b"type = "f"type = "f"type = "b"type = "ps"type = "p"Important
The downloaded raster data is RasterLayer defined by the raster package, not SpatRaster by the terra package.
A whole state by FIPS code, Nebraska being thirty-one, with type set to f. The warning about time is not a formality. At thirty metre resolution a state contains an enormous number of cells, and the download and the memory it needs are both substantial. This is exactly why cropping matters, and why the next example takes four counties rather than a state. If you only need part of a state, ask for that part rather than downloading everything and throwing most of it away. Your patience and the server will both benefit.
The code stores the request as c-d-l underscore N-E. Aoi thirty-one identifies Nebraska, year is the character value 2018, and letter f tells the service to interpret thirty-one as a FIPS code rather than as a point or a box. Evaluation is turned off on the slide, so there is no displayed raster output and you must run the request locally if you need it. The object returned by this call is the RasterLayer class noted on the previous tab.
Suppose you are interested in getting CDL data for the entire Nebraska.
In this case we can use the state FIP code for NE (31) for aoi and specify type to be "f" (Note that this would take some time if you run it.).
This can take a while. Since the spatial resolution is 30m, the CDL data covering the whole of Nebraska has an enormous number of cells and is memory-intensive.
The more typical case, and the more sensible one. Pull county boundaries with tigris, filter to the four you want, and hand that sf object straight to GetCDLData. Note what the service actually does with it, which is explained on the download tab: it uses only the bounding box, not the outlines, so you get a rectangle containing your counties along with the corners of their neighbours. Trimming that rectangle back to the actual county shapes is masking, from the previous deck, and it is one of the things the final exercise asks you to do.
On Area of interest, tigris counties downloads Illinois with generalized boundaries and no progress display. The membership filter keeps Champaign, Vermilion, Ford, and Iroquois in I-L underscore four underscore county. The output map first draws every Illinois county, then overlays those four with light-blue fill; theme void strips away the axes. That picture is a visual check that the filter selected the intended connected area.
On Download, the sf object becomes aoi, the requested year is 2018, and type b tells GetCDLData to use its bounding box. Because the call is not assigned in the shown code, running it displays or returns the download but does not save a named object for later steps. In your own workflow, assign that result if you plan to convert, mask, or map it. The rectangular extent is efficient to request, but remember that it is not the same shape as the four county polygons.
In this example, we are interested in obtaining CDL data for the following four counties in Illinois: Champaign, Vermilion, Ford, and Iroquois.
Let’s first get the county boundary data for them:
Here is where they are:

The final exercise of the course, and it pulls in nearly everything. Download a county boundary with tigris, use it to fetch CDL data, convert to a SpatRaster, mask it to the county outline, aggregate it to something a computer can plot, and map the result. That is four decks’ worth of material in five steps. One thing to watch in the aggregation: CDL cells hold crop codes rather than quantities, so averaging them is meaningless. Take the most common code in each block instead, with the modal function, exactly as the raster deck warned.
On AOI, tigris counties requests generalized Iowa county boundaries, and the pipe sends them directly to dplyr filter. The exact name test keeps Sioux County and stores the one-feature sf object as sioux underscore county. On Download, that object is the aoi, 2022 is the requested CDL year, and type b requests its bounding box. The pipe immediately passes the returned RasterLayer to terra rast, so sioux underscore c-d-l underscore 2022 is a SpatRaster ready for the remaining terra operations.
On Mask, the county boundary is first transformed to the raster’s coordinate reference system. That alignment is necessary because masking compares locations, and coordinates in different systems cannot be compared correctly. Terra mask then keeps cells inside Sioux County and removes cells in the rest of the downloaded rectangle. On Aggregate, fact ten groups the original cells into ten-by-ten blocks. Fun modal assigns each larger cell the most common crop code in its block, preserving the categorical meaning while reducing the number of cells that must be drawn.
Finally, ggplot starts the map and geom spatraster draws sioux underscore aggregated. The code-folded answers are there for checking, so work through the tabs in order before opening them. The object names deliberately carry forward from one answer to the next, which makes this a single reproducible pipeline from an administrative boundary to a manageable county land-cover map.
Using the tigris package, download the county boundary for Iowa, and then filter it to keep only the Sioux county. Name the sf file sioux_county.
Answer
Using the CropScapeR::GetCDLData(), download the 2022 CDL data covering the Sioux County. Then, convert it to a SpatRaster object. Name the final product sioux_cdl_2022.
Answer
Mask the CDL data you just downloaded using sioux_county using terra::mask().
Answer
Before creating a map from the downloaded CDL layer data, let’s aggregate the data by factor of 10 using terra::aggregate(). Call it sioux_aggregated.
Careful with fun here — CDL cells hold crop codes, not quantities.
Answer
#--- CHANGED: fun = "modal" added. aggregate() defaults to fun = "mean", and ---#
#--- averaging land cover codes (1 = corn, 5 = soybeans, ...) produces ---#
#--- numbers that are not any crop. "modal" takes the most common code in ---#
#--- each block, which is the only sensible summary for a categorical layer. ---#
sioux_aggregated <- terra::aggregate(sioux_masked, fact = 10, fun = "modal")