Computer science crash course

CS crash course
Author

Pablo Fuenzalida

Published

July 21, 2026

Computer science crash course for ecologists

Types, memory, debugging, reproducibility. Light R.

Q1 · Programs, data, and R

A program is instructions that transform input data into output (tables, plots, model fits).

input_rows <- nrow(reef) # how many rows enter the pipeline
output_sites <- length(unique(reef$site)) # how many sites after summarising
c(input_rows = input_rows, output_sites = output_sites) # named result vector
  input_rows output_sites 
          60            3 

Q2 · Assignment and names

<- stores a value under a name (object).

mean_depth <- mean(reef$depth_m) # store summary under a clear name
mean_depth # retrieve by name
[1] 7.591667
rm(mean_depth) # remove binding (free name)

Q3 · Types and coercion

Every value has a type (numeric, character, logical, …).

typeof(reef$count) # should be double/integer
[1] "integer"
char_counts <- as.character(reef$count[1:3]) # simulate import as text
as.numeric(char_counts) # explicit conversion back
[1]  4 12  1

Q4 · Memory: copy vs modify

R often shares memory until you modify a copy (copy-on-write).

a <- reef$count # share column vector
b <- a # b points at same data until changed
b[1] <- 999L # replace triggers copy for b
a[1] # original column unchanged in reef
[1] 4

Q5 · Files and paths

Scripts live in directories; data live in files with paths.

getwd() # current working directory
[1] "/Users/owuss/Documents/GitHub/website-building/ai-tutorials/cs-crash-course"
file.path("ai-tutorials", "data", "reef_transects.csv") # portable path string
[1] "ai-tutorials/data/reef_transects.csv"
file.exists(data_path("reef_transects.csv")) # TRUE when CSV is found
[1] TRUE

Q6 · Functions as contracts

A function is a contract: inputs (arguments), output (return value), and side effects (files written, messages).

cpue <- function(catch, effort) { # catch / effort with guard
  if (effort <= 0) return(NA_real_) # invalid effort → missing
  catch / effort
}
cpue(catch = 40, effort = 8) # example call
[1] 5

Q7 · Boolean logic

Logical values (TRUE / FALSE) drive if, filter(), and indexing.

bad_depth <- reef$depth_m < 0 # logical vector
any(bad_depth, na.rm = TRUE) # any impossible depths?
[1] FALSE
all(!duplicated(acoustic$animal_id) | TRUE) # tags can repeat rows; logic demo
[1] TRUE
table(acoustic$detections > 3) # TRUE/FALSE counts

FALSE  TRUE 
   12    12 

Q8 · Algorithms: find and sort

An algorithm is a step-by-step recipe.

i_max <- which.max(fishery$tonnes) # row with largest landing
fishery[i_max, c("year", "species", "tonnes")] # inspect that record
  year species tonnes
9 2020 Snapper  144.5
order(fishery$tonnes, decreasing = TRUE)[1:3] # top three indices
[1]  9 13 12

Q9 · Big-O intuition for n rows

Big-O describes how work grows with n (rows).

n <- nrow(acoustic) # n for this file
c(rows = n, double_pass = 2 * n, nested_demo = length(unique(acoustic$animal_id)) * 10)
       rows double_pass nested_demo 
         24          48          30 

Q10 · Vectors, lists, tables

Vector = one type, one dimension.

str(reef$count) # atomic vector
 int [1:60] 4 12 1 7 21 6 7 2 9 20 ...
str(split(reef$count, reef$site)) # list of vectors
List of 3
 $ East : int [1:20] 6 13 4 5 26 11 10 8 8 26 ...
 $ North: int [1:20] 4 12 1 7 21 6 8 1 2 17 ...
 $ South: int [1:20] 6 7 2 9 20 15 10 0 4 22 ...
