Activity: Getting Started

From a question in the field to your first plot in R

Hands-on worksheet: forming a testable question, organizing a project, entering field data in Excel, and writing your first R script and ggplot.
Author

Bill Perry

Worksheet: Getting Started

How to use this worksheet

Work through each part in order, at your own pace. Type every line of code yourself into a plain R script — do not copy-paste. Blocks marked ▶ Run this are code you should type and execute. Blocks marked ✏️ Your turn ask you to write, modify, or answer something. Boxes marked 🚀 If you finish early and Part 15 (Going further) are optional bonus material.


Part 1 · Today’s question

Recap

  • We are asking whether pine needle length differs between the shady side and the sunny side of a tree.
  • The tree, not the needle, is our unit of replication — one tree is not enough (this is called pseudoreplication).

✏️ Your turn: Write your own null and alternate hypotheses in your own words.

H0:


Ha:

✏️ Your turn: Is the following statement inductive or deductive reasoning? “I measured needles on three trees, all showed the sheltered side longer, so I expect a fourth tree to show the same pattern.” Why can’t we test a hypothesis like this using only one tree?

Reasoning type:


Why one tree isn't enough:

Field collection plan

As a group, agree on and record your answers before you go outside:

Sample height on tree:



Definition of "mature" needle:



Needles per side, per tree:



Label convention (n/s? shady/sunny?):

Part 2 · Build your project folder

Why a project folder?

Everything for this project lives in one folder, so every file path is relative to it — no more hunting for where a file went.

Create the folder structure

Make a new folder called pine_project. Inside it, create four sub-folders:

pine_project/
├── data/       ← your CSV and Excel files go here
├── scripts/    ← your .R code goes here
├── figures/    ← plots you save go here
└── output/    ← place to save modified dataframes

Copy pine_needles.csv into pine_project/data/.

⚠️ Important: Leave this copy alone — it is raw data. We will read it but never overwrite it. (Your own field data will get cleaned up and added here starting in Wrangling Your Data.)

✏️ Your turn: Full path to your pine_project folder:

_________________________________________________________________________________________________________________________________________________________________


Part 3 · Enter your data in Excel

Before R ever sees your numbers, they have to get from your field notes into a spreadsheet. How you do that now determines how much pain you’re in later — so we do it deliberately.

Set up your header row first

Open Excel and make row 1 your column headers — nothing else goes in that row. Use the same controlled vocabulary your group already agreed on in Part 1, written the lower_snake_case way (no spaces, no units hidden in the text): what will they be?

Column units? What goes in it

⚠️ Watch out! Needle Length (mm) and length_mm look equally clear to a human — only one of them is a header R can use without a fight. Spaces, parentheses, and units in the header text all cause problems later.

One row = one measurement

This is the part people get wrong under time pressure: every single needle you measured gets its own row. Not one row per tree with six length columns crammed in — one row per needle, with tree_no, n_s, and sun repeated on every row that needle belongs to. Yes, that means typing n and shady over and over. That repetition is doing real work — it’s what lets R filter, group, and plot by any of those columns later.

Warning

⚠️ Watch out! — Common Excel data-entry mistakes

  • Merged cells. They look nice, they break every import. Never merge.
  • Blank rows or blank columns used as visual spacers. R reads them as missing data, not as whitespace.
  • More than one header row, or notes typed above row 1. R will try to read your note as a column name.
  • Numbers stored as text. 20 mm in a cell is text; 20 is a number. Keep units in the header, not in the cell.
  • Inconsistent spelling of the same categorySunny, sunny (trailing space), and sun are three different values to R, even though they mean the same thing to you.
  • Color-coding instead of a column. Highlighting a row yellow to mean something is invisible to R. If it matters, it needs its own column.

Enter your group’s data

▶ Do this now: Using the measurements your group collected today, enter one row per needle into Excel using the header row above.

✏️ Your turn: How many rows should your sheet have, if your group measured ______________ needles per

side, per tree, on _____________ trees? Show your arithmetic: ________________________

Tip

🖐 Honest preview — this probably isn’t “tidy” yet

Excel invites you to spread related numbers across columns — one column per tree, or one column per needle. That’s a completely normal way to enter data quickly, and it is not the same as tidy data: the rule (from R for Data Science) that every variable is a column, every observation is a row, and every value is a cell. If your sheet doesn’t fully follow that yet, that’s fine — turning a wide, human-friendly layout into a tidy one is exactly what we’ll do with pivot_longer() in Wrangling Your Data. Today, just get the numbers in accurately.

