PSA Training Series github.com/precision-sustainable-ag

Database Querying with R — Brian W. Davis 2020-01-07

  1. What is a DB? (video)
  2. Getting started with R (video)
  3. How to use a local DB (video)
  4. How to use the PSA DB (video)

1 Set up a local DB

To introduce working with a database, we’ll use one you can’t break or mess up, since it’s just running locally on your computer. SQLite is a lightweight DB platform that’s widely available and relatively simple. It’s so simple that the “server” part is literally just a file sitting in your working directory, instead of a whole other application. You can use your command line as a client, or you can use R.

1.1 At the command line

You could open your command line/Terminal/PowerShell and type individual commands:

cd ./PATH/TO/YOUR/WORKING/DIRECTORY
sqlite3 experiment.db

Then you can use .help for instructions, and input all the SQL you want (or .exit to quit). But it’s a little bit hard to read at the command line, so lets do it from R instead.

1.2 Inside R

The con object we just created is important. It’s how R knows where and how to send SQL queries, and how to process the data that gets returned back from them.

You can always make new connections to a database, but either your DB or R will crash if you run out of memory. This is very unlikely to happen in normal usage (both can handle millions of connections concurrently), but it’s a good idea to close a connection you opened after your script is done: dbDisconnect(con)

NOTE: Connection objects contain pointers to memory addresses, so you can’t save them in your R environment for later, or save them to disk. Every time you restart R or open a new R session, you have to make a new connection at the top of your script.

Now we have an empty database, stored in a file in our working directory.

2 Load data into DB

Let’s go back to our yields experiment and put that data into our database. Again, all of this could be done at the command line instead.

2.1 Pure SQL from R

You can run arbitrary SQL from R using dbExecute.

  • First you need to make a statement that creates a table (called CREATE TABLE, which is nice and easy). Note that when you make each column, you have to decide what data type goes in there, and different DBs support different data types.
  • Then you need a statement to INSERT INTO that table sets of VALUES.
  • Finally you can check your work with dbGetQuery to see what was written.
[1] 0
[1] 8
  plot_id crop    tillage
1     101 corn    no-till
2     102 corn strip-till
3     103  soy strip-till
4     104  soy    no-till
5     201 corn strip-till
6     202  soy    no-till
7     203 corn    no-till
8     204  soy strip-till

That is kind of hard to read, and hard to write by hand without making any mistakes. We can use the R to handle a lot of that for us.

2.2 Using DBI to handle the SQL

  plot_id Mg_ha
1     101  10.0
2     102  12.0
3     103   4.5
4     104   4.5
5     201  11.0
6     202   3.0
7     203   9.5
8     204   5.0

dbWriteTable did all the work of converting the dataframes into those long SQL statements, then sent and executed the query. If you want to reset the database back to the beginning to try running these again, you should run dbRemoveTable(con, "treatments") and dbRemoveTable(con, "yields")

3 Get list of tables

[1] "treatments" "yields"    

This is the same output you’d get from .tables at the SQLite command line.

4 Reading tables

4.1 Vanilla SQL and DBI

I just showed examples of how to read individual tables with dbGetQuery(con, "SELECT * FROM treatments;") and dbReadTable(con, "yields"). These functions both send out a query, and return the whole table back to R immediately. This is fine for small tables, but if a table has a million rows, this might be too slow and cumbersome.

Fortunately one of the powerful features of databases is that you can send a query and get back a preview without computing the whole request. Besides just getting back whole tables, SQL provides many operations that you can do inside the database, sending back just that preview. These functions include counting rows, adding two columns together, filtering on conditions, and lots more. This way you can construct a query and change it bit-by-bit until you’re sure you’re getting the right data without sacrificing speed and memory at each step.

You can do this with the vanilla SQL interface:

<SQLiteResult>
  SQL  SELECT * FROM treatments
  ROWS Fetched: 0 [incomplete]
       Changed: 0
  plot_id crop    tillage
1     101 corn    no-till
2     102 corn strip-till
3     103  soy strip-till
4     104  soy    no-till
5     201 corn strip-till

Then you look at the preview and decide that is the query you wanted:

  plot_id crop    tillage
1     101 corn    no-till
2     102 corn strip-till
3     103  soy strip-till
4     104  soy    no-till
5     201 corn strip-till
6     202  soy    no-till
7     203 corn    no-till
8     204  soy strip-till

And you have to clear the pending query when you’re done. This pattern of having intermediate function calls and variables is a pain and a common source of errors, so I don’t recommend it. But if you have a particularly complex SQL query you’ve written, this is one way to go.

4.2 Tidy R

If you don’t want to memorize new SQL functions and handle query-state management, there is an alternative in the database backend of the popular {dplyr} package. {dbplyr} takes the common verbs you can learn on locally stored dataframes and translates them into SQL, then constructs the request for you.

# Source:   table<treatments> [?? x 3]
# Database: sqlite 3.30.1 [./experiment.db]
  plot_id crop  tillage   
    <int> <chr> <chr>     
1     101 corn  no-till   
2     102 corn  strip-till
3     103 soy   strip-till
4     104 soy   no-till   
5     201 corn  strip-till
6     202 soy   no-till   
7     203 corn  no-till   
8     204 soy   strip-till

Notice at the top, it says table<treatments> [?? x 3]? This tells us that we’re viewing table (table<treatments>) in a database (Database: sqlite 3.30.1 [./experiment.db]), instead of a local dataframe. (You might also see lazy query instead of a table name; that means we’ve constructed a temporary unnamed table in the DB’s memory.)

It also tells us that we have 3 columns, but unknown number of rows. Since we just created this table we actually know there are only those 8 rows. However, often in queries, the database could have millions of rows. It’s only returning this short preview. If we want to manipulate this query, we can do that without forcing the database to give us everything (which saves your time in front of a computer waiting for commands to run).

Let’s look at only the rows that match corn plots.

# Source:   lazy query [?? x 3]
# Database: sqlite 3.30.1 [./experiment.db]
  plot_id crop  tillage   
    <int> <chr> <chr>     
1     101 corn  no-till   
2     102 corn  strip-till
3     201 corn  strip-till
4     203 corn  no-till   

