Before you begin

Under the File tab, choose Save As… and save your own copy of this file.

Then replace YOUR NAME in the header of this file with your name.

This document is your analysis notebook. It contains both:

  1. the code you run, and
  2. the explanation of what you discovered.

That is one reason scientists use notebooks like R Markdown: they make the work reproducible. Someone else can read your report, see your code, and understand how you made each figure.


Big idea: data can hide patterns

Large biological datasets are often too big to understand by reading numbers in a table.

In DREAM-High, we will eventually use heatmaps to look for patterns in breast cancer gene expression data from patients in The Cancer Genome Atlas. A heatmap can help us ask questions such as:

Today we will learn the same basic idea using a small practice dataset that comes with R.

Main idea: A heatmap turns numbers into colors so that hidden structure becomes easier to see.


What is R?

R is a programming language used for data analysis, statistics, and visualization. In this course, we will use R to explore cancer-related datasets.

You do not need to memorize every command. Your goal is to learn how to:

You will also be asked to modify code based on what you learn.


What is RStudio?

RStudio is the environment we are using to write and run R code.

You are currently working in an R Markdown file. This type of file lets us combine:

in one report.

When you click Knit, RStudio runs the code and turns this notebook into an HTML report.


A few R Markdown basics

R Markdown uses simple formatting rules:

  • # creates a large heading
  • ## creates a smaller heading
  • **bold** makes text bold
  • *italic* makes text italic
  • backticks show code, like x <- 5
  • code chunks contain R code

A code chunk looks like this:

dim(mtcars)
## [1] 32 11

You can run one code chunk at a time by clicking the green triangle at the top right of the chunk.

The dim function gives the number of rows and columns of the object.

The mtcars dataset has 32 rows and 13 columns.

#View(mtcars)

The practice dataset: mtcars

R comes with several built-in datasets. Today we will use mtcars, a small dataset about 32 cars from the 1970s.

Each row is a car.
Each column is a feature of the car.

For example:

This dataset is not about cancer, but it is useful because it is small, clean, and already available in R.

Later, the same heatmap logic will help us analyze cancer gene expression data.

# This is a comment line. Commenting your code is a critical practice!

# What do the first few rows look like?

### the first few rows look like these are efficient cars, with miles per gallon of around 21 to 23
head(mtcars)
##                    mpg cyl disp  hp drat    wt  qsec vs am gear carb
## Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
## Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
## Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
## Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
## Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
## Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1

Quick reflection

Before moving on, answer in one or two sentences:

What do you notice first when you look at this table?

Write your answer here:

I first noticed the abbreivations for the names of the different variables of the cars such as wt and mpg, allowing for a shorthand version as to save space and time.


Learning to inspect data

Before making a plot, scientists usually inspect the data.

# How many rows and columns are in mtcars?
dim(mtcars)
## [1] 32 11
# print an empty line to separate our output
cat("\n")
# What are the column names?
colnames(mtcars)
##  [1] "mpg"  "cyl"  "disp" "hp"   "drat" "wt"   "qsec" "vs"   "am"   "gear"
## [11] "carb"
cat("\n")
# What kind of object is mtcars?
class(mtcars)
## [1] "data.frame"

The result of dim(mtcars) tells us the number of rows and columns.

Check yourself

View the entire dataset:

View(mtcars)
  • How many cars are in the dataset?
  • How many features are measured for each car?

Write your answer here:There are 32 cars and 11 features measured for each car.


Getting help in R

R has built-in help pages.

You can run the following lines if you want more information. They are marked with eval=FALSE, which means they will not run automatically when you knit the report.

# what exactly is in mtcars?
help(mtcars)

The help function takes an object or function as an argument and gives us a detailed description. Look for the help file in the bottom right panel of our environment.

help is especially useful when we want to figure out what information and in what format we should supply to a function. It also provides additional arguments we may want to include.

# how is the heatmap function used and what parameters does it take?
help(heatmap)

From data frame to matrix

The heatmap() function expects a numeric matrix. See the description of the argument x in the help file.

As we saw above, mtcars is a data frame (data.frame class). A data frame is like a table. Because all columns in mtcars are numeric, we can convert it to a matrix.

# Convert mtcars from a data frame to a numeric matrix
data <- as.matrix(mtcars)

The symbol <- is the assignment operator. It stores the result on the right side inside the object name on the left side.

For example:

# Assign the value 5 to the variable x
x <- 5
x
## [1] 5

Here, R stores the number 5 in an object called x.

Check the object class and type for the matrix data. Is the output what you expect?

Yes, this is the output I expect befcause we just converted data into a matrix and we expect the numerical data type to be “double”.

class(data)
## [1] "matrix" "array"
typeof(data)
## [1] "double"

The type double means decimal numbers.


First heatmap: raw data

Now we will make a heatmap from the raw mtcars matrix.

# heatmap provides a color image of our data with dendrograms that help identify patterns
heatmap(data)

The heatmap turns numbers into colors. Red represents higher values and yellow represents lower values.

It also clusters:

