R语言数据处理与清洗
一、查看数据概况
1. 查看数据的基本信息
r
复制代码
# 使用内置数据集 mtcars 作为示例
data(mtcars)
# 查看数据框的结构(变量名、类型、前几个值)
str(mtcars)
# 查看数据框的维度(行数、列数)
dim(mtcars)
# 查看列名
names(mtcars)
# 查看前6行
head(mtcars)
# 查看后6行
tail(mtcars)
# 查看行数
nrow(mtcars)
# 查看列数
ncol(mtcars)
2. 查看摘要信息
r
复制代码
# 整体摘要统计(最小值、1分位、中位数、均值、3分位、最大值)
summary(mtcars)
# 使用 psych 包获得更详细的描述统计
# install.packages("psych") # 首次运行需安装
library(psych)
describe(mtcars)
3. 查看数据整体概况
r
复制代码
# 使用 skimr 包生成整体概况
# install.packages("skimr")
library(skimr)
skim(mtcars)
# 查看数据类型分布
table(sapply(mtcars, class))
# 查看缺失值总数
sum(is.na(mtcars))
# 查看每列缺失值个数
colSums(is.na(mtcars))
二、数据清洗
1. 缺失值的检测与处理
r
复制代码
# 创建一个包含缺失值的数据框
df <- data.frame(
id = 1:5,
name = c("张三", "李四", NA, "赵六", "钱七"),
score = c(85, NA, 78, 92, NA)
)
print(df)
# === 检测缺失值 ===
# 判断每个元素是否为缺失值
is.na(df)
# 查看每列缺失值数量
colSums(is.na(df))
# 查看包含缺失值的行
df[!complete.cases(df), ]
# === 处理缺失值 ===
# 方法1:删除含有缺失值的行
df_clean1 <- na.omit(df)
print(df_clean1)
# 方法2:删除缺失值超过一定比例的列
# 如果某列缺失比例超过50%,则删除该列
df_clean2 <- df[, colMeans(is.na(df)) < 0.5]
# 方法3:用均值/中位数填充数值型缺失值
df$score[is.na(df$score)] <- mean(df$score, na.rm = TRUE)
# 方法4:用众数填充字符型缺失值
get_mode <- function(x) {
ux <- unique(x[!is.na(x)])
ux[which.max(tabulate(match(x, ux)))]
}
df$name[is.na(df$name)] <- get_mode(df$name)
# 方法5:使用 tidyr 包填充缺失值
# install.packages("tidyr")
library(tidyr)
df <- data.frame(id = 1:5, score = c(85, NA, 78, 92, NA))
# 用上一行的值填充
df_filled <- fill(df, score, .direction = "down")
print(df_filled)
2. 重复值的检测与处理
r
复制代码
# 创建包含重复值的数据框
df <- data.frame(
id = c(1, 2, 2, 3, 4, 4, 5),
name = c("A", "B", "B", "C", "D", "D", "E"),
value = c(10, 20, 20, 30, 40, 40, 50)
)
print(df)
# === 检测重复值 ===
# 对整行判断是否重复(第一次出现不算重复)
duplicated(df)
# 查看重复的行
df[duplicated(df), ]
# 统计完全重复的行数
sum(duplicated(df))
# 根据指定列判断重复(如id列)
duplicated(df$id)
# === 处理重复值 ===
# 方法1:删除完全重复的行(保留第一次出现)
df_unique <- distinct(df)
print(df_unique)
# 方法2:使用 dplyr 去重
library(dplyr)
df_unique2 <- df %>% distinct(id, .keep_all = TRUE)
# 方法3:删除重复行(保留最后一次出现)
df_unique3 <- df[!duplicated(df, fromLast = TRUE), ]
3. 异常值的检测与处理
r
复制代码
# 创建包含异常值的数据
df <- data.frame(
id = 1:10,
age = c(25, 28, 130, 30, 27, 200, 29, 26, 28, 27),
salary = c(5000, 5200, 1000000, 5100, 4900, 5300, 51000, 5050, 5150, 5080)
)
print(df)
# === 检测异常值(使用箱线图法:超过Q1-1.5*IQR 或 Q3+1.5*IQR)===
Q1 <- quantile(df$age, 0.25)
Q3 <- quantile(df$age, 0.75)
IQR_value <- Q3 - Q1
lower_bound <- Q1 - 1.5 * IQR_value
upper_bound <- Q3 + 1.5 * IQR_value
# 找出异常值
outliers <- df$age[df$age < lower_bound | df$age > upper_bound]
print(outliers)
# === Z-score 法检测异常值(|Z| > 3 视为异常)===
z_scores <- scale(df$age) # 计算Z分数
df$z_score <- as.numeric(z_scores)
outliers_z <- df[abs(df$z_score) > 3, ]
print(outliers_z)
# === 处理异常值 ===
# 方法1:删除异常值所在行
df_clean <- df[df$age >= lower_bound & df$age <= upper_bound, ]
# 方法2:将异常值替换为边界值(封箱处理)
df$age_win <- df$age
df$age_win[df$age_win < lower_bound] <- lower_bound
df$age_win[df$age_win > upper_bound] <- upper_bound
# 方法3:将异常值替换为中位数
median_age <- median(df$age[df$age >= lower_bound & df$age <= upper_bound], na.rm = TRUE)
df$age_fixed <- df$age
df$age_fixed[df$age_fixed < lower_bound | df$age_fixed > upper_bound] <- median_age
三、数据排序
r
复制代码
# 创建示例数据框
df <- data.frame(
name = c("张三", "李四", "王五", "赵六"),
age = c(25, 30, 22, 28),
score = c(88, 92, 85, 90)
)
print(df)
# === 基础排序(order函数)===
# 按 age 升序排列
df_order1 <- df[order(df$age), ]
print(df_order1)
# 按 age 降序排列
df_order2 <- df[order(-df$age), ]
print(df_order2)
# 按多列排序:先按 age 升序,再按 score 降序
df_order3 <- df[order(df$age, -df$score), ]
# === 使用 dplyr 排序 ===
library(dplyr)
# 升序排列
df_arrange1 <- df %>% arrange(age)
# 降序排列
df_arrange2 <- df %>% arrange(desc(age))
# 多列排序
df_arrange3 <- df %>% arrange(age, desc(score))
四、数据抽样
r
复制代码
# 创建示例数据
df <- data.frame(
id = 1:100,
value = rnorm(100, mean = 50, sd = 10)
)
# === 简单随机抽样 ===
# 方法1:使用 sample 函数抽取行索引
set.seed(123) # 设置随机种子,保证结果可重复
sample_indices <- sample(1:nrow(df), size = 10, replace = FALSE) # 抽取10个不重复的样本
df_sample1 <- df[sample_indices, ]
# 方法2:有放回抽样(自助法)
df_sample2 <- df[sample(1:nrow(df), size = 10, replace = TRUE), ]
# === 分层抽样 ===
# 创建带分组的数据
df_strata <- data.frame(
group = rep(c("A", "B", "C"), each = 30),
value = rnorm(90)
)
# 使用 dplyr 进行分层抽样(每组抽取5个)
library(dplyr)
df_strata_sample <- df_strata %>%
group_by(group) %>%
sample_n(size = 5) %>%
ungroup()
print(df_strata_sample)
# 按比例分层抽样(每组抽取20%)
df_strata_sample2 <- df_strata %>%
group_by(group) %>%
sample_frac(size = 0.2) %>%
ungroup()
五、字符串处理 --- stringr包
1. stringr包常用函数
r
复制代码
# 加载 stringr 包
# install.packages("stringr")
library(stringr)
# 示例字符串
text <- c("Hello World", "R is great", "Data Science", " spaces ")
names <- c("张三", "李四", "王五")
# 1. 计算字符串长度
str_length(text)
# 2. 字符串拼接
str_c("Hello", "World", sep = "_") # 输出 "Hello_World"
str_c(names, "同学", sep = "") # 输出 "张三同学" "李四同学" "王五同学"
# 3. 提取子串(按位置)
str_sub(text, 1, 5) # 提取每个字符串的第1到第5个字符
# 4. 检测是否包含某个模式
str_detect(text, "R") # 返回 TRUE FALSE FALSE FALSE
# 5. 统计匹配次数
str_count(text, "a") # 统计每个字符串中字母a出现的次数
# 6. 大小写转换
str_to_upper(text) # 转大写
str_to_lower(text) # 转小写
str_to_title(text) # 每个单词首字母大写
# 7. 去除空白字符
str_trim(" spaces ") # 去除首尾空白,输出 "spaces"
str_squish(" Hello World ") # 去除所有多余空白,输出 "Hello World"
# 8. 替换
str_replace_all("apple apple apple", "apple", "orange") # 替换所有匹配
2. 字符串替换函数 str_sub()
r
复制代码
library(stringr)
# str_sub() 用于提取或替换子串
text <- "ABCDEFGHIJK"
# 提取子串
str_sub(text, 3, 6) # 提取第3到第6个字符 = "CDEF"
str_sub(text, 4) # 从第4个字符到结尾 = "DEFGHIJK"
str_sub(text, -3) # 从倒数第3个字符到结尾 = "IJK"
str_sub(text, -5, -2) # 从倒数第5到倒数第2 = "FGHI"
# 替换子串
x <- "Hello World"
str_sub(x, 7, 11) <- "R"
print(x) # 输出 "Hello R"
# 批量替换
addresses <- c("北京市朝阳区", "上海市浦东新区", "广州市天河区")
str_sub(addresses, 1, 3) <- "XX"
print(addresses) # 输出 "XX市朝阳区" "XX市浦东新区" "XX市天河区"
3. 字符串分割函数 str_split()
r
复制代码
library(stringr)
# 基本分割
text <- "apple,banana,orange,grape"
result <- str_split(text, pattern = ",")
print(result) # 返回一个列表
# 简化输出为矩阵(当每个分割后长度一致时)
result_simple <- str_split_fixed(text, pattern = ",", n = 4)
print(result_simple) # 返回字符矩阵
# 分割多行字符串
fruits <- c("a:apple", "b:banana", "c:cherry")
result_list <- str_split(fruits, ":")
print(result_list)
# 使用简化版提取特定位置
result_simple <- str_split_fixed(fruits, ":", 2)
colnames(result_simple) <- c("code", "fruit")
print(result_simple)
# 分割并展开为多行(使用 tidyr)
library(tidyr)
df <- data.frame(id = 1:3, tags = c("R,Python,SQL", "Java,C++", "JavaScript"))
df_split <- df %>%
separate_rows(tags, sep = ",") # 将逗号分隔的标签拆分为多行
print(df_split)
4. 正则表达式的应用
r
复制代码
library(stringr)
library(dplyr)
# 示例数据
emails <- c(
"zhang@example.com",
"li@company.org",
"invalid-email",
"wang@test.net",
"no-at-sign"
)
phones <- c(
"13812345678",
"010-87654321",
"12345",
"18888888888",
"021-1234567"
)
# === 正则表达式常用元字符 ===
# . : 匹配任意单个字符(除换行符)
# ^ : 匹配字符串开头
# $ : 匹配字符串结尾
# * : 匹配前一个字符0次或多次
# + : 匹配前一个字符1次或多次
# ? : 匹配前一个字符0次或1次
# \d : 匹配数字,等价于[0-9]
# \w : 匹配字母、数字、下划线
# \s : 匹配空白字符(空格、制表符等)
# [abc] : 匹配a、b或c中的任意一个
# [^abc] : 匹配除a、b、c以外的任意字符
# {n} : 匹配前一个字符恰好n次
# {n,m} : 匹配前一个字符n到m次
# 1. 检测字符串是否匹配正则
str_detect(emails, "@") # 检测是否包含@符号
# 2. 提取匹配的内容
# 提取邮箱中的用户名(@之前的部分)
username <- str_extract(emails, "^[^@]+")
print(username)
# 提取手机号(11位数字,以1开头)
phone_pattern <- "^1[0-9]{10}$"
valid_phones <- phones[str_detect(phones, phone_pattern)]
print(valid_phones)
# 3. 提取所有匹配(如提取所有数字)
text <- "订单号:2024001,金额:299.5元,数量:3件"
numbers <- str_extract_all(text, "\\d+\\.?\\d*") # 匹配整数或小数
print(numbers)
# 4. 替换匹配内容
# 将手机号中间4位替换为****
hide_phone <- function(phone) {
str_replace(phone, "(\\d{3})\\d{4}(\\d{4})", "\\1****\\2")
}
print(hide_phone("13812345678")) # 输出 "138****5678"
# 5. 邮箱验证
email_pattern <- "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
valid_emails <- emails[str_detect(emails, email_pattern)]
print(valid_emails)
# 6. 提取日期信息
text2 <- "会议日期:2024-08-20,截止日期:2024/09/01"
dates <- str_extract_all(text2, "\\d{4}[-/]\\d{2}[-/]\\d{2}")
print(dates)
六、数据合并与拆分
1. 数据合并
r
复制代码
# === 按列合并(cbind):要求行数相同 ===
df1 <- data.frame(id = 1:3, name = c("A", "B", "C"))
df2 <- data.frame(score = c(85, 90, 88), grade = c("B", "A", "B"))
df_col <- cbind(df1, df2)
print(df_col)
# === 按行合并(rbind):要求列名相同 ===
df3 <- data.frame(id = 4:5, name = c("D", "E"))
df_row <- rbind(df1, df3)
print(df_row)
# === 使用 merge 进行表连接 ===
employees <- data.frame(
emp_id = c(101, 102, 103, 104),
name = c("张三", "李四", "王五", "赵六"),
dept_id = c(1, 2, 1, 3)
)
departments <- data.frame(
dept_id = c(1, 2, 3, 4),
dept_name = c("技术部", "市场部", "人事部", "财务部")
)
# 内连接(只保留匹配的行)
inner_join <- merge(employees, departments, by = "dept_id", all = FALSE)
print(inner_join)
# 左连接(保留左表所有行)
left_join <- merge(employees, departments, by = "dept_id", all.x = TRUE)
# 右连接(保留右表所有行)
right_join <- merge(employees, departments, by = "dept_id", all.y = TRUE)
# 全外连接(保留所有行)
full_join <- merge(employees, departments, by = "dept_id", all = TRUE)
# === 使用 dplyr 进行连接(更简洁)===
library(dplyr)
inner_join_dplyr <- employees %>% inner_join(departments, by = "dept_id")
left_join_dplyr <- employees %>% left_join(departments, by = "dept_id")
2. 数据拆分
r
复制代码
# === split 函数按因子拆分 ===
df <- data.frame(
id = 1:10,
group = rep(c("A", "B", "C"), length.out = 10),
value = runif(10, 0, 100)
)
# 按group列拆分为多个数据框
split_list <- split(df, df$group)
print(split_list$A) # 访问组A的数据
# === 拆分列为多列 ===
dates <- c("2024-01-15", "2024-02-20", "2024-03-25")
# 使用 str_split_fixed 拆分日期
date_split <- str_split_fixed(dates, "-", 3)
colnames(date_split) <- c("year", "month", "day")
date_df <- as.data.frame(date_split)
print(date_df)
# 使用 tidyr::separate 拆分列
library(tidyr)
df_dates <- data.frame(id = 1:3, full_date = dates)
df_separated <- df_dates %>%
separate(full_date, into = c("year", "month", "day"), sep = "-")
print(df_separated)
七、数据分段
r
复制代码
# 创建示例数据
ages <- c(18, 22, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70)
df <- data.frame(id = 1:12, age = ages, score = runif(12, 50, 100))
# === cut 函数实现数据分段 ===
# 等距分段(将年龄分为3段)
df$age_group1 <- cut(df$age, breaks = 3)
print(table(df$age_group1))
# 自定义断点分段
breaks <- c(0, 30, 50, 100) # 断点
labels <- c("青年", "中年", "老年") # 分段标签
df$age_group2 <- cut(df$age, breaks = breaks, labels = labels, right = FALSE)
print(df[, c("age", "age_group2")])
# 包含区间显示
df$age_interval <- cut(df$age, breaks = breaks, right = FALSE)
# === 使用 ifelse 进行条件分段 ===
df$age_category <- ifelse(df$age < 30, "青年",
ifelse(df$age < 50, "中年", "老年"))
# === 使用 dplyr 的 case_when 进行多条件分段(推荐)===
library(dplyr)
df <- df %>%
mutate(
score_level = case_when(
score < 60 ~ "不及格",
score < 70 ~ "及格",
score < 85 ~ "良好",
score <= 100 ~ "优秀",
TRUE ~ "未知"
)
)
# 查看分段统计
table(df$score_level)
八、数据转换与重塑
1. 将数据转换为数字格式
r
复制代码
# 创建混合类型数据
df <- data.frame(
id = c("1", "2", "3", "4"),
price = c("10.5", "20.8", "thirty", "40.2"),
quantity = c("5", "A", "10", "8"),
stringsAsFactors = FALSE
)
print(df)
str(df) # 查看各列类型
# === 安全的类型转换 ===
# 转换为数值型(非数字会变成NA)
df$price_num <- as.numeric(df$price) # "thirty" 变成 NA
df$quantity_num <- as.numeric(df$quantity) # "A" 变成 NA
# 使用 suppressWarnings 隐藏转换警告
df$price_num2 <- suppressWarnings(as.numeric(df$price))
# === 使用 type.convert 自动转换 ===
df2 <- data.frame(
a = c("1", "2", "3"),
b = c("4.5", "5.6", "6.7"),
c = c("TRUE", "FALSE", "TRUE"),
stringsAsFactors = FALSE
)
df2_converted <- type.convert(df2, as.is = TRUE)
str(df2_converted)
# === 处理转换中的错误 ===
# 自定义安全的转换函数
safe_as_numeric <- function(x) {
result <- rep(NA_real_, length(x))
for (i in seq_along(x)) {
converted <- suppressWarnings(as.numeric(x[i]))
if (!is.na(converted)) {
result[i] <- converted
}
}
return(result)
}
df$price_safe <- safe_as_numeric(df$price)
df$quantity_safe <- safe_as_numeric(df$quantity)
print(df)
2. 数据转置
r
复制代码
# 创建矩阵形式的数据
df <- data.frame(
Product = c("A", "B", "C"),
Q1 = c(100, 150, 120),
Q2 = c(110, 140, 130),
Q3 = c(120, 160, 125),
Q4 = c(130, 170, 135)
)
print(df)
# === 使用 t() 函数转置 ===
# 先保存行名为产品名,删除产品列
mat <- as.matrix(df[, -1])
rownames(mat) <- df$Product
mat_t <- t(mat) # 转置
print(mat_t)
# === 使用 tidyr 的 pivot_longer / pivot_wider 进行长宽转换 ===
library(tidyr)
# 宽表转长表(类似转置的效果)
df_long <- df %>%
pivot_longer(
cols = starts_with("Q"), # 选择所有季度列
names_to = "Quarter", # 新列名:季度
values_to = "Sales" # 新列名:销售额
)
print(df_long)
# 长表转宽表(逆向操作)
df_wide <- df_long %>%
pivot_wider(
names_from = Quarter, # 季度列的值变成新列名
values_from = Sales # 销售额列的值填充到新列
)
print(df_wide)
3. 数据整合(聚合)
r
复制代码
# 创建示例销售数据
sales <- data.frame(
product = c("A", "A", "B", "B", "A", "C", "B", "C"),
region = c("北区", "南区", "北区", "南区", "北区", "南区", "北区", "南区"),
amount = c(100, 150, 200, 120, 130, 180, 110, 160),
quantity = c(5, 7, 10, 6, 6, 9, 5, 8)
)
print(sales)
# === 使用 aggregate 函数 ===
# 按单一变量分组求和
by_product <- aggregate(amount ~ product, data = sales, FUN = sum)
print(by_product)
# 按多变量分组,计算多个统计量
by_product_region <- aggregate(
cbind(amount, quantity) ~ product + region,
data = sales,
FUN = function(x) c(sum = sum(x), mean = mean(x))
)
print(by_product_region)
# === 使用 dplyr 进行数据整合(推荐)===
library(dplyr)
# 分组求和
summary1 <- sales %>%
group_by(product) %>%
summarise(
total_amount = sum(amount),
total_quantity = sum(quantity),
avg_amount = mean(amount),
count = n() # 记录数
)
print(summary1)
# 多级分组汇总
summary2 <- sales %>%
group_by(product, region) %>%
summarise(
total_amount = sum(amount),
avg_quantity = mean(quantity),
.groups = "drop" # 取消分组
)
print(summary2)
# 计算分组占比
summary3 <- sales %>%
group_by(product) %>%
mutate(
product_total = sum(amount),
ratio = amount / product_total * 100
) %>%
ungroup()
print(summary3)
# 使用 data.table 包进行高效聚合(大数据集推荐)
# install.packages("data.table")
library(data.table)
dt <- as.data.table(sales)
dt_summary <- dt[, .(
total_amount = sum(amount),
avg_quantity = mean(quantity),
.N
), by = .(product, region)]
print(dt_summary)
九、要点回顾
| 知识点 |
核心函数/包 |
关键要点 |
| 查看数据 |
str(), summary(), head(), skim() |
了解数据结构、类型、分布、缺失情况 |
| 缺失值处理 |
is.na(), na.omit(), tidyr::fill() |
先检测再处理:删除/填充/插补 |
| 重复值处理 |
duplicated(), dplyr::distinct() |
完全重复或基于特定列去重 |
| 异常值处理 |
箱线图法、Z-score法 |
检测后根据业务逻辑处理:删除/替换/保留 |
| 排序 |
order(), dplyr::arrange() |
支持单列/多列、升序/降序 |
| 抽样 |
sample(), dplyr::sample_n() |
简单随机、分层抽样、有放回/无放回 |
| 字符串处理 |
stringr包 |
str_sub(), str_split(), str_replace() |
| 正则表达式 |
str_detect(), str_extract() |
模式匹配、提取、替换、验证 |
| 数据合并 |
rbind(), cbind(), merge() |
按行/列合并,各种连接方式 |
| 数据拆分 |
split(), tidyr::separate() |
按条件拆分数据框或拆分列 |
| 数据分段 |
cut(), ifelse(), case_when() |
连续变量离散化 |
| 类型转换 |
as.numeric(), type.convert() |
注意非数字字符导致NA |
| 数据转置 |
t(), pivot_longer(), pivot_wider() |
长表与宽表互转 |
| 数据聚合 |
aggregate(), dplyr::summarise() |
分组计算总和、均值、计数等 |