Create and subset vectors, matrices, data frames, and lists; recognize numeric, character, logical, and factor variables; and write a small reusable function.
In this section we will be dealing with basic operations using R. One should be aware of these basics before proceeding to any kind of data analysis using R.
The RStudio interface is simple. You type R code into the bottom line of the RStudio console pane (see figure Figure 1.3) and then click Enter to run it. The code you type is called a command, because it will command your computer to do something for you. The line you type it into is called the command line.
‘#’ hashtag in R
R treats the hashtag character, ’#’in a special way; R will not run anything that follows a hashtag on a line. This makes hashtags very useful for adding comments and annotations to your code. We will be able to read the comments, but your computer will not process it.
2.2 Basic arithmetic operations
# Try these codes in R, Type these codes in console and hit enter# result of R code is also shown here # + (Addition) 7+4
[1] 11
# - (Subtraction) 7-4
[1] 3
# * (Multiplication)7*2
[1] 14
# / (division) 7/2
[1] 3.5
# ^ (exponentiation) 7^2
[1] 49
The colon operator (:) returns every integer between two integers. It is an easy way to create a sequence of numbers.
x <-2# it is that value 2 is assigned to variable x y <-5# it is that value 5 is assigned to variable y# Now if you give x+5 or using any operator between x and y, you can see as below x + y
[1] 7
x * y
[1] 10
x / y
[1] 0.4
x +2*y
[1] 12
Note that R is case sensitive, i.e. x and X are not equal.
2.5 Basic data types
numeric
character
logical
my_age <-32# Numeric variable 32 is assigned to my_age# while writing your code it is preffered to use '_' instead of space my_name <-"Dr Pratheesh"# Character variable # Are you a data scientist?: (yes/no) <=> (TRUE/FALSE) is_datascientist <-TRUE# logical variable
2.5.1 Vectors
A combination of multiple values (numeric, character or logical)
How to create a vector
Create a vector: c() for concatenate
Case of missing values: NA (not available) and NaN (not a number)
Get a subset of a vector: my_vector[i] to get the ith element
# Create a numeric vectorstudent_ages <-c(27, 25, 29, 26, 20, 21, 23, 25) # Create a character vectorstudent_name <-c("asha", "adhi", "aravind", "mary", "peter", "daisy", "papu", "ramu") # subset of a vector # obtain 3 rd element from student_name student_name[3]
[1] "aravind"
Case of missing values: NA (not available) and NaN (not a number) Get a subset of a vector: my_vector[i] to get the ith element
Calculations with vectors
max(x), min(x), range(x), length(x), sum(x), mean(x), prod(x): product of the elements in x, sd(x): standard deviation, var(x): variance, sort(x)
# Create a numeric vectorstudent_ages <-c(27, 25, 29, 26, 20, 21, 23, 25) # Maximum value of the vectormax(student_ages)
[1] 29
# Minimum value of the vectormin(student_ages)
[1] 20
# Range of the vectorrange(student_ages)
[1] 20 29
# Length of the vectorlength(student_ages)
[1] 8
# Total of the values in the vectorsum(student_ages)
[1] 196
# Mean of the vectormean(student_ages)
[1] 24.5
# Product of the elements in the vectorprod(student_ages)
[1] 122911425000
# Standard deviation sd(student_ages)
[1] 3.023716
# Variance of the vectorvar(student_ages)
[1] 9.142857
# Sort the values of the vector# Ascending ordersort(student_ages,decreasing =FALSE)
It’s a homogeneous collection of data sets which is arranged in a two dimensional organisation. It’s a m*n array with similar data type. It is created using a vector input. It has a fixed number of rows and columns. You can perform many arithmetic operations on R matrix like – addition, subtraction, multiplication, and divisions.
Create and naming matrix: matrix(), cbind(), rbind(), rownames(), colnames() Transpose a matrix: t() Dimensions of a matrix: ncol(), nrow(), dim() Get a subset of a matrix: my_data[row, col] Calculations with numeric matrices: rowSums(), colSums(), rowMeans(), colMeans()
# make three vectors c1, c2 and c3c1<-c(3,4,5)c2<-c(7,8,9)c3<-c(11,12,13)# Creating matrix by binding column wise A <-cbind(c1,c2,c3)print(A)
c1 c2 c3
[1,] 3 7 11
[2,] 4 8 12
[3,] 5 9 13
# Creating matrix by binding row wise B <-rbind(c1,c2,c3)print(B)
[,1] [,2] [,3]
c1 3 4 5
c2 7 8 9
c3 11 12 13
# Adding two matricesA+B
c1 c2 c3
[1,] 6 11 16
[2,] 11 16 21
[3,] 16 21 26
# Simple element by element multiplication up to matrices.A*B
# Knowing dimensions of a matrix#Number of columns in Ancol(A)
[1] 3
#Number of rows in Anrow(A)
[1] 3
# Number of rows and columnsdim(A)
[1] 3 3
# Calculations with numeric matrices # Compute row sums of matrix ArowSums(A)
[1] 21 24 27
# Compute column sums of matrix AcolSums(A)
c1 c2 c3
12 24 36
# Compute row means of matrix ArowMeans(A)
[1] 7 8 9
# Compute column means of matrix AcolMeans(A)
c1 c2 c3
4 8 12
2.5.3 Data frames
A data frame is the data type, we will be going to use frequently. A data frame is like a matrix but can have columns with different types (numeric, character, logical). Rows are observations (individuals) and columns are variables. Function data.frame() is used to create data frame
name age height married
1 asha 20 180 TRUE
2 adhi 23 170 FALSE
3 aravind 22 185 TRUE
4 mary 21 169 TRUE
To check whether a data is a data frame, use the is.data.frame() function. Returns TRUE if the data is a data frame:
is.data.frame(friends_data)
[1] TRUE
To check whether a data is a matrix, data frame or any other class use the function class()
# What is the class of A? --> matrixclass(A)
[1] "matrix" "array"
# What is the class of friends_data? --> data.frameclass(friends_data)
[1] "data.frame"
Convert a data class to data frame use the function as.data.frame()
# Convert it as a data frameA2 <-as.data.frame(A)# Now, the class is data.frameclass(A2)
[1] "data.frame"
As described in matrix section, you can use the function t() to transpose a data frame:
t(friends_data)
[,1] [,2] [,3] [,4]
name "asha" "adhi" "aravind" "mary"
age "20" "23" "22" "21"
height "180" "170" "185" "169"
married "TRUE" "FALSE" "TRUE" "TRUE"
2.5.3.1 Operations on data frame
Positive indexing by name and by location
# Access the data in 'name' column in friends_data# dollar sign is usedfriends_data$name
[1] "asha" "adhi" "aravind" "mary"
# or use thisfriends_data[, 'name']
[1] "asha" "adhi" "aravind" "mary"
# Subset columns 1 and 3friends_data[ , c(1, 3)]
name height
1 asha 180
2 adhi 170
3 aravind 185
4 mary 169
# Subset columns 1 to 3 friends_data[ , c(1:3)]
name age height
1 asha 20 180
2 adhi 23 170
3 aravind 22 185
4 mary 21 169
Negative Indexing
# Exclude column 1 friends_data[, -1]
age height married
1 20 180 TRUE
2 23 170 FALSE
3 22 185 TRUE
4 21 169 TRUE
Index by characteristics
We want to select all friends with age greater than or equal to 22.
# Identify rows that meet the conditionfriends_data$age >=22
[1] FALSE TRUE TRUE FALSE
# TRUE specifies that the row contains a value of age >= 22.
# Select the rows that meet the conditionfriends_data[friends_data$age >=22, ]
name age height married
2 adhi 23 170 FALSE
3 aravind 22 185 TRUE
# The R code above, tells R to get all rows from friends_data where age >= 22
If you don’t want to see all the column data for the selected rows but are just interested in displaying, for example, friend names and age for friends with age >= 22, you could use the following R code:
# Use column locationsfriends_data[friends_data$age >=22, c(1, 2)]
name age
2 adhi 23
3 aravind 22
# Or use column namesfriends_data[friends_data$age >=22, c("name", "age")]
name age
2 adhi 23
3 aravind 22
If you’re finding that your selection statement is starting to be inconvenient, you can put your row and column selections into variables first, such as:
age22 <- friends_data$age >=22cols <-c("name", "age")# Then you can select the rows and columns with those variables:friends_data[age22, cols]
name age
2 adhi 23
3 aravind 22
It’s also possible to use the function subset() as follow.
# Select friends data with age >= 22subset(friends_data, age >=22)
name age height married
2 adhi 23 170 FALSE
3 aravind 22 185 TRUE
Another option is to use the functions attach() and detach(). The function attach() takes a data frame and makes its columns accessible by simply giving their names.
# Attach a data frameattach(friends_data)# === Data manipulation ====friends_data[age>=22, ]
name age height married
2 adhi 23 170 FALSE
3 aravind 22 185 TRUE
# === End of data manipulation ====# Detach the data framedetach(friends_data)
Extend a data frame
Add new column in a data frame
# Add group column to friends_datadata<-c("BSc", "MSc", "Phd", "Btech")friends_data$degree <- datafriends_data
name age height married degree
1 asha 20 180 TRUE BSc
2 adhi 23 170 FALSE MSc
3 aravind 22 185 TRUE Phd
4 mary 21 169 TRUE Btech
It’s also possible to use the functions cbind() and rbind() to extend a data frame.
name age height married degree roll_no
1 asha 20 180 TRUE BSc 234
2 adhi 23 170 FALSE MSc 235
3 aravind 22 185 TRUE Phd 236
4 mary 21 169 TRUE Btech 238
Calculations with data frame
With numeric data frame, you can use the function rowSums(), colSums(), colMeans(), rowMeans() and apply().
# Following can be used when it is a numeric data frame# creating a numeric data framec1<-c(3,4,5)c2<-c(7,8,9)c3<-c(11,12,13)# Creating matrix by binding column wise ex <-cbind(c1,c2,c3)# converting matrix to data frameex<-as.data.frame(ex)ex
c1 c2 c3
1 3 7 11
2 4 8 12
3 5 9 13
rowSums(ex)
[1] 21 24 27
colSums(ex)
c1 c2 c3
12 24 36
# calculation in non numeric data frame on selected numeric columnsattach(friends_data)sum(age)
[1] 86
mean(age)
[1] 21.5
apply() function
apply(X, MARGIN, FUN)
Here:
-x: an array or matrix
-MARGIN: take a value or range between 1 and 2 to define where to apply the function:
-MARGIN=1`: the manipulation is performed on rows
-MARGIN=2`: the manipulation is performed on columns
-MARGIN=c(1,2)` the manipulation is performed on rows and columns
-FUN: tells which function to apply. Built functions like mean, median, sum, min, max and even user-defined functions can be applied>
apply(ex,2, mean)
c1 c2 c3
4 8 12
apply(ex,1, mean)
[1] 7 8 9
# Close the final attach() demonstration before continuing.detach(friends_data)
The attach() examples explain commands encountered in existing scripts. In new analyses, prefer friends_data$age, with(friends_data, mean(age)), or a function with a data = argument. These make the variable source explicit and avoid masking another object with the same name.
2.6 Agricultural applications, interpretation, and further practice
2.6.1 Values and vectors
An atomic vector holds values of one underlying type. Combining numbers and text causes coercion, usually to character. Numeric values support arithmetic; character values describe names; logical values represent TRUE or FALSE. A factor records categories with a specified set of levels.
R indexes from 1. Square brackets select elements. > compares values; == tests equality; != tests inequality; and %in% tests membership. Use & and | for elementwise “and” and “or”. Arithmetic is vectorized. R can recycle a shorter vector, so check lengths when combining measurements; recycling does not establish which observations belong together.
plot_id <-1:4plot_id[plot_id %in%c(2, 4)]
[1] 2 4
yield[yield >=4.5& variety =="B"]
[1] 5.1 4.5
length(yield)
[1] 4
2.6.2 Matrices
A matrix is a rectangular atomic vector with dimensions. It is useful for numeric arrays such as soil measurements at grid positions. A data frame is usually better for a table containing measurements and treatment labels.
west centre east
[1,] 21 22 23
[2,] 24 25 26
[3,] 27 28 29
[4,] 30 31 32
soil_grid[2, 3]
east
26
soil_grid[, "west"]
[1] 21 24 27 30
rowMeans(soil_grid)
[1] 22 25 28 31
colMeans(soil_grid)
west centre east
25.5 26.5 27.5
t(soil_grid)
[,1] [,2] [,3] [,4]
west 21 24 27 30
centre 22 25 28 31
east 23 26 29 32
x * y multiplies corresponding elements, whereas x %*% y performs matrix multiplication and requires conformable dimensions. Transposing a mixed data frame can coerce its contents to character; select numeric columns before numerical matrix operations.
2.6.3 Data frames and experimental units
A data frame can hold a different type in each column. For a simple field trial, each row should represent one harvested experimental plot, and each column one variable. Treatment labels are ordinary columns and may repeat. Plot identifiers should uniquely identify experimental units; do not use treatment names as unique row names.
field <-data.frame(plot_id, variety =factor(variety), yield_t_ha = yield,irrigated = irrigated)str(field)
plot_id variety yield_t_ha irrigated yield_kg_ha
Min. :1.00 A:2 Min. :4.200 Mode :logical Min. :4200
1st Qu.:1.75 B:2 1st Qu.:4.425 FALSE:2 1st Qu.:4425
Median :2.50 Median :4.650 TRUE :2 Median :4650
Mean :2.50 Mean :4.650 Mean :4650
3rd Qu.:3.25 3rd Qu.:4.875 3rd Qu.:4875
Max. :4.00 Max. :5.100 Max. :5100
field[rows, columns] separates row and column selection by a comma. A blank position means all rows or all columns. Prefer explicit names and data = arguments over attach(), which can obscure where a variable comes from.
2.6.4 Lists, missing values, and functions
A list can contain objects of different structures. Statistical model results are often lists. $ or [[ ]] extracts a component, while [ ] keeps a sublist.
plot_id variety yield_t_ha irrigated yield_kg_ha
1 1 A 4.2 TRUE 4200
2 2 A 4.8 TRUE 4800
3 3 B 5.1 FALSE 5100
4 4 B 4.5 FALSE 4500
x <-c(4.2, NA, 5.1)is.na(x)
[1] FALSE TRUE FALSE
mean(x)
[1] NA
mean(x, na.rm =TRUE)
[1] 4.65
NA records an unknown value. It is neither zero nor the character string "NA". Use is.na(x), not x == NA. NaN is an undefined numeric result and Inf represents infinity. Removing missing values from a calculation changes its denominator and may change the population represented by the result.
A function names a repeatable calculation. Arguments supply inputs and the final expression supplies the return value.
The checks stop the calculation for invalid inputs. Later, the pipe |> passes a result into the first argument of the next function: sqrt(16) and 16 |> sqrt() are equivalent. A pipe expresses a sequence, not a new statistical method.
2.6.5 Practical 2 and exercises
Create a vector from 1 to 30. Select values below 8, then values below 8 or above 12, and finally 2, 6, and 12. This extends the earlier self-assessment exercises.
Create a four-row, five-column matrix from 1 to 20. Calculate its row means and select row 2, column 3.
Construct a six-plot data frame with a unique identifier, variety, and yield. Select yields above the overall mean.
Store the data frame and a description in a list. Explain why an atomic vector is unsuitable for this task.
Predict the type of c(1, "2"); verify it. Why could this matter when importing data?
Chapter review. Choose structures according to the information they represent. An identifier, a category, and a measurement may all look like numbers but play different analytical roles.
2.7 Original self-assessment exercises
The following exercises retain the tasks from the author’s original self-assessment document. Attempt them before looking at a solution. For each task, save the command, its output, and a short explanation. The later chapters return to the import and statistical-summary tasks with more detail.
Calculate the sum of two numbers. Create variables num1 and num2, assign them two numbers, and then calculate their sum.
Convert temperature from Celsius to Fahrenheit. Create temp_celsius, assign a temperature in Celsius, and calculate Fahrenheit using (temp_celsius * 9/5) + 32. State the units of the input and output.
Calculate the area of a rectangle. Create variables for length and width, assign the dimensions, and calculate their product. If dimensions are in metres, state the unit of the area. Prefer plot_length and plot_width to names that shadow common functions.
Calculate the factorial of a number. Assign a nonnegative integer to num and calculate its factorial using a built-in function. Explain the meaning of the answer for a small integer.
Determine whether a number is even or odd. Assign an integer to num, use the remainder operator %%, and print an appropriate result. What does num %% 2 == 0 test?
Calculate a square root. Assign a nonnegative number to num and calculate its square root using a built-in function. Explain why a negative input requires a different numerical interpretation.
Generate a sequence of numbers. Create a sequence from 1 to 10 using a built-in function; compare it with 1:10.
Create a vector and calculate its length. Use elements of your choice, inspect the vector’s class, and calculate its length.
Filter elements in a vector. Create a numeric vector and remove elements greater than a chosen threshold using logical indexing. State whether your condition selects values to keep or values to discard.
Create a CSV file of the iris dataset. Load iris, export it with row.names = FALSE, and identify where the file was saved.
Perform basic statistical calculations on iris. Import the CSV, calculate the mean, median, and standard deviation for each numeric measurement column, and summarize species as a category. Explain why it is inappropriate to average the species names.
For a further agricultural application, write a function that converts a vector of harvested grain masses to t/ha using a common harvested area. Check that the area is positive and demonstrate the function on at least three observations. This connects the original programming exercises with the yield calculation in the opening chapter.
2.8 Live R laboratory
Build a field observation table
Edit the code, then select Run R. The first run downloads R into your browser and may take a little time. Copy code also lets you use this example in RStudio.
field <-data.frame(plot =1:6,variety =factor(rep(c("A", "B"), each =3)),yield_t_ha =c(3.1, 3.4, 3.2, 3.8, 4.0, 3.9))str(field)aggregate(yield_t_ha ~ variety, field, mean)
Investigate. Identify the class of each column. Change one yield and explain why only one variety mean changes. These are simulated observations.
From the browser to your practical notebook
Record the input you changed, the output you obtained, and your interpretation. These live examples use base R and work independently. The full chapter examples, including package-based analyses, remain available with copy buttons for RStudio. Browser objects last until this page is reloaded; Reset code restores the editor text. Internet access is needed on first use; long or interactive desktop tasks belong in RStudio.