[![](https://img.shields.io/badge/download%20html-blue)](../rendered-html/data-frames.html)
[![](https://img.shields.io/badge/download%20Rmd-blue)](data-frames.Rmd)

```{r setup, include=FALSE}
# Set global options for all code chunks
knitr::opts_chunk$set(echo = TRUE)  # Display code in output
```

## Introduction

Data frames are one of the most commonly used data structures in R. They are particularly useful for working with tabular data, such as gene expression datasets, clinical data, or experimental results. A data frame is a 2-dimensional structure where:

- Each column can contain different types of data (numeric, character, logical)
- All columns must have the same length
- Each column has a unique name

## Creating Data Frames

### Method 1: From Scratch

```{r create-basic}
# Create a simple data frame with patient information
df <- data.frame(
  patient_id = c("P001", "P002", "P003", "P004"),    # Patient identifiers
  age = c(45, 52, 38, 61),                           # Patient ages
  diabetic = c(TRUE, FALSE, FALSE, TRUE)             # Diabetes status
)

# Display the created data frame
print("Basic data frame:")                           # Show the data
print(df)

# Examine the structure of the data frame
print("\nStructure of the data frame:")              # Show data types and structure
str(df)                                             # Display detailed structure
```

### Method 2: From Existing Vectors

```{r create-from-vectors}
# Create vectors for RNA-seq analysis results
gene_names <- c("BRCA1", "TP53", "EGFR", "KRAS", "HER2")  # Gene identifiers
expression_values <- c(1543.7, 2345.2, 1234.5, 876.3, 2345.6)  # Expression levels
is_significant <- c(TRUE, TRUE, FALSE, FALSE, TRUE)    # Statistical significance
p_values <- c(0.001, 0.0001, 0.08, 0.07, 0.001)       # Statistical p-values
fold_changes <- c(2.5, 3.2, 1.1, 0.9, 2.8)            # Expression fold changes

# Combine vectors into a data frame for analysis
gene_expression_df <- data.frame(
  gene = gene_names,                    # Gene names column
  expression = expression_values,        # Expression values column
  significant = is_significant,          # Significance flags column
  p_value = p_values,                   # P-values column
  fold_change = fold_changes            # Fold changes column
)

# Display the resulting data frame
print("Gene expression data frame:")     # Show combined data
print(gene_expression_df)
```

### Method 3: Reading from Files

```{r read-files}
# Save data frame to CSV file for sharing/storage
write.csv(gene_expression_df, "gene_expression.csv", row.names = FALSE)  # Export data

# Read data back from CSV file
df_from_csv <- read.csv("gene_expression.csv")    # Import data from file
print("Data frame read from CSV:")                # Show imported data
head(df_from_csv)                                # Display first few rows
```

## Examining Data Frames

```{r examining}
# Display basic information about the data frame
print("Dimensions of the data frame:")           # Show size of data
dim(gene_expression_df)                          # [rows, columns]

print("\nColumn names:")                         # Show variable names
names(gene_expression_df)                        # List all columns

print("\nSummary of the data:")                  # Show statistical summary
summary(gene_expression_df)                      # Basic statistics for each column

print("\nFirst few rows:")                       # Show start of data
head(gene_expression_df, n = 3)                  # First 3 rows

print("\nLast few rows:")                        # Show end of data
tail(gene_expression_df, n = 2)                  # Last 2 rows
```

## Accessing Elements in Data Frames

### 1. Accessing Columns

```{r accessing-columns}
# Demonstrate different methods to access columns
# Using $ notation (direct column access)
expression_values <- gene_expression_df$expression    # Get expression column
print("Expression values using $:")
print(expression_values)

# Using column name with brackets (returns data frame)
p_values <- gene_expression_df["p_value"]            # Get p-value column
print("\nP-values using brackets:")
print(p_values)

# Select multiple columns at once
selected_cols <- gene_expression_df[c("gene", "fold_change")]  # Get gene names and fold changes
print("\nSelected columns:")
print(selected_cols)
```

### 2. Accessing Rows

```{r accessing-rows}
# Demonstrate row access methods
# Get a single row
row_1 <- gene_expression_df[1, ]                     # First row
print("First row:")
print(row_1)

# Get multiple consecutive rows
rows_2_4 <- gene_expression_df[2:4, ]               # Rows 2 through 4
print("\nRows 2-4:")
print(rows_2_4)
```

### 3. Accessing Specific Elements

```{r accessing-elements}
# Access individual elements using different methods
# Using row and column numbers
element <- gene_expression_df[1, 2]                  # First row, second column
print("Element at row 1, column 2:")
print(element)

# Using row number and column name
p_val <- gene_expression_df[2, "p_value"]           # Second row, p-value column
print("\nP-value for second gene:")
print(p_val)

# Get a subset of rows and columns
subset <- gene_expression_df[1:2, c("gene", "expression")]  # First 2 rows, selected columns
print("\nSubset of first two genes with names and expression:")
print(subset)
```

## Filtering Data Frames

### 1. Using Logical Conditions

```{r filtering}
# Filter data based on various conditions
# Find genes with statistical significance
significant_genes <- gene_expression_df[gene_expression_df$significant == TRUE, ]
print("Significantly expressed genes:")
print(significant_genes)

# Find genes with high expression levels
high_expression <- gene_expression_df[gene_expression_df$expression > 2000, ]
print("\nHighly expressed genes:")
print(high_expression)

# Combine multiple filtering conditions
important_genes <- gene_expression_df[
  gene_expression_df$significant == TRUE &           # Must be significant
  gene_expression_df$fold_change > 2.5,             # Must have high fold change
]
print("\nSignificant genes with high fold change:")
print(important_genes)
```

### 2. Using which() Function

```{r which-function}
# Demonstrate which() function for finding matching elements
# Get indices of significant genes
sig_indices <- which(gene_expression_df$significant)  # Find TRUE values
print("Indices of significant genes:")
print(sig_indices)

# Use indices to extract rows
sig_genes <- gene_expression_df[sig_indices, ]       # Get corresponding rows
print("\nSignificant genes (using which):")
print(sig_genes)
```

## Modifying Data Frames

### 1. Adding Columns

```{r adding-columns}
# Add new calculated columns to the data frame
# Add log2 transformed expression values
gene_expression_df$log2_expression <- log2(gene_expression_df$expression)  # Log transformation

# Add multiple columns simultaneously
gene_expression_df <- cbind(
  gene_expression_df,
  neg_log10_pvalue = -log10(gene_expression_df$p_value),  # Transform p-values
  pass_filter = gene_expression_df$p_value < 0.05         # Add significance filter
)

print("Data frame with new columns:")
print(gene_expression_df)
```

### 2. Adding Rows

```{r adding-rows}
# Create and add a new row of data
# Create data for new gene
new_gene <- data.frame(
  gene = "BRAF",                        # Gene name
  expression = 1876.5,                  # Expression value
  significant = TRUE,                   # Significance flag
  p_value = 0.002,                     # P-value
  fold_change = 2.1,                   # Fold change
  log2_expression = log2(1876.5),      # Log2 expression
  neg_log10_pvalue = -log10(0.002),    # Transformed p-value
  pass_filter = TRUE                    # Filter status
)

# Add new row to existing data frame
gene_expression_df <- rbind(gene_expression_df, new_gene)  # Combine rows

print("Data frame with new row:")
print(gene_expression_df)
```

### 3. Modifying Values

```{r modifying-values}
# Update existing values in the data frame
# Change a single value
gene_expression_df$expression[1] <- 1600    # Update first expression value

# Update multiple values based on condition
gene_expression_df$significant[gene_expression_df$p_value > 0.05] <- FALSE  # Update significance

print("Modified data frame:")
print(gene_expression_df)
```

## Practice Exercises

### Exercise 1: Creating and Filtering
Create a data frame containing clinical trial data with:
- Patient ID
- Treatment group (A or B)
- Age
- Response (TRUE/FALSE)
Then filter for responding patients over 50.

```{r exercise1}
# Create the data frame
clinical_data <- data.frame(
  patient_id = paste0("P", 1:10),
  treatment = rep(c("A", "B"), each = 5),
  age = c(45, 62, 55, 48, 71, 52, 67, 43, 58, 49),
  response = c(TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE)
)

# Filter for responding patients over 50
responders_over_50 <- clinical_data[
  clinical_data$response == TRUE & clinical_data$age > 50,
]

print("Responding patients over 50:")
print(responders_over_50)
```

### Exercise 2: Data Frame Manipulation
Using the gene expression data frame:
1. Calculate z-scores for expression values
2. Add a column for expression categories (High, Medium, Low)
3. Filter for genes meeting specific criteria

```{r exercise2}
# Calculate z-scores
mean_expr <- mean(gene_expression_df$expression)
sd_expr <- sd(gene_expression_df$expression)
gene_expression_df$z_score <- (gene_expression_df$expression - mean_expr) / sd_expr

# Add expression categories
gene_expression_df$expr_category <- cut(
  gene_expression_df$z_score,
  breaks = c(-Inf, -1, 1, Inf),
  labels = c("Low", "Medium", "High")
)

# Filter for interesting genes
interesting_genes <- gene_expression_df[
  gene_expression_df$significant == TRUE & 
  gene_expression_df$expr_category == "High",
]

print("Genes with high expression and significance:")
print(interesting_genes)
```

## Common Mistakes and How to Avoid Them

1. **Forgetting that data frames are 1-indexed**
```{r mistakes-1}
# This gets the first row, not the zeroth
first_row <- gene_expression_df[1, ]
```

2. **Dropping dimensions unintentionally**
```{r mistakes-2}
# Keep data frame structure with drop=FALSE
single_col_df <- gene_expression_df[, "expression", drop=FALSE]
print("Single column as data frame:")
print(single_col_df)
```

3. **Not checking for missing values**
```{r mistakes-3}
# Check for missing values
print("Missing values in each column:")
colSums(is.na(gene_expression_df))
```

## Next Steps

After mastering data frames, you can move on to:
- Data manipulation with dplyr
- Data visualization with ggplot2
- Statistical analysis of data frames
- Advanced filtering and aggregation techniques

```{r cleanup, include=FALSE}
# Clean up files created during the tutorial
file.remove("gene_expression.csv")
``` 