Now we’ve got a “lazy query”, because the filtering happened in the database, not in R. We can poke into the backend and find out what’s being executed:

<SQL>
SELECT *
FROM `treatments`
WHERE (`crop` = 'corn')

You can choose rows based on the values in any column, whether they’re text (like crop == "corn" above), numeric, dates, boolean/logical, etc. Let’s look at the yield table, and filter for the rows where the yields were low:

# Source:   lazy query [?? x 2]
# Database: sqlite 3.30.1 [./experiment.db]
  plot_id Mg_ha
    <int> <dbl>
1     103   4.5
2     104   4.5
3     202   3  

This is the same as using the vanilla SQL query: SELECT * FROM yields WHERE (Mg_ha < 5.0);.

Another common use case of database operations is group summaries:

# Source:   lazy query [?? x 3]
# Database: sqlite 3.30.1 [./experiment.db]
# Groups:   crop
  crop  tillage        n
  <chr> <chr>      <int>
1 corn  no-till        2
2 corn  strip-till     2
3 soy   no-till        2
4 soy   strip-till     2

And again, we can see what SQL query is really being sent with show_query():

<SQL>
SELECT `crop`, `tillage`, COUNT() AS `n`
FROM `treatments`
GROUP BY `crop`, `tillage`

But there are some functions that can only be run inside R. For those, you have to force the lazy query to execute fully, with collect().

# A tibble: 8 x 5
  plot_id crop  tillage    label        Rep
    <int> <chr> <chr>      <chr>      <dbl>
1     101 corn  no-till    No-Till        1
2     102 corn  strip-till Strip-Till     1
3     103 soy   strip-till Strip-Till     1
4     104 soy   no-till    No-Till        1
5     201 corn  strip-till Strip-Till     2
6     202 soy   no-till    No-Till        2
7     203 corn  no-till    No-Till        2
8     204 soy   strip-till Strip-Till     2

You can try running it without the collect() and you’ll see you get a fairly unhelpful error message. However, if you see any kind of error that claims a column isn’t present when you know it’s there, or a function is missing, that’s a good indicator of a failed lazy query.

Here’s a handy reference for the functions that {dbplyr} currently knows how to translate and run inside the DB: https://dbplyr.tidyverse.org/articles/sql-translation.html

5 Joins

One of the most powerful functions in SQL is the join. There are several types of joins, and it usually takes a little thinking to make sure you’re getting the one you want. If there’s a one-to-one relationship with your keys (each row is identified by one plot_id, and each plot_id only refers to one row in a table, for example), then a LEFT JOIN is a good bet.

5.1 Vanilla SQL

  plot_id crop    tillage plot_id..4 Mg_ha
1     101 corn    no-till        101  10.0
2     102 corn strip-till        102  12.0
3     103  soy strip-till        103   4.5
4     104  soy    no-till        104   4.5
5     201 corn strip-till        201  11.0
6     202  soy    no-till        202   3.0
7     203 corn    no-till        203   9.5
8     204  soy strip-till        204   5.0

Notice that plot_id gets printed twice, this is a quirk of SQL. While it is nice to make sure that the right columns were matched, it’s usually just in the way. There are cleaner ways to get your query using tidy R syntax.

5.2 Tidy R

Joining, by = "plot_id"
# Source:   lazy query [?? x 4]
# Database: sqlite 3.30.1 [./experiment.db]
  plot_id crop  tillage    Mg_ha
    <int> <chr> <chr>      <dbl>
1     101 corn  no-till     10  
2     102 corn  strip-till  12  
3     103 soy   strip-till   4.5
4     104 soy   no-till      4.5
5     201 corn  strip-till  11  
6     202 soy   no-till      3  
7     203 corn  no-till      9.5
8     204 soy   strip-till   5  

Again, notice that this is a lazy query! The join happened inside the DB. This can be MUCH faster than inside R for large complex joins. We can then collect() the result and use it for further analysis.

