Why this activity matters

In this activity, we will explore real clinical data from breast cancer patients in The Cancer Genome Atlas (TCGA).

The goal is not only to learn R. The goal is to use R to ask scientific questions:

Later in DREAM-High, we will connect this type of clinical information to gene expression data. That is where computational biology becomes especially powerful: we can ask how molecular patterns relate to patient and tumor characteristics.


Preliminaries

Create your own copy

Before editing, use:

File → Save As…

Save a copy with your name in the file name.

This gives you your own workbook while preserving the original.


About the data files

This activity uses:

  • brca_clin.csv — a simplified TCGA breast cancer clinical dataset
  • duct_cells.jpg — a schematic of breast duct changes
  • metastasis.JPG — a schematic of metastatic spread

The files should be located in the shared data directory.

```{r setup, message=FALSE, warning=FALSE} # This chunk sets up file path for the activity.

data_dir <- “/shared/dreamhigh/data”


---

## Breast cancer biology: what are we studying?

Breast cancer often begins in the ducts or lobules of the breast. As cancer develops, cells can grow abnormally, fill spaces where they do not belong, and eventually invade nearby tissue.

The figure below shows a simplified progression from a normal duct to invasive ductal cancer.

```{r duct-image, echo=FALSE, out.width="95%"}
knitr::include_graphics(file.path(data_dir, "duct_cells.jpg"))

Think before moving on:

What do you notice as the duct changes from normal tissue to invasive cancer?

Write one observation here:

Your answer:


Metastasis

A major danger of cancer is metastasis: cancer cells can leave the original tumor, move through the body, and grow in distant organs.

{r metastasis-image, echo=FALSE, out.width="95%"} knitr::include_graphics(file.path(data_dir, "metastasis.jpg"))

Overview of the metastatic cascade in cancer progression.

Cancer metastasis is a multi-step process in which tumor cells spread from the primary tumor to distant organs. - (1) Tumor cells acquire invasive properties and detach from the primary tumor. - (2) Cancer cells migrate through surrounding tissue and invade nearby blood vessels. - (3) Tumor cells survive in the circulation and travel through the bloodstream. - (4) Circulating tumor cells exit blood vessels (extravasation) at distant sites. - (5) Cancer cells colonize distant organs and form metastatic tumors.

The figure also highlights important biological processes involved in metastasis, including:

  • Motility and invasion — movement through tissue and blood vessels
  • Plasticity — the ability of tumor cells to change cellular states
  • Modulation of the microenvironment — interactions between tumor cells and surrounding stromal or immune cells
  • Colonization — successful growth of tumor cells in distant organs such as lung, liver, bone, or brain

Metastasis is biologically complex and difficult to predict or treat.

In clinical cancer datasets, we often look for features that tell us something about tumor severity, spread, treatment, or patient outcome.


Load the clinical data

We will use a CSV file. CSV stands for comma-separated values. It is a plain-text table format that can be opened in R, Excel, or many other programs.

{r load-data} brca_clin_df <- read.csv( file.path(data_dir, "brca_clin.csv"), stringsAsFactors = FALSE )

The object name brca_clin_df is descriptive:

A data frame is a table:


First look at the data

Dimensions

{r dimensions} dim(brca_clin_df)

The first number is the number of rows. The second number is the number of columns.

```{r dimensions-sentence} n_patients <- nrow(brca_clin_df) n_features <- ncol(brca_clin_df)

paste(“This dataset contains”, n_patients, “patients and”, n_features, “clinical features.”)


---

### Column names

```{r column-names}
colnames(brca_clin_df)

Question: Which column names look familiar? Which ones are confusing?

Your answer:


First few rows

{r head-data} head(brca_clin_df)

For a larger view, you can run this interactively:

{r view-data, eval=FALSE} View(brca_clin_df)


Working with rows and columns

A single value

{r single-value} brca_clin_df[1, 1]

The first number selects the row. The second number selects the column.


A single patient

{r single-patient} t(brca_clin_df[1, ])

The function t() transposes the row so it is easier to read.


A few patients and features

{r selected-rows-columns} brca_clin_df[c(1, 2, 3), 1:6]

Prediction question: What do you think this code will show?

{r selected-rows-columns-2} brca_clin_df[c(10, 20, 30), c("gender", "age_at_diagnosis", "vital_status")]

Your answer:


Real data are messy

Clinical data are not perfectly clean. Some values are missing, unknown, not evaluated, or not available.