The tree diagrams on the sides are called dendrograms. They show which rows or columns are grouped together.

Interpretation question

Look at the heatmap above.

Is it easy to see meaningful patterns? Why or why not?

Write your answer here:

I’d say it’s pretty difficult to see meaningful patterns since all the colors in my dataset are clustered on one side of the graph and seem to mostly be the same dark red or the same light tan.


Second heatmap: scaled data

Let’s create a heatmap that allows us to find the true underlying patterns.

Why scaling matters

Some columns in mtcars are measured on very different scales.

For example, compare cyl and disp:

# Range of cylinders
min(mtcars$cyl)
## [1] 4
max(mtcars$cyl)
## [1] 8
cat("\n")
# Range of displacement
min(mtcars$disp)
## [1] 71.1
max(mtcars$disp)
## [1] 472

These two features are not measured on the same scale. A value of 8 is high for cyl, but tiny for disp.

If we make a heatmap without adjusting for scale, large-number columns can dominate the visualization.

This same issue appears in biological data. Some measurements naturally have much larger ranges than others. If we do not account for scale, the strongest-looking pattern may simply reflect measurement units.


Scaling the data

The function scale() standardizes each column.

After scaling:

  • each column has an average close to 0
  • each column has a standard deviation of 1
  • features become more comparable
# Let's change the range of each feature so they are comparable
# We'll assign the output to a new variable data_scaled

data_scaled <- scale(data)

Let’s compare the first few values before and after scaling.

# First five rows and first five columns before scaling
data[1:5, 1:5]
##                    mpg cyl disp  hp drat
## Mazda RX4         21.0   6  160 110 3.90
## Mazda RX4 Wag     21.0   6  160 110 3.90
## Datsun 710        22.8   4  108  93 3.85
## Hornet 4 Drive    21.4   6  258 110 3.08
## Hornet Sportabout 18.7   8  360 175 3.15
# First five rows and first five columns after scaling
data_scaled[1:5, 1:5]
##                          mpg        cyl       disp         hp       drat
## Mazda RX4          0.1508848 -0.1049878 -0.5706198 -0.5350928  0.5675137
## Mazda RX4 Wag      0.1508848 -0.1049878 -0.5706198 -0.5350928  0.5675137
## Datsun 710         0.4495434 -1.2248578 -0.9901821 -0.7830405  0.4739996
## Hornet 4 Drive     0.2172534 -0.1049878  0.2200937 -0.5350928 -0.9661175
## Hornet Sportabout -0.2307345  1.0148821  1.0430812  0.4129422 -0.8351978

The syntax data[1:5, 1:5] means:

  • rows 1 through 5
  • columns 1 through 5

This is called indexing.

Reflection

What do you notice about the difference between the values for data and data_scaled? Put your answer here: >The values for “data” appear to simply be the original values and dependent on the actual unit being measured (such as the lenght unit of miles for mpg). However, the data_scaled values all seem to be between -1 and 1.


Now we make a heatmap using the scaled data.

# heatmap provides a color image of our data with dendrograms that help identify patterns
heatmap(data_scaled)

Remember, red represents higher values and yellow represents lower values.

Interpretation questions

Answer the following:

  1. Does this heatmap look more informative than the raw-data heatmap?
  2. Which features seem to group together?
  3. Do mpg and wt show similar or opposite patterns?
  4. Do the car groupings make sense based on what you know about cars?

Write your answers here:

  1. I’d say the heatmap definitely looks more informative. All the data isn’t squished to one side, and it’s instead more spread out with a wider range of colors. 2. The features which seem to group together are the qsec, disp, and cyc because for some amounts of cars they have significantly redder areas closer together. 3. Mpg and wt seem to show opposite patterns, since for the lower half and upper half of cars they seem to be opposite colors of light tan or dark red. 4. I would say so, especially considering how the lower half of cars are generally not as fast or sports cars and therefore have a greater cluster around miles per gallon. This would be because the cars value daily usability over momentary speed.

Color choices

Different color palettes can make patterns easier or harder to see.

We will use the package RColorBrewer, which contains ready-made color palettes.

# Packages are loaded with the library() function
library(RColorBrewer)
# Parameters for plotting, so we can see the colors' names
par(cex = 0.5)

# Show available palettes
display.brewer.all()

Now choose one palette and use it in a heatmap.

# Try changing "Greens" to another palette name
heatmap(data_scaled, col = brewer.pal(8, "GnBu"))

Challenge Question 1:

Customization challenge

Choose a different color palette and make a new heatmap.

# Replace "Greens" with a palette you choose
heatmap(data_scaled, col = brewer.pal(8, "BuGn"))

Which palette did you choose, and why?

I chose the GnBu color pallete as it has great contrast between the darker and lighter colors. Additionally, the mix of green and blue allows for greater availability of variation between the two extremes. However, it is not too much variation, allowing the reader to simply understand the differences between a darker or lighter color.


Challenge Question 2

A more focused question: relationships among features

Sometimes we care less about individual rows and more about how variables relate to one another.