Other types of joins you might run into are full/outer/cross, inner, right, semi, and anti. The help page at ?dplyr::full_join is a good resource for explaining them, and there are many online tutorials as well (e.g. https://r4ds.had.co.nz/relational-data.html#understanding-joins). However, not all the joins are implemented in SQLite, so you may have to collect() and do them locally inside R. I’m fairly sure all of them are implemented in {RPostgres} though, so we’ll look at them more when we read the PSA On-Farm database.


6 Putting it all together

Joining, by = "plot_id"
<SQL>
SELECT `crop`, `tillage`, AVG(`Mg_ha`) AS `mean`, STDEV(`Mg_ha`) AS `sd`
FROM (SELECT `LHS`.`plot_id` AS `plot_id`, `LHS`.`crop` AS `crop`, `LHS`.`tillage` AS `tillage`, `RHS`.`Mg_ha` AS `Mg_ha`
FROM `treatments` AS `LHS`
LEFT JOIN `yields` AS `RHS`
ON (`LHS`.`plot_id` = `RHS`.`plot_id`)
)
GROUP BY `crop`, `tillage`
# Source:   lazy query [?? x 4]
# Database: sqlite 3.30.1 [./experiment.db]
# Groups:   crop
  crop  tillage     mean    sd
  <chr> <chr>      <dbl> <dbl>
1 corn  no-till     9.75 0.354
2 corn  strip-till 11.5  0.707
3 soy   no-till     3.75 1.06 
4 soy   strip-till  4.75 0.354

6.1 Cleaning up

It’s not strictly required to close connections manually, because R will close inactive ones eventually. But it’s good practice to do, so that it will be a habit when you’re connecting to remote databases that may have many concurrent users.

LS0tCnRpdGxlOiAiMDAzIC0gSG93IHRvIHVzZSBhIGxvY2FsIERCIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0aGVtZTogcmVhZGFibGUKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgY29kZV9mb2xkaW5nOiBub25lCiAgICBpbmNsdWRlczoKICAgICAgYmVmb3JlX2JvZHk6IGFzc2V0cy9oZWFkZXIuaHRtbAogICAgICBhZnRlcl9ib2R5OiBhc3NldHMvZm9vdGVyLmh0bWwKLS0tCgojIFNldCB1cCBhIGxvY2FsIERCCgpUbyBpbnRyb2R1Y2Ugd29ya2luZyB3aXRoIGEgZGF0YWJhc2UsIHdlJ2xsIHVzZSBvbmUgeW91IGNhbid0IGJyZWFrIG9yIG1lc3MgdXAsIHNpbmNlIGl0J3MganVzdCBydW5uaW5nIGxvY2FsbHkgb24geW91ciBjb21wdXRlci4gU1FMaXRlIGlzIGEgbGlnaHR3ZWlnaHQgREIgcGxhdGZvcm0gdGhhdCdzIHdpZGVseSBhdmFpbGFibGUgYW5kIHJlbGF0aXZlbHkgc2ltcGxlLiBJdCdzIHNvIHNpbXBsZSB0aGF0IHRoZSAic2VydmVyIiBwYXJ0IGlzIGxpdGVyYWxseSBqdXN0IGEgZmlsZSBzaXR0aW5nIGluIHlvdXIgd29ya2luZyBkaXJlY3RvcnksIGluc3RlYWQgb2YgYSB3aG9sZSBvdGhlciBhcHBsaWNhdGlvbi4gWW91IGNhbiB1c2UgeW91ciBjb21tYW5kIGxpbmUgYXMgYSBjbGllbnQsIG9yIHlvdSBjYW4gdXNlIFIuCgojIyBBdCB0aGUgY29tbWFuZCBsaW5lCgpZb3UgY291bGQgb3BlbiB5b3VyIGNvbW1hbmQgbGluZS9UZXJtaW5hbC9Qb3dlclNoZWxsIGFuZCB0eXBlIGluZGl2aWR1YWwgY29tbWFuZHM6CgpgYGAKY2QgLi9QQVRIL1RPL1lPVVIvV09SS0lORy9ESVJFQ1RPUlkKc3FsaXRlMyBleHBlcmltZW50LmRiCmBgYAoKVGhlbiB5b3UgY2FuIHVzZSBgLmhlbHBgIGZvciBpbnN0cnVjdGlvbnMsIGFuZCBpbnB1dCBhbGwgdGhlIFNRTCB5b3Ugd2FudCAob3IgYC5leGl0YCB0byBxdWl0KS4gQnV0IGl0J3MgYSBsaXR0bGUgYml0IGhhcmQgdG8gcmVhZCBhdCB0aGUgY29tbWFuZCBsaW5lLCBzbyBsZXRzIGRvIGl0IGZyb20gUiBpbnN0ZWFkLgoKIyMgSW5zaWRlIFIKCmBgYHtyIGVjaG8gPSBGQUxTRX0KIyB0aGlzIGlzIGEgaGlkZGVuIGNodW5rIGJlZm9yZSB3ZSBzdGFydCBtYW5pcHVsYXRpbmcgdGhlIERCCiMganVzdCB0byBtYWtlIHN1cmUgaXQncyBhIGNsZWFuIGVudmlyb25tZW50CmlmIChleGlzdHMoImNvbiIpICYmIAogICAgY2xhc3MoY29uKSA9PSAiU1FMaXRlQ29ubmVjdGlvbiIgJiYgCiAgICBjb25AZGJuYW1lID09ICIuL2V4cGVyaW1lbnQuZGIiICYmCiAgICBEQkk6OmRiSXNWYWxpZChjb24pKSB7CiAgREJJOjpkYkRpc2Nvbm5lY3QoY29uKQogIGludmlzaWJsZSgpCn0KCmlmIChmaWxlLmV4aXN0cygiLi9leHBlcmltZW50LmRiIikpIHsKICBmaWxlLnJlbW92ZSgiLi9leHBlcmltZW50LmRiIikKICBpbnZpc2libGUoKQp9CmBgYAoKYGBge3J9CmxpYnJhcnkoUlNRTGl0ZSkKY29uIDwtIGRiQ29ubmVjdChTUUxpdGUoKSwgIi4vZXhwZXJpbWVudC5kYiIpCmBgYAoKVGhlICoqYGNvbmAqKiBvYmplY3Qgd2UganVzdCBjcmVhdGVkIGlzIGltcG9ydGFudC4gSXQncyBob3cgUiBrbm93cyB3aGVyZSBhbmQgaG93IHRvIHNlbmQgU1FMIHF1ZXJpZXMsIGFuZCBob3cgdG8gcHJvY2VzcyB0aGUgZGF0YSB0aGF0IGdldHMgcmV0dXJuZWQgYmFjayBmcm9tIHRoZW0uCgo+IFlvdSBjYW4gYWx3YXlzIG1ha2UgbmV3IGNvbm5lY3Rpb25zIHRvIGEgZGF0YWJhc2UsIGJ1dCBlaXRoZXIgeW91ciBEQiBvciBSIHdpbGwgY3Jhc2ggaWYgeW91IHJ1biBvdXQgb2YgbWVtb3J5LiBUaGlzIGlzIHZlcnkgdW5saWtlbHkgdG8gaGFwcGVuIGluIG5vcm1hbCB1c2FnZSAoYm90aCBjYW4gaGFuZGxlICoqbWlsbGlvbnMqKiBvZiBjb25uZWN0aW9ucyBjb25jdXJyZW50bHkpLCBidXQgaXQncyBhIGdvb2QgaWRlYSB0byBjbG9zZSBhIGNvbm5lY3Rpb24geW91IG9wZW5lZCBhZnRlciB5b3VyIHNjcmlwdCBpcyBkb25lOiAqKmBkYkRpc2Nvbm5lY3QoY29uKWAqKgoKPiAqKk5PVEU6KiogQ29ubmVjdGlvbiBvYmplY3RzIGNvbnRhaW4gcG9pbnRlcnMgdG8gbWVtb3J5IGFkZHJlc3Nlcywgc28geW91IGNhbid0IHNhdmUgdGhlbSBpbiB5b3VyIFIgZW52aXJvbm1lbnQgZm9yIGxhdGVyLCBvciBzYXZlIHRoZW0gdG8gZGlzay4gRXZlcnkgdGltZSB5b3UgcmVzdGFydCBSIG9yIG9wZW4gYSBuZXcgUiBzZXNzaW9uLCB5b3UgaGF2ZSB0byBtYWtlIGEgbmV3IGNvbm5lY3Rpb24gYXQgdGhlIHRvcCBvZiB5b3VyIHNjcmlwdC4KCk5vdyB3ZSBoYXZlIGFuIGVtcHR5IGRhdGFiYXNlLCBzdG9yZWQgaW4gYSBmaWxlIGluIG91ciB3b3JraW5nIGRpcmVjdG9yeS4gCgojIExvYWQgZGF0YSBpbnRvIERCCgpMZXQncyBnbyBiYWNrIHRvIG91ciB5aWVsZHMgZXhwZXJpbWVudCBhbmQgcHV0IHRoYXQgZGF0YSBpbnRvIG91ciBkYXRhYmFzZS4gQWdhaW4sIGFsbCBvZiB0aGlzIGNvdWxkIGJlIGRvbmUgYXQgdGhlIGNvbW1hbmQgbGluZSBpbnN0ZWFkLgoKCgpgYGB7cn0KdHJlYXRtZW50cyA8LSBkYXRhLmZyYW1lKAogIHBsb3RfaWQgPSBhcy5pbnRlZ2VyKGMoMTAxLCAxMDIsIDEwMywgMTA0LCAyMDEsIDIwMiwgMjAzLCAyMDQpKSwKICBjcm9wID0gYygiY29ybiIsICJjb3JuIiwgInNveSIsICJzb3kiLCAiY29ybiIsICJzb3kiLCAiY29ybiIsICJzb3kiKSwKICB0aWxsYWdlID0gYygKICAgICJuby10aWxsIiwgInN0cmlwLXRpbGwiLCAic3RyaXAtdGlsbCIsICJuby10aWxsIiwgCiAgICAic3RyaXAtdGlsbCIsICJuby10aWxsIiwgIm5vLXRpbGwiLCAic3RyaXAtdGlsbCIKICAgICkKKQoKeWllbGRzIDwtIGRhdGEuZnJhbWUoCiAgcGxvdF9pZCA9IGFzLmludGVnZXIoYygxMDEsIDEwMiwgMTAzLCAxMDQsIDIwMSwgMjAyLCAyMDMsIDIwNCkpLAogIE1nX2hhID0gYygxMCwgMTIsIDQuNSwgNC41LCAxMSwgMywgOS41LCA1KQopCmBgYAojIyBQdXJlIFNRTCBmcm9tIFIKCllvdSBjYW4gcnVuIGFyYml0cmFyeSBTUUwgZnJvbSBSIHVzaW5nICoqYGRiRXhlY3V0ZWAqKi4gCgoqIEZpcnN0IHlvdSBuZWVkIHRvIG1ha2UgYSBzdGF0ZW1lbnQgdGhhdCBjcmVhdGVzIGEgdGFibGUgKGNhbGxlZCBgQ1JFQVRFIFRBQkxFYCwgd2hpY2ggaXMgbmljZSBhbmQgZWFzeSkuIE5vdGUgdGhhdCB3aGVuIHlvdSBtYWtlIGVhY2ggY29sdW1uLCB5b3UgaGF2ZSB0byBkZWNpZGUgd2hhdCBkYXRhIHR5cGUgZ29lcyBpbiB0aGVyZSwgYW5kIGRpZmZlcmVudCBEQnMgc3VwcG9ydCBkaWZmZXJlbnQgZGF0YSB0eXBlcy4gCiogVGhlbiB5b3UgbmVlZCBhIHN0YXRlbWVudCB0byBgSU5TRVJUIElOVE9gIHRoYXQgdGFibGUgc2V0cyBvZiBgVkFMVUVTYC4gCiogRmluYWxseSB5b3UgY2FuIGNoZWNrIHlvdXIgd29yayB3aXRoICoqYGRiR2V0UXVlcnlgKiogdG8gc2VlIHdoYXQgd2FzIHdyaXR0ZW4uCgpgYGB7ciBwYWdlZC5wcmludD1GQUxTRX0KZGJFeGVjdXRlKAogIGNvbiwgIgpDUkVBVEUgVEFCTEUgYHRyZWF0bWVudHNgICgKICBgcGxvdF9pZGAgSU5URUdFUiwKICBgY3JvcGAgVEVYVCwKICBgdGlsbGFnZWAgVEVYVAopOwoiKQojIDAgcm93cyBhZmZlY3RlZCAobmV3IHRhYmxlKQoKZGJFeGVjdXRlKAogIGNvbiwgIgpJTlNFUlQgSU5UTyB0cmVhdG1lbnRzIChwbG90X2lkLCBjcm9wLCB0aWxsYWdlKSAKICBWQUxVRVMgCiAgKDEwMSwnY29ybicsJ25vLXRpbGwnKSwgCiAgKDEwMiwnY29ybicsJ3N0cmlwLXRpbGwnKSwKICAoMTAzLCdzb3knLCdzdHJpcC10aWxsJyksCiAgKDEwNCwnc295Jywnbm8tdGlsbCcpLAogICgyMDEsJ2Nvcm4nLCdzdHJpcC10aWxsJyksCiAgKDIwMiwnc295Jywnbm8tdGlsbCcpLAogICgyMDMsJ2Nvcm4nLCduby10aWxsJyksCiAgKDIwNCwnc295Jywnc3RyaXAtdGlsbCcpOwoiKQojIDggcm93cyBhZmZlY3RlZCAobmV3IHZhbHVlcykKCmRiR2V0UXVlcnkoY29uLCAiU0VMRUNUICogRlJPTSB0cmVhdG1lbnRzOyIpCgpgYGAKClRoYXQgaXMga2luZCBvZiBoYXJkIHRvIHJlYWQsIGFuZCBoYXJkIHRvIHdyaXRlIGJ5IGhhbmQgd2l0aG91dCBtYWtpbmcgYW55IG1pc3Rha2VzLiBXZSBjYW4gdXNlIHRoZSBSIHRvIGhhbmRsZSBhIGxvdCBvZiB0aGF0IGZvciB1cy4KCiMjIFVzaW5nIERCSSB0byBoYW5kbGUgdGhlIFNRTAoKYGBge3IgcGFnZWQucHJpbnQ9RkFMU0V9CmRiV3JpdGVUYWJsZShjb24sICJ5aWVsZHMiLCB5aWVsZHMpCgpkYlJlYWRUYWJsZShjb24sICJ5aWVsZHMiKQpgYGAKCioqYGRiV3JpdGVUYWJsZWAqKiBkaWQgYWxsIHRoZSB3b3JrIG9mIGNvbnZlcnRpbmcgdGhlIGRhdGFmcmFtZXMgaW50byB0aG9zZSBsb25nIFNRTCBzdGF0ZW1lbnRzLCB0aGVuIHNlbnQgYW5kIGV4ZWN1dGVkIHRoZSBxdWVyeS4gSWYgeW91IHdhbnQgdG8gcmVzZXQgdGhlIGRhdGFiYXNlIGJhY2sgdG8gdGhlIGJlZ2lubmluZyB0byB0cnkgcnVubmluZyB0aGVzZSBhZ2FpbiwgeW91IHNob3VsZCBydW4gKipgZGJSZW1vdmVUYWJsZShjb24sICJ0cmVhdG1lbnRzIilgKiogYW5kICoqYGRiUmVtb3ZlVGFibGUoY29uLCAieWllbGRzIilgKioKCgojIEdldCBsaXN0IG9mIHRhYmxlcwoKYGBge3J9CmRiTGlzdFRhYmxlcyhjb24pCmBgYAoKVGhpcyBpcyB0aGUgc2FtZSBvdXRwdXQgeW91J2QgZ2V0IGZyb20gYC50YWJsZXNgIGF0IHRoZSBTUUxpdGUgY29tbWFuZCBsaW5lLiAKCiMgUmVhZGluZyB0YWJsZXMKCiMjIFZhbmlsbGEgU1FMIGFuZCBEQkkKCkkganVzdCBzaG93ZWQgZXhhbXBsZXMgb2YgaG93IHRvIHJlYWQgaW5kaXZpZHVhbCB0YWJsZXMgd2l0aCBgZGJHZXRRdWVyeShjb24sICJTRUxFQ1QgKiBGUk9NIHRyZWF0bWVudHM7IilgIGFuZCBgZGJSZWFkVGFibGUoY29uLCAieWllbGRzIilgLiBUaGVzZSBmdW5jdGlvbnMgYm90aCBzZW5kIG91dCBhIHF1ZXJ5LCBhbmQgcmV0dXJuIHRoZSB3aG9sZSB0YWJsZSBiYWNrIHRvIFIgaW1tZWRpYXRlbHkuIFRoaXMgaXMgZmluZSBmb3Igc21hbGwgdGFibGVzLCBidXQgaWYgYSB0YWJsZSBoYXMgYSBtaWxsaW9uIHJvd3MsIHRoaXMgbWlnaHQgYmUgdG9vIHNsb3cgYW5kIGN1bWJlcnNvbWUuCgpGb3J0dW5hdGVseSBvbmUgb2YgdGhlIHBvd2VyZnVsIGZlYXR1cmVzIG9mIGRhdGFiYXNlcyBpcyB0aGF0IHlvdSBjYW4gc2VuZCBhIHF1ZXJ5IGFuZCBnZXQgYmFjayBhIHByZXZpZXcgd2l0aG91dCBjb21wdXRpbmcgdGhlIHdob2xlIHJlcXVlc3QuIEJlc2lkZXMganVzdCBnZXR0aW5nIGJhY2sgd2hvbGUgdGFibGVzLCBTUUwgcHJvdmlkZXMgbWFueSBvcGVyYXRpb25zIHRoYXQgeW91IGNhbiBkbyBpbnNpZGUgdGhlIGRhdGFiYXNlLCBzZW5kaW5nIGJhY2sganVzdCB0aGF0IHByZXZpZXcuIFRoZXNlIGZ1bmN0aW9ucyBpbmNsdWRlIGNvdW50aW5nIHJvd3MsIGFkZGluZyB0d28gY29sdW1ucyB0b2dldGhlciwgZmlsdGVyaW5nIG9uIGNvbmRpdGlvbnMsIGFuZCBsb3RzIG1vcmUuIFRoaXMgd2F5IHlvdSBjYW4gY29uc3RydWN0IGEgcXVlcnkgYW5kIGNoYW5nZSBpdCBiaXQtYnktYml0IHVudGlsIHlvdSdyZSBzdXJlIHlvdSdyZSBnZXR0aW5nIHRoZSByaWdodCBkYXRhIHdpdGhvdXQgc2FjcmlmaWNpbmcgc3BlZWQgYW5kIG1lbW9yeSBhdCBlYWNoIHN0ZXAuCgpZb3UgY2FuIGRvIHRoaXMgd2l0aCB0aGUgdmFuaWxsYSBTUUwgaW50ZXJmYWNlOgoKYGBge3IgcGFnZWQucHJpbnQgPSBGQUxTRX0KcSA8LSBkYlNlbmRRdWVyeShjb24sICJTRUxFQ1QgKiBGUk9NIHRyZWF0bWVudHMiKQoKcQoKaGVhZGVyIDwtIGRiRmV0Y2gocSwgbiA9IDUpCmhlYWRlcgpgYGAKClRoZW4geW91IGxvb2sgYXQgdGhlIHByZXZpZXcgYW5kIGRlY2lkZSB0aGF0IGlzIHRoZSBxdWVyeSB5b3Ugd2FudGVkOgoKYGBge3IgcGFnZWQucHJpbnQgPSBGQUxTRX0KcmVtYWluaW5nIDwtIGRiRmV0Y2gocSwgbiA9IEluZikKcmJpbmQoaGVhZGVyLCByZW1haW5pbmcpCgpkYkNsZWFyUmVzdWx0KHEpCmBgYApBbmQgeW91IGhhdmUgdG8gY2xlYXIgdGhlIHBlbmRpbmcgcXVlcnkgd2hlbiB5b3UncmUgZG9uZS4gVGhpcyBwYXR0ZXJuIG9mIGhhdmluZyBpbnRlcm1lZGlhdGUgZnVuY3Rpb24gY2FsbHMgYW5kIHZhcmlhYmxlcyBpcyBhIHBhaW4gYW5kIGEgY29tbW9uIHNvdXJjZSBvZiBlcnJvcnMsIHNvIEkgZG9uJ3QgcmVjb21tZW5kIGl0LiBCdXQgaWYgeW91IGhhdmUgYSBwYXJ0aWN1bGFybHkgY29tcGxleCBTUUwgcXVlcnkgeW91J3ZlIHdyaXR0ZW4sIHRoaXMgaXMgb25lIHdheSB0byBnby4KCgoKIyMgVGlkeSBSCgpJZiB5b3UgZG9uJ3Qgd2FudCB0byBtZW1vcml6ZSBuZXcgU1FMIGZ1bmN0aW9ucyBhbmQgaGFuZGxlIHF1ZXJ5LXN0YXRlIG1hbmFnZW1lbnQsIHRoZXJlIGlzIGFuIGFsdGVybmF0aXZlIGluIHRoZSBkYXRhYmFzZSBiYWNrZW5kIG9mIHRoZSBwb3B1bGFyIGB7ZHBseXJ9YCBwYWNrYWdlLiBge2RicGx5cn1gIHRha2VzIHRoZSBjb21tb24gdmVyYnMgeW91IGNhbiBsZWFybiBvbiBsb2NhbGx5IHN0b3JlZCBkYXRhZnJhbWVzIGFuZCB0cmFuc2xhdGVzIHRoZW0gaW50byBTUUwsIHRoZW4gY29uc3RydWN0cyB0aGUgcmVxdWVzdCBmb3IgeW91LgoKYGBge3IsIHBhZ2VkLnByaW50ID0gRn0KbGlicmFyeShkcGx5ciwgd2Fybi5jb25mbGljdHMgPSBGQUxTRSkKbGlicmFyeShkYnBseXIsIHdhcm4uY29uZmxpY3RzID0gRkFMU0UpCnRibChjb24sICJ0cmVhdG1lbnRzIikKYGBgCgpOb3RpY2UgYXQgdGhlIHRvcCwgaXQgc2F5cyBgdGFibGU8dHJlYXRtZW50cz4gWz8/IHggM11gPyBUaGlzIHRlbGxzIHVzIHRoYXQgd2UncmUgdmlld2luZyB0YWJsZSAoKipgdGFibGU8dHJlYXRtZW50cz5gKiopIGluIGEgZGF0YWJhc2UgKCoqYERhdGFiYXNlOiBzcWxpdGUgMy4zMC4xIFsuL2V4cGVyaW1lbnQuZGJdYCoqKSwgaW5zdGVhZCBvZiBhIGxvY2FsIGRhdGFmcmFtZS4gKFlvdSBtaWdodCBhbHNvIHNlZSAqKmBsYXp5IHF1ZXJ5YCoqIGluc3RlYWQgb2YgYSB0YWJsZSBuYW1lOyB0aGF0IG1lYW5zIHdlJ3ZlIGNvbnN0cnVjdGVkIGEgdGVtcG9yYXJ5IHVubmFtZWQgdGFibGUgaW4gdGhlIERCJ3MgbWVtb3J5LikKCkl0IGFsc28gdGVsbHMgdXMgdGhhdCB3ZSBoYXZlICoqMyoqIGNvbHVtbnMsIGJ1dCAqKnVua25vd24gbnVtYmVyKiogb2Ygcm93cy4gU2luY2Ugd2UganVzdCBjcmVhdGVkIHRoaXMgdGFibGUgd2UgYWN0dWFsbHkga25vdyB0aGVyZSBhcmUgb25seSB0aG9zZSA4IHJvd3MuIEhvd2V2ZXIsIG9mdGVuIGluIHF1ZXJpZXMsIHRoZSBkYXRhYmFzZSBjb3VsZCBoYXZlIG1pbGxpb25zIG9mIHJvd3MuIEl0J3Mgb25seSByZXR1cm5pbmcgdGhpcyBzaG9ydCBwcmV2aWV3LiBJZiB3ZSB3YW50IHRvIG1hbmlwdWxhdGUgdGhpcyBxdWVyeSwgd2UgY2FuIGRvIHRoYXQgd2l0aG91dCBmb3JjaW5nIHRoZSBkYXRhYmFzZSB0byBnaXZlIHVzIGV2ZXJ5dGhpbmcgKHdoaWNoIHNhdmVzIHlvdXIgdGltZSBpbiBmcm9udCBvZiBhIGNvbXB1dGVyIHdhaXRpbmcgZm9yIGNvbW1hbmRzIHRvIHJ1bikuIAoKTGV0J3MgbG9vayBhdCBvbmx5IHRoZSByb3dzIHRoYXQgbWF0Y2ggY29ybiBwbG90cy4KCgpgYGB7ciBwYWdlZC5wcmludCA9IEZ9CnRibChjb24sICJ0cmVhdG1lbnRzIikgJT4lIAogIGZpbHRlcihjcm9wID09ICJjb3JuIikKYGBgCgpOb3cgd2UndmUgZ290IGEgImxhenkgcXVlcnkiLCBiZWNhdXNlIHRoZSBmaWx0ZXJpbmcgaGFwcGVuZWQgaW4gdGhlIGRhdGFiYXNlLCBub3QgaW4gUi4gV2UgY2FuIHBva2UgaW50byB0aGUgYmFja2VuZCBhbmQgZmluZCBvdXQgd2hhdCdzIGJlaW5nIGV4ZWN1dGVkOgoKYGBge3IgcGFnZWQucHJpbnQgPSBGfQp0YmwoY29uLCAidHJlYXRtZW50cyIpICU+JSAKICBmaWx0ZXIoY3JvcCA9PSAiY29ybiIpICU+JSAKICBzaG93X3F1ZXJ5KCkKYGBgCgpZb3UgY2FuIGNob29zZSByb3dzIGJhc2VkIG9uIHRoZSB2YWx1ZXMgaW4gYW55IGNvbHVtbiwgd2hldGhlciB0aGV5J3JlIHRleHQgKGxpa2UgYGNyb3AgPT0gImNvcm4iYCBhYm92ZSksIG51bWVyaWMsIGRhdGVzLCBib29sZWFuL2xvZ2ljYWwsIGV0Yy4gTGV0J3MgbG9vayBhdCB0aGUgeWllbGQgdGFibGUsIGFuZCBmaWx0ZXIgZm9yIHRoZSByb3dzIHdoZXJlIHRoZSB5aWVsZHMgd2VyZSBsb3c6CgpgYGB7ciBwYWdlZC5wcmludCA9IEZBTFNFfQp0YmwoY29uLCAieWllbGRzIikgJT4lIAogIGZpbHRlcihNZ19oYSA8IDUpCmBgYAoKVGhpcyBpcyB0aGUgc2FtZSBhcyB1c2luZyB0aGUgdmFuaWxsYSBTUUwgcXVlcnk6IGBTRUxFQ1QgKiBGUk9NIHlpZWxkcyBXSEVSRSAoTWdfaGEgPCA1LjApO2AuCgpBbm90aGVyIGNvbW1vbiB1c2UgY2FzZSBvZiBkYXRhYmFzZSBvcGVyYXRpb25zIGlzIGdyb3VwIHN1bW1hcmllczoKCmBgYHtyIHBhZ2VkLnByaW50ID0gRn0KbnVtYmVyX29mX3Bsb3RzIDwtIHRibChjb24sICJ0cmVhdG1lbnRzIikgJT4lIAogIGdyb3VwX2J5KGNyb3AsIHRpbGxhZ2UpICU+JSAKICB0YWxseSgpIAoKbnVtYmVyX29mX3Bsb3RzCmBgYAoKQW5kIGFnYWluLCB3ZSBjYW4gc2VlIHdoYXQgU1FMIHF1ZXJ5IGlzIHJlYWxseSBiZWluZyBzZW50IHdpdGggKipgc2hvd19xdWVyeSgpYCoqOgoKYGBge3IgcGFnZWQucHJpbnQgPSBGfQpzaG93X3F1ZXJ5KG51bWJlcl9vZl9wbG90cykKYGBgCgoKQnV0IHRoZXJlIGFyZSBzb21lIGZ1bmN0aW9ucyB0aGF0IGNhbiBvbmx5IGJlIHJ1biBpbnNpZGUgUi4gRm9yIHRob3NlLCB5b3UgaGF2ZSB0byBmb3JjZSB0aGUgbGF6eSBxdWVyeSB0byBleGVjdXRlIGZ1bGx5LCB3aXRoICoqYGNvbGxlY3QoKWAqKi4KCmBgYHtyIHBhZ2VkLnByaW50ID0gRn0KdGJsKGNvbiwgInRyZWF0bWVudHMiKSAlPiUgCiAgY29sbGVjdCgpICU+JSAKICBtdXRhdGUoCiAgICBsYWJlbCA9IHN0cmluZ3I6OnN0cl90b190aXRsZSh0aWxsYWdlKSwKICAgIFJlcCA9IHBsb3RfaWQgJS8lIDEwMAogICAgKQpgYGAKCllvdSBjYW4gdHJ5IHJ1bm5pbmcgaXQgd2l0aG91dCB0aGUgKipgY29sbGVjdCgpYCoqIGFuZCB5b3UnbGwgc2VlIHlvdSBnZXQgYSBmYWlybHkgdW5oZWxwZnVsIGVycm9yIG1lc3NhZ2UuIEhvd2V2ZXIsIGlmIHlvdSBzZWUgYW55IGtpbmQgb2YgZXJyb3IgdGhhdCBjbGFpbXMgYSBjb2x1bW4gaXNuJ3QgcHJlc2VudCB3aGVuIHlvdSBrbm93IGl0J3MgdGhlcmUsIG9yIGEgZnVuY3Rpb24gaXMgbWlzc2luZywgdGhhdCdzIGEgZ29vZCBpbmRpY2F0b3Igb2YgYSBmYWlsZWQgbGF6eSBxdWVyeS4KCkhlcmUncyBhIGhhbmR5IHJlZmVyZW5jZSBmb3IgdGhlIGZ1bmN0aW9ucyB0aGF0IGB7ZGJwbHlyfWAgY3VycmVudGx5IGtub3dzIGhvdyB0byB0cmFuc2xhdGUgYW5kIHJ1biBpbnNpZGUgdGhlIERCOiBbaHR0cHM6Ly9kYnBseXIudGlkeXZlcnNlLm9yZy9hcnRpY2xlcy9zcWwtdHJhbnNsYXRpb24uaHRtbF0oaHR0cHM6Ly9kYnBseXIudGlkeXZlcnNlLm9yZy9hcnRpY2xlcy9zcWwtdHJhbnNsYXRpb24uaHRtbCl7dGFyZ2V0PSJfYmxhbmsifQoKIyBKb2lucwoKT25lIG9mIHRoZSBtb3N0IHBvd2VyZnVsIGZ1bmN0aW9ucyBpbiBTUUwgaXMgdGhlIGpvaW4uIFRoZXJlIGFyZSBzZXZlcmFsIHR5cGVzIG9mIGpvaW5zLCBhbmQgaXQgdXN1YWxseSB0YWtlcyBhIGxpdHRsZSB0aGlua2luZyB0byBtYWtlIHN1cmUgeW91J3JlIGdldHRpbmcgdGhlIG9uZSB5b3Ugd2FudC4gSWYgdGhlcmUncyBhIG9uZS10by1vbmUgcmVsYXRpb25zaGlwIHdpdGggeW91ciBrZXlzIChlYWNoIHJvdyBpcyBpZGVudGlmaWVkIGJ5IG9uZSAqKmBwbG90X2lkYCoqLCBhbmQgZWFjaCAqKmBwbG90X2lkYCoqIG9ubHkgcmVmZXJzIHRvIG9uZSByb3cgaW4gYSB0YWJsZSwgZm9yIGV4YW1wbGUpLCB0aGVuIGEgKipMRUZUIEpPSU4qKiBpcyBhIGdvb2QgYmV0LgoKIyMgVmFuaWxsYSBTUUwKYGBge3IgcGFnZWQucHJpbnQgPSBGfQpkYkdldFF1ZXJ5KAogIGNvbiwgIgpTRUxFQ1QgKiBGUk9NIHRyZWF0bWVudHMgCiAgTEVGVCBKT0lOIHlpZWxkcyAKICBPTiB0cmVhdG1lbnRzLnBsb3RfaWQgPSB5aWVsZHMucGxvdF9pZDsKICAiCikKYGBgCgpOb3RpY2UgdGhhdCAqKmBwbG90X2lkYCoqIGdldHMgcHJpbnRlZCB0d2ljZSwgdGhpcyBpcyBhIHF1aXJrIG9mIFNRTC4gV2hpbGUgaXQgaXMgbmljZSB0byBtYWtlIHN1cmUgdGhhdCB0aGUgcmlnaHQgY29sdW1ucyB3ZXJlIG1hdGNoZWQsIGl0J3MgdXN1YWxseSBqdXN0IGluIHRoZSB3YXkuIFRoZXJlIGFyZSBjbGVhbmVyIHdheXMgdG8gZ2V0IHlvdXIgcXVlcnkgdXNpbmcgdGlkeSBSIHN5bnRheC4KCiMjIFRpZHkgUgpgYGB7ciBwYWdlZC5wcmludCA9IEZBTFNFfQp5aWVsZF9kYXRhIDwtIAogIGxlZnRfam9pbigKICAgIHRibChjb24sICJ0cmVhdG1lbnRzIiksIAogICAgdGJsKGNvbiwgInlpZWxkcyIpCiAgICApCgp5aWVsZF9kYXRhCmBgYAoKQWdhaW4sIG5vdGljZSB0aGF0IHRoaXMgaXMgYSBsYXp5IHF1ZXJ5ISBUaGUgam9pbiBoYXBwZW5lZCBpbnNpZGUgdGhlIERCLiBUaGlzIGNhbiBiZSBNVUNIIGZhc3RlciB0aGFuIGluc2lkZSBSIGZvciBsYXJnZSBjb21wbGV4IGpvaW5zLiBXZSBjYW4gdGhlbiAqKmBjb2xsZWN0KClgKiogdGhlIHJlc3VsdCBhbmQgdXNlIGl0IGZvciBmdXJ0aGVyIGFuYWx5c2lzLgoKT3RoZXIgdHlwZXMgb2Ygam9pbnMgeW91IG1pZ2h0IHJ1biBpbnRvIGFyZSAqKmZ1bGwqKi8qKm91dGVyKiovKipjcm9zcyoqLCAqKmlubmVyKiosICoqcmlnaHQqKiwgKipzZW1pKiosIGFuZCAqKmFudGkqKi4gVGhlIGhlbHAgcGFnZSBhdCAqKmA/ZHBseXI6OmZ1bGxfam9pbmAqKiBpcyBhIGdvb2QgcmVzb3VyY2UgZm9yIGV4cGxhaW5pbmcgdGhlbSwgYW5kIHRoZXJlIGFyZSBtYW55IG9ubGluZSB0dXRvcmlhbHMgYXMgd2VsbCAoZS5nLiBbaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9yZWxhdGlvbmFsLWRhdGEuaHRtbCN1bmRlcnN0YW5kaW5nLWpvaW5zXShodHRwczovL3I0ZHMuaGFkLmNvLm56L3JlbGF0aW9uYWwtZGF0YS5odG1sI3VuZGVyc3RhbmRpbmctam9pbnMpe3RhcmdldD0iX2JsYW5rfSkuIEhvd2V2ZXIsIG5vdCBhbGwgdGhlIGpvaW5zIGFyZSBpbXBsZW1lbnRlZCBpbiBTUUxpdGUsIHNvIHlvdSBtYXkgaGF2ZSB0byAqKmBjb2xsZWN0KClgKiogYW5kIGRvIHRoZW0gbG9jYWxseSBpbnNpZGUgUi4gSSdtIGZhaXJseSBzdXJlIGFsbCBvZiB0aGVtIGFyZSBpbXBsZW1lbnRlZCBpbiBge1JQb3N0Z3Jlc31gIHRob3VnaCwgc28gd2UnbGwgbG9vayBhdCB0aGVtIG1vcmUgd2hlbiB3ZSByZWFkIHRoZSBQU0EgT24tRmFybSBkYXRhYmFzZS4KCi0tLS0KCiMgUHV0dGluZyBpdCBhbGwgdG9nZXRoZXIKCmBgYHtyIHBhZ2VkLnByaW50ID0gRkFMU0V9CmRhdGFfc3VtbWFyeSA8LSBsZWZ0X2pvaW4oCiAgdGJsKGNvbiwgInRyZWF0bWVudHMiKSwKICB0YmwoY29uLCAieWllbGRzIikKKSAlPiUgCiAgZ3JvdXBfYnkoY3JvcCwgdGlsbGFnZSkgJT4lIAogIHN1bW1hcmlzZSgKICAgIG1lYW4gPSBtZWFuKE1nX2hhLCBuYS5ybSA9IFRSVUUpLCAKICAgIHNkID0gc2QoTWdfaGEsIG5hLnJtID0gVFJVRSkKICAgICkKCnNob3dfcXVlcnkoZGF0YV9zdW1tYXJ5KQoKZGF0YV9zdW1tYXJ5CmBgYAoKIyMgQ2xlYW5pbmcgdXAKCkl0J3Mgbm90IHN0cmljdGx5IHJlcXVpcmVkIHRvIGNsb3NlIGNvbm5lY3Rpb25zIG1hbnVhbGx5LCBiZWNhdXNlIFIgd2lsbCBjbG9zZSBpbmFjdGl2ZSBvbmVzIGV2ZW50dWFsbHkuIEJ1dCBpdCdzIGdvb2QgcHJhY3RpY2UgdG8gZG8sIHNvIHRoYXQgaXQgd2lsbCBiZSBhIGhhYml0IHdoZW4geW91J3JlIGNvbm5lY3RpbmcgdG8gcmVtb3RlIGRhdGFiYXNlcyB0aGF0IG1heSBoYXZlIG1hbnkgY29uY3VycmVudCB1c2Vycy4KCmBgYHtyfQpkYkRpc2Nvbm5lY3QoY29uKQpgYGAK

precisionsustainableag.org


Icons: fontawesome.com, BY 4.0 | This work, BY-SA 4.0