Let us look for values that indicate missing or uncertain information.

```{r missing-like-values} missing_like_values <- c(“[Not Available]”, “[Not Evaluated]”, “[Unknown]”, “Indeterminate”, “[Discrepancy]”, ““)

missing_like_count <- numeric(ncol(brca_clin_df))

for (i in 1:ncol(brca_clin_df)) { missing_like_count[i] <- sum(brca_clin_df[[i]] %in% missing_like_values | is.na(brca_clin_df[[i]])) }

missing_summary <- data.frame( feature = colnames(brca_clin_df), missing_or_uncertain_count = missing_like_count, percent = round(100 * missing_like_count / nrow(brca_clin_df), 1) )

missing_summary


### Visualize missing or uncertain values

```{r missing-barplot}
ordered_missing <- missing_summary[order(missing_summary$missing_or_uncertain_count, decreasing = TRUE), ]

barplot(
  ordered_missing$missing_or_uncertain_count,
  names.arg = ordered_missing$feature,
  las = 2,
  cex.names = 0.7,
  main = "Missing or uncertain values by clinical feature",
  ylab = "Number of patients"
)

Question: Which clinical features have the most missing or uncertain information?

Your answer:

This is an important lesson: real biomedical datasets are powerful, but they are also imperfect.


Summarizing categorical variables

A categorical variable describes groups or labels.

Examples:

The base R function table() counts how many times each value appears.


Gender

{r gender-table} table(brca_clin_df$gender)

{r gender-barplot} barplot( table(brca_clin_df$gender), width = 0.5, space = 0.5, main = "Gender distribution in the TCGA breast cancer cohort", ylab = "Number of patients", las = 1 )

Question: What does this tell us about the cohort?

Your answer:


Race

{r race-table} table(brca_clin_df$race)

```{r race-barplot} race_counts <- sort(table(brca_clin_df$race), decreasing = TRUE)

barplot( race_counts, main = “Race distribution”, ylab = “Number of patients”, las = 2, cex.names = 0.8 )


**Discussion question:** Why is it important to look at who is represented in a biomedical dataset?

> Your answer:

---

## Summarizing numerical variables

A **numerical variable** contains numbers.

Example:

- age at diagnosis

### Age at diagnosis

```{r age-summary}
summary(brca_clin_df$age_at_diagnosis)

{r age-histogram} hist( brca_clin_df$age_at_diagnosis, breaks = 20, main = "Age at diagnosis", xlab = "Age at diagnosis", ylab = "Number of patients" )

Question: Around what ages are most patients diagnosed in this dataset?

Your answer:


Receptor status

Breast cancer tumors are often tested for receptors that can influence growth and treatment.

Important receptor features include:


Estrogen receptor status

{r er-status} table(brca_clin_df$estrogen_receptor_status)

{r er-status-barplot} barplot( table(brca_clin_df$estrogen_receptor_status), main = "Estrogen receptor status", ylab = "Number of patients", las = 2 )


Progesterone receptor status

{r pr-status} table(brca_clin_df$progesterone_receptor_status)


HER2 receptor status

{r her2-status} table(brca_clin_df$her2_receptor_status)


Combining conditions

A powerful part of data analysis is selecting patients who meet more than one condition.

For example, how many patients are both ER-positive and PR-positive?

```{r er-pr-positive} ERpos_PRpos <- brca_clin_df[ brca_clin_df\(estrogen_receptor_status == "Positive" & brca_clin_df\)progesterone_receptor_status == “Positive”,]

nrow(ERpos_PRpos)


```{r er-pr-positive-sentence}
paste("There are", nrow(ERpos_PRpos), "patients with both ER-positive and PR-positive tumors.")

Triple-negative breast cancer

Triple-negative breast cancer means the tumor is:

These cancers can be more difficult to treat because they do not have these common therapeutic targets.

```{r tnbc} tnbc <- brca_clin_df[ brca_clin_df\(estrogen_receptor_status == "Negative" & brca_clin_df\)progesterone_receptor_status == “Negative” & brca_clin_df$her2_receptor_status == “Negative”,]

nrow(tnbc)


```{r tnbc-percent}
tnbc_percent <- round(100 * nrow(tnbc) / nrow(brca_clin_df), 1)

paste(
  "Triple-negative breast cancer cases make up",
  tnbc_percent,
  "% of this dataset."
)

Looking at relationships between variables