Correlation refers to the statistical relationship between two variables (feature), describing how they change together. For example:

cor(mtcars$mpg,mtcars$wt)
## [1] -0.8676594

This means that across cars, mpg decreases as weight increases. This matches our intuition. Consider the scatterplot where each point corresponds to a car:

plot(mtcars$wt, mtcars$mpg, xlab = "Weight", ylab = "Miles per Gallon")

# Let's label a few points with the car names
index_seq <- seq(3, nrow(mtcars), by = 3)
text(mtcars$wt[index_seq], 
     mtcars$mpg[index_seq], 
     labels = rownames(mtcars)[index_seq], 
     pos = 4,    # Positions text to the right of the points
     cex = 0.7)  # Shrinks the text slightly so it is easier to read

We can calculate the correlation of each pair of features.

A correlation matrix measures how strongly pairs of features move together.

  • A positive correlation means two features tend to increase together.
  • A negative correlation means one feature tends to increase while the other decreases.
  • A correlation near zero means there is not much linear relationship.
# create an object to hold the correlation values
cor_mtcars <- cor(mtcars)

# Round the values to two decimal places
round(cor_mtcars, 2)
##        mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
## mpg   1.00 -0.85 -0.85 -0.78  0.68 -0.87  0.42  0.66  0.60  0.48 -0.55
## cyl  -0.85  1.00  0.90  0.83 -0.70  0.78 -0.59 -0.81 -0.52 -0.49  0.53
## disp -0.85  0.90  1.00  0.79 -0.71  0.89 -0.43 -0.71 -0.59 -0.56  0.39
## hp   -0.78  0.83  0.79  1.00 -0.45  0.66 -0.71 -0.72 -0.24 -0.13  0.75
## drat  0.68 -0.70 -0.71 -0.45  1.00 -0.71  0.09  0.44  0.71  0.70 -0.09
## wt   -0.87  0.78  0.89  0.66 -0.71  1.00 -0.17 -0.55 -0.69 -0.58  0.43
## qsec  0.42 -0.59 -0.43 -0.71  0.09 -0.17  1.00  0.74 -0.23 -0.21 -0.66
## vs    0.66 -0.81 -0.71 -0.72  0.44 -0.55  0.74  1.00  0.17  0.21 -0.57
## am    0.60 -0.52 -0.59 -0.24  0.71 -0.69 -0.23  0.17  1.00  0.79  0.06
## gear  0.48 -0.49 -0.56 -0.13  0.70 -0.58 -0.21  0.21  0.79  1.00  0.27
## carb -0.55  0.53  0.39  0.75 -0.09  0.43 -0.66 -0.57  0.06  0.27  1.00

We can visualize this correlation matrix as a heatmap too.

# heatmap works with any numeric matrix
heatmap(cor_mtcars, symm = TRUE)

Note Each element represents the correlation between the two features. Remember, red represents higher values and yellow represents lower values.

Interpretation question

Find one pair of features with a strong positive relationship and one pair with a strong negative relationship.

Write your answer here:

It seems like carb and hp have a strong positive relationship. I assume this is because having more carberators allows for a greater horsepower in the car because of the extra power that the carberators add. One strong negative relationship is vs and hp. I guess this means that the more straight an engine is (versus V-shaped) the less horsepower the car has.


Why this matters for cancer biology

Today we used cars because the dataset is small and easy to practice with.

But the same workflow can be used for biological data:

patients or samples  → rows
genes or features    → columns
expression values    → numbers inside the matrix
heatmap              → visual pattern discovery
clustering           → grouping similar samples or genes

In breast cancer gene expression data, heatmaps can help reveal groups of patients whose tumors have similar molecular patterns. These patterns can sometimes correspond to clinically meaningful tumor subtypes.

This is one example of how coding helps us see structure in complex biological systems.


Final reflection

Answer these questions in a few sentences.

  1. What did scaling change about the heatmap?
  2. Why might heatmaps be useful for studying cancer data?
  3. What is one R concept you feel more comfortable with now?
  4. What is one R concept you still find confusing?

Write your reflection here:

  1. Scaling really made the heatmap make more sense. It put all the values in the same range and, by doing so, spread the values out somewhat evenly across the map. This wider spread made it easier to find dark versus light patterns and actually compare the data. 2. Heatmaps would definitely be useful for studying cancer data because we can use them to find the correlation between, perhaps, different types of cells and whether that cell is likely to become cancerous. We would be able to see this as a dark red mark on a heatmap. 3. One R concept which I feel more comfortable with now is using the “help” to understand more aspects of datasets or of functions. 4. One concept I still find somewhat confusing is how the “scale” function actually works. I guess a mathematical comparison between values within the specific feature to scale them all down to between -1 and 1?

Knit your report

Click the Knit button and choose Knit to HTML.

Your final report should include:


Summary

In this activity, you practiced:

The most important idea is not the exact syntax. The most important idea is this:

Computational tools can help us discover patterns that are hidden inside large biological datasets.