]> code.communitydata.science - stats_class_2020.git/blobdiff - r_tutorials/w04-R_tutorial.html
tweaks to incorporate read_csv and lubridate refs
[stats_class_2020.git] / r_tutorials / w04-R_tutorial.html
index 9a5cb2ee9304b1f0a8438a6e284999a2fa9d914e..46618e4d941159882dc65b539cd5d34a2e6d86ca 100644 (file)
@@ -1572,8 +1572,54 @@ head(more.cars)</code></pre>
 <pre><code>## 
 ## TRUE 
 ##  384</code></pre>
-<p>When find yourself trying to load a tabular data file that consists of plain text, but has some idiosyncratic difference from a csv (e.g., it is tab-separated instead of comma-separated), you should use <code>read.delim()</code>.</p>
-<p>How will you know what to use? Get to know your data first! Seriously, try opening it the file (or at least opening up part of it) using a text editor and/or spreadsheet software. Looking at the “raw” plain text can help you figure out what arguments you need to use to make the data load up exactly the way you want it.</p>
+<p>When find yourself trying to load a tabular data file that consists of plain text, but has some idiosyncratic difference from a csv (e.g., it is tab-separated instead of comma-separated), you may want to use <code>read.delim()</code>.</p>
+<p>Note that the Tidyverse has some related functions, namely <code>read_csv()</code> that can also import csv’s very efficiently and with helpful defaults to try and guess what kinds of variables you’re working with. The guesses are usually pretty good! Here’s an example using <code>read_csv()</code></p>
+<pre class="r"><code>library(tidyverse)</code></pre>
+<pre><code>## ── Attaching packages ─────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.0 ──</code></pre>
+<pre><code>## ✓ ggplot2 3.3.2     ✓ purrr   0.3.4
+## ✓ tibble  3.0.3     ✓ dplyr   1.0.2
+## ✓ tidyr   1.1.2     ✓ stringr 1.4.0
+## ✓ readr   1.3.1     ✓ forcats 0.5.0</code></pre>
+<pre><code>## ── Conflicts ────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
+## x dplyr::filter() masks stats::filter()
+## x dplyr::lag()    masks stats::lag()</code></pre>
+<pre class="r"><code>yet.more.cars &lt;- read_csv(url(&quot;https://communitydata.cc/~ads/teaching/2019/stats/data/week_03/mtcars.csv&quot;))</code></pre>
+<pre><code>## Warning: Missing column names filled in: &#39;X1&#39; [1]</code></pre>
+<pre><code>## Parsed with column specification:
+## cols(
+##   X1 = col_character(),
+##   mpg = col_double(),
+##   cyl = col_double(),
+##   disp = col_double(),
+##   hp = col_double(),
+##   drat = col_double(),
+##   wt = col_double(),
+##   qsec = col_double(),
+##   vs = col_double(),
+##   am = col_double(),
+##   gear = col_double(),
+##   carb = col_double()
+## )</code></pre>
+<pre class="r"><code>yet.more.cars</code></pre>
+<pre><code>## # A tibble: 32 x 12
+##    X1            mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
+##    &lt;chr&gt;       &lt;dbl&gt; &lt;dbl&gt; &lt;dbl&gt; &lt;dbl&gt; &lt;dbl&gt; &lt;dbl&gt; &lt;dbl&gt; &lt;dbl&gt; &lt;dbl&gt; &lt;dbl&gt; &lt;dbl&gt;
+##  1 Mazda RX4    21       6  160    110  3.9   2.62  16.5     0     1     4     4
+##  2 Mazda RX4 …  21       6  160    110  3.9   2.88  17.0     0     1     4     4
+##  3 Datsun 710   22.8     4  108     93  3.85  2.32  18.6     1     1     4     1
+##  4 Hornet 4 D…  21.4     6  258    110  3.08  3.22  19.4     1     0     3     1
+##  5 Hornet Spo…  18.7     8  360    175  3.15  3.44  17.0     0     0     3     2
+##  6 Valiant      18.1     6  225    105  2.76  3.46  20.2     1     0     3     1
+##  7 Duster 360   14.3     8  360    245  3.21  3.57  15.8     0     0     3     4
+##  8 Merc 240D    24.4     4  147.    62  3.69  3.19  20       1     0     4     2
+##  9 Merc 230     22.8     4  141.    95  3.92  3.15  22.9     1     0     4     2
+## 10 Merc 280     19.2     6  168.   123  3.92  3.44  18.3     1     0     4     4
+## # … with 22 more rows</code></pre>
+<pre class="r"><code>table(yet.more.cars == my.mtcars)</code></pre>
+<pre><code>## 
+## TRUE 
+##  384</code></pre>
+<p>How will you know what to use? The best practice is always to get to know your data first! Seriously, try opening it the file (or at least opening up part of it) using a text editor and/or spreadsheet software. Looking at the “raw” plain text can help you figure out what arguments you need to use to make the data load up exactly the way you want it.</p>
 <p>For example, you might notice that my import of the mtcars.csv file introduces an important difference from the original <code>mtcars</code> dataset. In the original <code>mtcars</code>, the car model names are <code>row.names</code> attributes of the dataframe instead of a variable.</p>
 <pre class="r"><code>head(mtcars)</code></pre>
 <pre><code>##                    mpg cyl disp  hp drat    wt  qsec vs am gear carb
@@ -1595,7 +1641,7 @@ head(more.cars)</code></pre>
 ## [25] &quot;Pontiac Firebird&quot;    &quot;Fiat X1-9&quot;           &quot;Porsche 914-2&quot;      
 ## [28] &quot;Lotus Europa&quot;        &quot;Ford Pantera L&quot;      &quot;Ferrari Dino&quot;       
 ## [31] &quot;Maserati Bora&quot;       &quot;Volvo 142E&quot;</code></pre>
-<p>Since it’s the first column of the raw data, you can fix this with an additional argument to <code>read.csv</code>:</p>
+<p>Since it’s the first column of the raw data, you can fix this with an additional argument to <code>read.csv</code> (I’m sure there’s also a way to do this with <code>read_csv()</code> but I didn’t look it up in time to include in this tutorial):</p>
 <pre class="r"><code>my.mtcars &lt;- read.csv(url(&quot;https://communitydata.science/~ads/teaching/2020/stats/data/week_04/mtcars.csv&quot;), 
                       row.names=1)
 head(my.mtcars)</code></pre>
@@ -1639,16 +1685,9 @@ sapply(mtcars[variables], function(v){
 <div id="conditional-means-in-tidyverse-code" class="section level2">
 <h2><span class="header-section-number">2.2</span> Conditional means in Tidyverse code</h2>
 <p>I should reiterate here that the <code>*apply</code> functions are part of Base R. Other functions to do similar things exist and you may find these other functions more intelligible or useful for you. In particular, this is the sort of task that the Tidyverse handles in an arguably more intuitive fashion than Base R because it allows you to organize functions as a sequence of actions with function names that are verbs and this generally leads to more readable code. Here’s an example snippet that replicates the same output. :</p>
-<pre class="r"><code>library(tidyverse)</code></pre>
-<pre><code>## ── Attaching packages ───────────────────────────────────────────────────── tidyverse 1.3.0 ──</code></pre>
-<pre><code>## ✓ ggplot2 3.3.2     ✓ purrr   0.3.4
-## ✓ tibble  3.0.1     ✓ dplyr   1.0.2
-## ✓ tidyr   1.1.1     ✓ stringr 1.4.0
-## ✓ readr   1.3.1     ✓ forcats 0.5.0</code></pre>
-<pre><code>## ── Conflicts ──────────────────────────────────────────────────────── tidyverse_conflicts() ──
-## x dplyr::filter() masks stats::filter()
-## x dplyr::lag()    masks stats::lag()</code></pre>
-<pre class="r"><code>mtcars %&gt;% 
+<pre class="r"><code>library(tidyverse)
+
+mtcars %&gt;% 
   group_by(cyl) %&gt;%
   summarize( across(
     .cols=c(mpg, disp, hp, wt),
@@ -1750,9 +1789,9 @@ head(cyl.conditional.means)</code></pre>
 <p>Again, you could run <code>row.names(cyl.conditional.means)</code> &lt;- NULL` to reset the row numbers.</p>
 <p>By now you might have noticed that a some of my code in this section replicates parts of the output that the Tidyverse example above created by default. As I said earlier, the Tidyverse really shines when it comes to tidying data (shocking, I know). There are also functions (“verbs”) in the Tidyverse libraries to perform all of the additional steps (such as sorting data by the values of a column). If you are interested in doing so, check out more of the <a href="https://tidyverse.org/">Tidyverse documentation</a> and the <a href="https://r4ds.had.co.nz/">Wickham and Grolemund <em>R for Data Science</em> book</a> listed among the course resources.</p>
 </div>
-<div id="working-with-dates" class="section level2">
-<h2><span class="header-section-number">2.5</span> Working with dates</h2>
-<p>Date and time objects are another more advanced data class in R. Managing date and time data can be a headache. This is because dates and times tend to be formatted inconsistently and are usually given to you as character variables, so you will need to transform them into a format that R can “understand” as dates. There are many packages for working with dates and times, but for now I’ll introduce you to the Base R way of doing so. This uses a data type formally called “POSIX” (no need to worry about why it’s called that).</p>
+<div id="working-with-dates-in-base-r" class="section level2">
+<h2><span class="header-section-number">2.5</span> Working with dates (in Base R)</h2>
+<p>Date and time objects are another more advanced data class in R. Managing date and time data can be a headache. This is because dates and times tend to be formatted inconsistently and are usually given to you as character variables, so you should know how to transform them into a format that R can “understand” as dates. There are many packages for working with dates and times, but for now I’ll introduce you to the Base R way of doing so. This uses a data type formally called “POSIX” (no need to worry about why it’s called that).</p>
 <p>To build up an example, I’ll create some date-time values, add a little noise, and convert them into a character vector:</p>
 <pre class="r"><code>add.an.hour &lt;- seq(0, 3600*24, by=3600)
 some.hours &lt;- as.character(Sys.time() + add.an.hour) ## Look up Sys.time() to see what it does.</code></pre>
@@ -1760,34 +1799,48 @@ some.hours &lt;- as.character(Sys.time() + add.an.hour) ## Look up Sys.time() to
 <pre class="r"><code>class(some.hours)</code></pre>
 <pre><code>## [1] &quot;character&quot;</code></pre>
 <pre class="r"><code>head(some.hours)</code></pre>
-<pre><code>## [1] &quot;2020-09-28 12:06:04&quot; &quot;2020-09-28 13:06:04&quot; &quot;2020-09-28 14:06:04&quot;
-## [4] &quot;2020-09-28 15:06:04&quot; &quot;2020-09-28 16:06:04&quot; &quot;2020-09-28 17:06:04&quot;</code></pre>
+<pre><code>## [1] &quot;2020-09-29 12:23:26&quot; &quot;2020-09-29 13:23:26&quot; &quot;2020-09-29 14:23:26&quot;
+## [4] &quot;2020-09-29 15:23:26&quot; &quot;2020-09-29 16:23:26&quot; &quot;2020-09-29 17:23:26&quot;</code></pre>
 <p>These are beautifully formatted timestamps, but R will not understand them as such. This is often how you might receive data in, for example, a dataset you import from Qualtrics, scrape from the web, or elsehwere. You can convert the <code>some.hours</code> vector into an object class that R will recognize as a time object using the <code>as.POSIXct()</code> function. Notice that it even adds a timezone back in!</p>
 <pre class="r"><code>as.POSIXct(some.hours)</code></pre>
-<pre><code>##  [1] &quot;2020-09-28 12:06:04 CDT&quot; &quot;2020-09-28 13:06:04 CDT&quot;
-##  [3] &quot;2020-09-28 14:06:04 CDT&quot; &quot;2020-09-28 15:06:04 CDT&quot;
-##  [5] &quot;2020-09-28 16:06:04 CDT&quot; &quot;2020-09-28 17:06:04 CDT&quot;
-##  [7] &quot;2020-09-28 18:06:04 CDT&quot; &quot;2020-09-28 19:06:04 CDT&quot;
-##  [9] &quot;2020-09-28 20:06:04 CDT&quot; &quot;2020-09-28 21:06:04 CDT&quot;
-## [11] &quot;2020-09-28 22:06:04 CDT&quot; &quot;2020-09-28 23:06:04 CDT&quot;
-## [13] &quot;2020-09-29 00:06:04 CDT&quot; &quot;2020-09-29 01:06:04 CDT&quot;
-## [15] &quot;2020-09-29 02:06:04 CDT&quot; &quot;2020-09-29 03:06:04 CDT&quot;
-## [17] &quot;2020-09-29 04:06:04 CDT&quot; &quot;2020-09-29 05:06:04 CDT&quot;
-## [19] &quot;2020-09-29 06:06:04 CDT&quot; &quot;2020-09-29 07:06:04 CDT&quot;
-## [21] &quot;2020-09-29 08:06:04 CDT&quot; &quot;2020-09-29 09:06:04 CDT&quot;
-## [23] &quot;2020-09-29 10:06:04 CDT&quot; &quot;2020-09-29 11:06:04 CDT&quot;
-## [25] &quot;2020-09-29 12:06:04 CDT&quot;</code></pre>
+<pre><code>##  [1] &quot;2020-09-29 12:23:26 CDT&quot; &quot;2020-09-29 13:23:26 CDT&quot;
+##  [3] &quot;2020-09-29 14:23:26 CDT&quot; &quot;2020-09-29 15:23:26 CDT&quot;
+##  [5] &quot;2020-09-29 16:23:26 CDT&quot; &quot;2020-09-29 17:23:26 CDT&quot;
+##  [7] &quot;2020-09-29 18:23:26 CDT&quot; &quot;2020-09-29 19:23:26 CDT&quot;
+##  [9] &quot;2020-09-29 20:23:26 CDT&quot; &quot;2020-09-29 21:23:26 CDT&quot;
+## [11] &quot;2020-09-29 22:23:26 CDT&quot; &quot;2020-09-29 23:23:26 CDT&quot;
+## [13] &quot;2020-09-30 00:23:26 CDT&quot; &quot;2020-09-30 01:23:26 CDT&quot;
+## [15] &quot;2020-09-30 02:23:26 CDT&quot; &quot;2020-09-30 03:23:26 CDT&quot;
+## [17] &quot;2020-09-30 04:23:26 CDT&quot; &quot;2020-09-30 05:23:26 CDT&quot;
+## [19] &quot;2020-09-30 06:23:26 CDT&quot; &quot;2020-09-30 07:23:26 CDT&quot;
+## [21] &quot;2020-09-30 08:23:26 CDT&quot; &quot;2020-09-30 09:23:26 CDT&quot;
+## [23] &quot;2020-09-30 10:23:26 CDT&quot; &quot;2020-09-30 11:23:26 CDT&quot;
+## [25] &quot;2020-09-30 12:23:26 CDT&quot;</code></pre>
 <p>If things aren’t formatted in quite the way R expects, you can also tell it how to parse a character string as a POSIXct object:</p>
-<pre class="r"><code>m &lt;- &quot;2019-02-21 04:35:00&quot;
-class(m)</code></pre>
+<pre class="r"><code>sometime &lt;- &quot;2019-02-21 04:35:00&quot;
+class(sometime)</code></pre>
 <pre><code>## [1] &quot;character&quot;</code></pre>
-<pre class="r"><code>a.good.time &lt;- as.POSIXct(m, format=&quot;%Y-%m-%d %H:%M:%S&quot;, tz=&quot;CDT&quot;)
+<pre class="r"><code>a.good.time &lt;- as.POSIXct(sometime, format=&quot;%Y-%m-%d %H:%M:%S&quot;, tz=&quot;CDT&quot;)
 class(a.good.time)</code></pre>
 <pre><code>## [1] &quot;POSIXct&quot; &quot;POSIXt&quot;</code></pre>
 <p>Once you have a time object, you can even do date arithmetic with <code>difftime()</code> (but watch out as this can get complicated):</p>
 <pre class="r"><code>difftime(Sys.time(), a.good.time, units=&quot;weeks&quot;)</code></pre>
-<pre><code>## Time difference of 83.64594 weeks</code></pre>
+<pre><code>## Time difference of 83.79052 weeks</code></pre>
 <p>This calculated the number of weeks elapsed between the current time and an example date/time I created above.</p>
+<div id="as.date-vs.-as.posix" class="section level3">
+<h3><span class="header-section-number">2.5.1</span> <code>as.Date</code> vs. <code>as.POSIX()</code></h3>
+<p>The <code>as.Date()</code> function provides an alternative to <code>as.POSIX()</code> that is far more memorable and readable, but far less precise. Note that it truncates the time of day and the timezone from the ouput. I’ll use the same</p>
+<pre class="r"><code>a.good.time &lt;- as.Date(sometime, format=&quot;%Y-%m-%d %H:%M:%S&quot;, tz=&quot;CDT&quot;)
+class(a.good.time)</code></pre>
+<pre><code>## [1] &quot;Date&quot;</code></pre>
+<pre class="r"><code>a.good.time</code></pre>
+<pre><code>## [1] &quot;2019-02-21&quot;</code></pre>
+</div>
+<div id="an-easier-way-most-of-the-time-in-the-tidyverse" class="section level3">
+<h3><span class="header-section-number">2.5.2</span> An easier way (most of the time) in the Tidyverse</h3>
+<p>The Tidyverse (via the <code>lubridate</code> package) usually handles dates and times quite well. When you need to import and work with date and time objects, you may find it best to try Tidyverse data import tools (e.g., <code>read_csv()</code>) as a starting point for this reason.</p>
+<p>I <em>highly recommend</em> reading <a href="https://r4ds.had.co.nz/dates-and-times.html">this chapter</a> of Wickham and Grolemund on dates and times as they introduce a number of challenges and nuances that can befuddle even the most brilliant statistical programmers.</p>
+</div>
 </div>
 </div>
 <div id="creating-repeated-sequences-distributions-and-samples" class="section level1">
@@ -1805,31 +1858,31 @@ head(odds)</code></pre>
 <pre class="r"><code>more.odds &lt;- rep(seq(from=1, to=100, by=2), 5)</code></pre>
 <p>You can use <code>sample()</code> to draw samples from an object:</p>
 <pre class="r"><code>sample(x=odds, size=3)</code></pre>
-<pre><code>## [1] 73 59 87</code></pre>
+<pre><code>## [1] 39 13 71</code></pre>
 <pre class="r"><code>sample(x=evens, size=3)</code></pre>
-<pre><code>## [1] 12 36 14</code></pre>
+<pre><code>## [1] 58 22 32</code></pre>
 <p>You can also sample “with replacement.” Here I take 100 random draws from the binomial distribution, which is analogous to 100 independent trials of, say, a coin flip:</p>
 <pre class="r"><code>draws &lt;- sample(x=c(0,1), size=100, replace=TRUE)
 
 table(draws)</code></pre>
 <pre><code>## draws
 ##  0  1 
-## 51 49</code></pre>
+## 52 48</code></pre>
 <p>What if you wanted to take a random set of 10 observations from a dataframe? You can use <code>sample</code> on an index of the rows:</p>
 <pre class="r"><code>odds.n.evens &lt;- data.frame(odds, evens)
 
 odds.n.evens[ sample(row.names(odds.n.evens), 10), ]</code></pre>
 <pre><code>##    odds evens
-## 34   67    68
-## 12   23    24
-## 33   65    66
 ## 35   69    70
-## 43   85    86
-## 15   29    30
+## 44   87    88
 ## 5     9    10
-## 10   19    20
-## 23   45    46
-## 9    17    18</code></pre>
+## 3     5     6
+## 43   85    86
+## 8    15    16
+## 36   71    72
+## 21   41    42
+## 47   93    94
+## 41   81    82</code></pre>
 <div id="managing-randomness" class="section level2">
 <h2><span class="header-section-number">3.1</span> Managing randomness</h2>
 <p>Try running one of the <code>sample</code> commands above again. You will (probably!) get a different result because <code>sample</code> makes a random draw each time it runs.</p>
@@ -1989,7 +2042,7 @@ $(document).ready(function ()  {
       theme: "bootstrap3",
       context: '.toc-content',
       hashGenerator: function (text) {
-        return text.replace(/[.\\/?&!#<>]/g, '').replace(/\s/g, '_').toLowerCase();
+        return text.replace(/[.\\/?&!#<>]/g, '').replace(/\s/g, '_');
       },
       ignoreSelector: ".toc-ignore",
       scrollTo: 0

Community Data Science Collective || Want to submit a patch?