So far, we summarized one variable at a time. But clinical questions often involve relationships between variables.

Receptor status and vital status

```{r er-vital-table} er_vital_table <- table( brca_clin_df\(estrogen_receptor_status, brca_clin_df\)vital_status )

er_vital_table


Add row percentages:

```{r er-vital-percent}
round(100 * prop.table(er_vital_table, margin = 1), 1)

This table asks:

Within each ER status group, what percent of patients are alive or dead?


Mosaic plot

A mosaic plot is a base R visualization for two categorical variables.

```{r er-vital-mosaic}

rownames(er_vital_table) <- c(“NE”,“ND”,“Negative”,“Positive”)

mosaicplot( er_vital_table, main = “Estrogen receptor status and vital status”, xlab = “Estrogen receptor status”, ylab = “Vital status”, color = TRUE, las = 1 )


**Question:** What can you conclude from this plot? What should you be careful *not* to overclaim?

> Your answer:

---

## Histological type

Histology describes what the tumor tissue looks like under a microscope.

```{r histological-type}
histology_counts <- sort(table(brca_clin_df$histological_type), decreasing = TRUE)
histology_counts

{r histology-barplot} par(mar = c(10, 4, 4, 2)) barplot( histology_counts, main = "Histological type", ylab = "Number of patients", las = 2, cex.names = 0.75 )

Most tumors in this dataset are infiltrating ductal carcinoma or infiltrating lobular carcinoma.

```{r percent-ductal} N_ductal <- histology_counts[“Infiltrating Ductal Carcinoma”] percent_ductal <- round(100 * N_ductal / nrow(brca_clin_df), 1)

paste( percent_ductal, “% of patients have infiltrating ductal carcinoma.”, sep = “” )


---

## Pathologic stage challenge

Pathologic stage (`pathologic_stage`) describes how far the cancer has progressed.

```{r stage-table}
# Uncomment the next line and place the correct feature after the $ sign
# See the hint above

# table(brca_clin_df$ )

Challenge 1

How many patients have stage II breast cancer?

```{r stage-II} # Uncomment the next two lines and place the symbol for stage II between the parentheses

N_stageII <- table(brca_clin_df$pathologic_stage)[” ”]

N_stageII


```{r stage-II-percent}

# Uncomment all of the lines below

# percent_stageII <- round(100 * N_stageII / nrow(brca_clin_df), 1)

# paste(
#   "There are",
#   N_stageII,
#   "patients with stage II breast cancer, representing",
#   percent_stageII,
#   "% of the cohort."
# )

Challenge 2

Make a bar plot of cancer stage (pathological_stage).

```{r stage-barplot} # Uncomment the next line and place the correct feature after the $ sign

stage_counts <- table(brca_clin_df$ )

Uncomment the next six lines. Replace X with the object you want to make the barplot for.

barplot(

X,

main = “Pathologic stage”,

xlab = “Stage”,

ylab = “Number of patients”

)


---

## A deeper question: stage and lymph nodes

Cancer staging often depends partly on whether cancer has spread to lymph nodes.

The column `number_of_lymphnodes_positive` looks numeric, but because it contains values such as `[Not Available]`, R may read it as text.

We can convert it carefully.

```{r lymph-node-cleaning}
lymph_nodes_positive <- suppressWarnings(as.numeric(brca_clin_df$number_of_lymphnodes_positive))

summary(lymph_nodes_positive)

The warning is suppressed because R turns non-numeric values like [Not Available] into NA.

Now we can compare positive lymph node counts by pathologic stage.

{r lymph-node-boxplot} boxplot( lymph_nodes_positive ~ brca_clin_df$pathologic_stage, main = "Positive lymph nodes by pathologic stage", xlab = "Pathologic stage", ylab = "Number of positive lymph nodes" )

Question: What pattern do you see?

Your answer:


Final reflection

In this activity, you used R to:

Most importantly, you saw that real biomedical data are both powerful and messy.

That is normal. A major part of computational biology is learning how to reason carefully with complex, imperfect data.


Create your report

Save your file. Then click:

Knit → Knit to HTML

Your final HTML report should include:

This is one reason R Markdown is powerful: it combines writing, code, results, and figures in one reproducible document.


Next time

In later DREAM-High activities, we will connect these clinical features to gene expression data from the same cancer type.

That will allow us to ask a deeper systems biology question:

Can molecular data reveal patterns that help us understand cancer subtype, treatment, and disease behavior?