str(reef) # data.frame of columns
'data.frame':   60 obs. of  5 variables:
 $ transect_id: chr  "T01" "T01" "T01" "T01" ...
 $ site       : chr  "North" "North" "North" "North" ...
 $ species    : chr  "Parrotfish" "Surgeonfish" "Grouper" "Snapper" ...
 $ count      : int  4 12 1 7 21 6 7 2 9 20 ...
 $ depth_m    : num  6.2 6.2 6.2 6.2 6.2 5.2 5.2 5.2 5.2 5.2 ...

Q11 · Encoding and factors

Factors store levels (categories) efficiently; character stores full strings.

f <- as.factor(reef$site) # site as categorical
levels(f) # allowed labels
[1] "East"  "North" "South"
as.integer(f)[1:5] # internal level codes
[1] 2 2 2 2 2

Q12 · Reproducibility & seeds

Reproducibility means someone else (or future you) gets the same result from the same code and data.

set.seed(42) # lock RNG
sample(reef$transect_id, 3) # three random transects
[1] "T10" "T08" "T01"
set.seed(42) # same seed again
sample(reef$transect_id, 3) # identical draw
[1] "T10" "T08" "T01"

Q13 · Debugging strategies

Debugging is hypothesis testing: predict, inspect, narrow.

str(acoustic) # structure first
'data.frame':   24 obs. of  4 variables:
 $ animal_id : chr  "A101" "A101" "A101" "A101" ...
 $ receiver  : chr  "R11" "R2" "R6" "R1" ...
 $ detections: int  5 2 7 4 5 4 4 1 3 3 ...
 $ reef_zone : chr  "Slope" "Crest" "Slope" "Crest" ...
summary(acoustic$detections) # range and NA check
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  1.000   2.000   3.500   3.792   5.000   7.000 
stopifnot(nrow(acoustic) > 0) # assert expectation early

Q14 · Version control mindset

Version control (Git) tracks changes to scripts over time — who changed what, when, and why.

Q15 · DRY and small functions

DRY (Don’t Repeat Yourself): one definition, many uses.

z_score <- function(x) (x - mean(x, na.rm = TRUE)) / sd(x, na.rm = TRUE) # reusable
z_score(reef$count)[1:5]
[1] -0.7841578  0.2881947 -1.1862899 -0.3820256  1.4945913
z_score(fishery$tonnes)[1:5]
[1]  0.51776773  0.64938929 -1.24393627  0.24777478 -0.07959167

Q16 · Validate inputs early

Fail fast: check column names, types, and ranges at the top of a script.

need <- c("animal_id", "detections") # required columns
if (!all(need %in% names(acoustic))) stop("Missing columns: ", paste(setdiff(need, names(acoustic)), collapse = ", "))
if (any(acoustic$detections < 0, na.rm = TRUE)) stop("Negative detections found")
message("Input checks passed")

Q17 · Composition and pipes

Composition chains small steps: output of step 1 feeds step 2.

suppressPackageStartupMessages(library(dplyr))
reef |>
  filter(count > 0) |>
  group_by(site) |>
  summarise(total = sum(count), .groups = "drop")
# A tibble: 3 × 2
  site  total
  <chr> <int>
1 East    219
2 North   176
3 South   196

Q18 · Reading error messages

Errors read bottom-up: what failed, then the call stack.

# Example: intentional typo (run to see error in console when knitting locally)
# mean(reef$counts) # counts vs count
tryCatch(mean(reef$counts), error = function(e) conditionMessage(e))
[1] NA

Q19 · Documentation & help()

?mean, help(mean), and package vignettes are authoritative.

args(mean) # formal arguments
function (x, ...) 
NULL
# help(mean) # run in RStudio for full help page

Q20 · CS capstone plan

Before writing code for a new dataset, sketch on paper:

plan <- list(
  input = "acoustic_detections.csv",
  output = "detections per animal_id",
  checks = c("no negative detections", "animal_id not empty"),
  steps = c("read.csv", "group_by + summarise", "ggplot if needed")
)
plan
$input
[1] "acoustic_detections.csv"

$output
[1] "detections per animal_id"

$checks
[1] "no negative detections" "animal_id not empty"   

$steps
[1] "read.csv"             "group_by + summarise" "ggplot if needed"