Save it — both formats

File → Save As, into your pine_project/data/ folder, twice:

  1. Once as an Excel workbook (.xlsx) — keeps any formatting you added.
  2. Once as CSV UTF-8 (.csv) — plain text, no hidden formatting, and the format every other tool (including R) can read without a special library.

⚠️ Important: Save both into data/, and — like pine_needles.csv — treat them as raw data from this point on: read them, never hand-edit them again. Corrections happen in R, where every change is a line of code you can see and undo.

✏️ Your turn: What are the two file names you just saved, and are they both sitting in pine_project/data/?


______________________________________________________________________________________


Part 4 · Orient yourself in Positron

Open your project folder as your workspace: File → Open Folder…

Four panes you’ll use constantly:

Pane Where What it does
Editor top-left your scripts live here
Console bottom where code actually runs
Environment right every object you’ve stored
Plots / Files right (tab) your figures and project files

✏️ Your turn: Click the Console tab. Type 1 + 1 and press Enter. Result: _____________


Part 5 · R as a calculator, and storing values

▶ Run this in the Console:

3 + 5
12 / 7
2 ^ 10
sqrt(144)

⚠️ Watch out! A + at the start of a console line (instead of the usual > prompt) means R is still waiting for you to finish typing something. Press Esc to cancel.

Now store a value with the assignment operator <- (shortcut: Alt/Option + -):

▶ Run this:

x <- 7      # store 7 under the name x
x           # read it back
x * 2       # use it in math

Look for x in the Environment pane.

✏️ Your turn: Store the number 42 as my_number, then multiply it by x. Result: __________________

# Write your code here:
Tip

🚀 If you finish early: Try x / my_number and my_number %% x (the remainder operator). Predict each result before you run it.


Part 6 · Naming things well

Good names make code readable months later: be explicit (needle_length, not x2), never start with a number, and R is case-sensitive (length_mmLength_mm). Use lower_snake_case.

✏️ Your turn: Which of these are valid R object names? Circle Y or N.

needle_length     Y / N
3rd_needle        Y / N
Length_mm         Y / N
my.needle.data    Y / N
n_s               Y / N

Part 7 · Comments

Anything after # is ignored by R — a note for humans.

▶ Run this in your Script:

# average of four needle lengths, in mm
needle_length <- c(20, 21, 23, 25)

mean(needle_length)   # average needle length

💡 Key idea: If a line confused you while writing it, comment why you did it — future you will forget.


Part 8 · Functions and arguments

A function is called by name and takes arguments in ().

▶ Run this in the Console:

sqrt(10)
round(3.14159)             # default: 0 decimal places
round(3.14159, 2)          # 2 decimal places
round(x = 3.14159, digits = 2)

Stuck? ?round opens the help page.

✏️ Your turn: Use round() to round pi (R knows this by name) to 4 decimal places. Result: _______________

# Write your code here:

Part 9 · Vectors and data types

A vector is a series of values built with c() which is also called a concatenated list and we will see it more. A spreadsheet column is really just a vector.

▶ Run this in your Script:

length_mm <- c(20, 21, 23, 25)     # numeric vector
side      <- c("n", "s")           # character vector — needs quotes

length(length_mm)   # how many values?
class(length_mm)    # what type?

⚠️ Watch out! "n" in quotes is text. Without quotes, R looks for an object called n and errors if none exists.

✏️ Your turn: Make a character vector called my_sides with the values "n" and "s". Check its length() and class().

# Write your code here:
Tip

🚀 If you finish early: Make a numeric vector of 5 needle lengths you make up. Run mean() and sd() on it — both work directly on a vector, no data frame needed.


Part 10 · Packages and libraries

Install once, in the Console:

install.packages("tidyverse")

Load every session — put this at the very top of every script:

▶ Run this at the top of your Script:

# ── Packages ──────────────────────────────
library(tidyverse)

✏️ Your turn: First line R prints after library(tidyverse): ________________________


Part 11 · Load the pine needle data

Create your script

File → New File → R Script. Save it immediately as 01_getting_started.R inside pine_project/scripts/.

▶ Run this in your Script:

# ── Load data ─────────────────────────────
pine_df <- read_csv("data/pine_needles.csv")

pine_df    # print to console

The path "data/..." is relative to your project folder — it works on anyone’s computer, not just yours.

Always look before you trust it

▶ Run each of these:

