R语言入门学习教程,从入门到精通,R语言获取数据 (8)

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() 分组计算总和、均值、计数等
相关推荐
qq_452396231 小时前
第十一篇:《性能压测基础:JMeter线程模型与压测策略设计》
java·开发语言·jmeter
ComputerInBook1 小时前
C++ 关键字 constexpr 和 consteval 之注意事项
开发语言·c++·constexpr·consteval
sensen_kiss1 小时前
CAN302 Technologies for E-Commerce 电子商务技术 Pt.8 网络安全(Secure the Web)
网络·学习·安全·web安全
通信小呆呆1 小时前
注意力机制用于信号同步:从匹配滤波到可学习对齐
人工智能·学习·机器学习·信息与通信
澈2071 小时前
二叉搜索树:高效增删查的秘诀
java·开发语言·算法
米啦啦.1 小时前
STL(标准模板库)
开发语言·c++·stl
lly2024062 小时前
建造者模式:构建复杂对象的最佳实践
开发语言
无尽冬.2 小时前
个人八股之string字符串
java·开发语言·经验分享·后端·异世界
YangYang9YangYan2 小时前
2026运营岗位学习数据分析对于提升个人能力的价值
学习·数据挖掘·数据分析