head(pine_df)       # first 6 rows
dim(pine_df)         # (rows, columns)
names(pine_df)       # column names
glimpse(pine_df)     # one line per column: name, type, first values

✏️ Your turn: Answer from the output above.

Rows:              Columns:
Type of n_s:        Values in sun:

✏️ Your turn: Look closely at n_s and sun. Do they ever disagree (n paired with sunny, or s paired with shady)? Why might a dataset include two columns that encode the same grouping? ________________________


Part 12 · Your first plot

Every ggplot is built from three pieces, joined with +: data, aes() (which columns map to x/y), and a geom (how to draw it).

▶ Run this — the simplest possible plot:

ggplot(pine_df, aes(x = n_s, y = length_mm)) +
  geom_point()

⚠️ Watch out! The + sits at the end of a line, never the start.

Improve it one layer at a time

▶ Run this:

ggplot(pine_df, aes(x = sun, y = length_mm)) +
  geom_boxplot() +
  geom_jitter(width = 0.15, alpha = 0.6, color = "tomato") +
  labs(x = "Sun exposure",
       y = "Needle length (mm)",
       title = "Needle length by sun exposure")

✏️ Your turn: Make the same plot using n_s instead of sun on the x-axis. Copy the code above and change what’s needed.

# Write your modified code here:

What pattern do you see — does the sheltered or exposed side tend to have longer needles? ________________________

Tip

🚀 If you finish early: Try geom_violin() instead of geom_boxplot(), or add color = group inside aes() to see each field team’s data separately.


Part 13 · Save your plot

▶ Run this:

needle_plot <- ggplot(pine_df, aes(x = sun, y = length_mm)) +
  geom_boxplot() +
  geom_jitter(width = 0.15, alpha = 0.6, color = "tomato") +
  labs(x = "Sun exposure", y = "Needle length (mm)")

ggsave("figures/needle_length.png",
       plot   = needle_plot,
       width  = 3,
       height = 3,
       units  = "in",
       dpi    = 300)

Check your figures/ folder — the PNG should be there.

⚠️ Watch out! ggsave() wants the filename first, then plot =. Always use dpi = 300.


Part 14 · Review and checkpoint

At this point you can:

✏️ Your turn — before you move on: Run your whole script top to bottom (or line by line). Ran cleanly? Y / N — if not, the error was: ________________________

Note

📤 What to turn in before next class

Upload both of these to the course management system:

  1. Your code — the scripts/ folder (or just 01_getting_started.R)
  2. This worksheet, with your written answers

Part 15 · Going further — optional in class, or take-home if you’d like more practice

Work through this if you finish early. There are no wrong answers — the goal is to see what ggplot can do.

Histograms, split by group

▶ Try this:

ggplot(pine_df, aes(x = length_mm, fill = sun)) +
  geom_histogram(binwidth = 2, position = position_dodge2(width = 0.5)) +
  labs(x = "Needle length (mm)", y = "Count", fill = "Sun exposure")

✏️ Your turn: Are the two distributions similar in shape, or different? ________________________

Facets — one panel per group

ggplot(pine_df, aes(x = length_mm)) +
  geom_histogram(binwidth = 2, fill = "darkblue", color = "white") +
  facet_wrap(~group)

Violin plots

ggplot(pine_df, aes(x = sun, y = length_mm, fill = sun)) +
  geom_violin(alpha = 0.5) +
  geom_jitter(width = 0.1, size = 2) +
  theme(legend.position = "none")

Themes — change the look

Add any of these to a plot above:

+ theme_bw()
+ theme_minimal()
+ theme_classic()

✏️ Your turn: Which theme do you like best for this kind of biological comparison? Why? ________________________


What your finished project folder looks like

pine_project/
├── data/
│   └── pine_needles.csv               <- never touch this
├── scripts/
│   └── 01_getting_started.R           <- your complete script
└── figures/
    └── needle_length.png              <- Part 13

This is the structure we will use for every analysis this term.

Folder Contents Rule
data/ raw files read-only, never overwrite
scripts/ .R code one script per module
figures/ saved plots always PNG, always dpi = 300

Getting unstuck

When code breaks — and it will, that is normal:

  1. Read the error message out loud. R usually names the line and the problem.
  2. Check the usual suspects: did you run library(tidyverse)? Spelling? A missing ) or a + at the start of a line?
  3. ?function_name opens the built-in help page.
  4. Bring the exact error (copy-paste it) to class or office hours.

💡 Key idea: Every working scientist googles error messages daily. Getting stuck is not failing — it is the job.