计算变量就是根据已有变量,创建新的变量。
例如学生成绩数据中有:
chinese, math, english
我们可以计算:
total = chinese + math + english
average = total / 3
这两个新变量就是计算变量。
student <- data.frame(
id = 1:8,
name = c("A", "B", "C", "D", "E", "F", "G", "H"),
gender = c("女", "男", "男", "女", "男", "女", "男", "女"),
chinese = c(85, 92, 78, 88, 95, 70, 82, 90),
math = c(90, 85, 80, 92, 96, 75, 88, 91),
english = c(88, 89, 76, 90, 94, 72, 84, 93)
)
student
## id name gender chinese math english
## 1 1 A 女 85 90 88
## 2 2 B 男 92 85 89
## 3 3 C 男 78 80 76
## 4 4 D 女 88 92 90
## 5 5 E 男 95 96 94
## 6 6 F 女 70 75 72
## 7 7 G 男 82 88 84
## 8 8 H 女 90 91 93
查看数据结构:
str(student)
## 'data.frame': 8 obs. of 6 variables:
## $ id : int 1 2 3 4 5 6 7 8
## $ name : chr "A" "B" "C" "D" ...
## $ gender : chr "女" "男" "男" "女" ...
## $ chinese: num 85 92 78 88 95 70 82 90
## $ math : num 90 85 80 92 96 75 88 91
## $ english: num 88 89 76 90 94 72 84 93
假设我们要计算三科总分:
student$total <- student$chinese + student$math + student$english
student
## id name gender chinese math english total
## 1 1 A 女 85 90 88 263
## 2 2 B 男 92 85 89 266
## 3 3 C 男 78 80 76 234
## 4 4 D 女 88 92 90 270
## 5 5 E 男 95 96 94 285
## 6 6 F 女 70 75 72 217
## 7 7 G 男 82 88 84 254
## 8 8 H 女 90 91 93 274
解释:
student$total
表示在 student 数据框中创建一个新变量
total。
右边:
student$chinese + student$math + student$english
表示三科分数相加。
student$average <- student$total / 3
student
## id name gender chinese math english total average
## 1 1 A 女 85 90 88 263 87.66667
## 2 2 B 男 92 85 89 266 88.66667
## 3 3 C 男 78 80 76 234 78.00000
## 4 4 D 女 88 92 90 270 90.00000
## 5 5 E 男 95 96 94 285 95.00000
## 6 6 F 女 70 75 72 217 72.33333
## 7 7 G 男 82 88 84 254 84.66667
## 8 8 H 女 90 91 93 274 91.33333
也可以直接写成:
student$average2 <- (student$chinese + student$math + student$english) / 3
student
## id name gender chinese math english total average average2
## 1 1 A 女 85 90 88 263 87.66667 87.66667
## 2 2 B 男 92 85 89 266 88.66667 88.66667
## 3 3 C 男 78 80 76 234 78.00000 78.00000
## 4 4 D 女 88 92 90 270 90.00000 90.00000
## 5 5 E 男 95 96 94 285 95.00000 95.00000
## 6 6 F 女 70 75 72 217 72.33333 72.33333
## 7 7 G 男 82 88 84 254 84.66667 84.66667
## 8 8 H 女 90 91 93 274 91.33333 91.33333
例如计算数学比语文高多少分:
student$math_minus_chinese <- student$math - student$chinese
student
## id name gender chinese math english total average average2
## 1 1 A 女 85 90 88 263 87.66667 87.66667
## 2 2 B 男 92 85 89 266 88.66667 88.66667
## 3 3 C 男 78 80 76 234 78.00000 78.00000
## 4 4 D 女 88 92 90 270 90.00000 90.00000
## 5 5 E 男 95 96 94 285 95.00000 95.00000
## 6 6 F 女 70 75 72 217 72.33333 72.33333
## 7 7 G 男 82 88 84 254 84.66667 84.66667
## 8 8 H 女 90 91 93 274 91.33333 91.33333
## math_minus_chinese
## 1 5
## 2 -7
## 3 2
## 4 4
## 5 1
## 6 5
## 7 6
## 8 1
如果结果是正数,说明数学比语文高。
如果结果是负数,说明数学比语文低。
例如计算语文分数占总分的比例:
student$chinese_ratio <- student$chinese / student$total
student
## id name gender chinese math english total average average2
## 1 1 A 女 85 90 88 263 87.66667 87.66667
## 2 2 B 男 92 85 89 266 88.66667 88.66667
## 3 3 C 男 78 80 76 234 78.00000 78.00000
## 4 4 D 女 88 92 90 270 90.00000 90.00000
## 5 5 E 男 95 96 94 285 95.00000 95.00000
## 6 6 F 女 70 75 72 217 72.33333 72.33333
## 7 7 G 男 82 88 84 254 84.66667 84.66667
## 8 8 H 女 90 91 93 274 91.33333 91.33333
## math_minus_chinese chinese_ratio
## 1 5 0.3231939
## 2 -7 0.3458647
## 3 2 0.3333333
## 4 4 0.3259259
## 5 1 0.3333333
## 6 5 0.3225806
## 7 6 0.3228346
## 8 1 0.3284672
如果想显示成百分比,可以乘以 100:
student$chinese_percent <- student$chinese_ratio * 100
student
## id name gender chinese math english total average average2
## 1 1 A 女 85 90 88 263 87.66667 87.66667
## 2 2 B 男 92 85 89 266 88.66667 88.66667
## 3 3 C 男 78 80 76 234 78.00000 78.00000
## 4 4 D 女 88 92 90 270 90.00000 90.00000
## 5 5 E 男 95 96 94 285 95.00000 95.00000
## 6 6 F 女 70 75 72 217 72.33333 72.33333
## 7 7 G 男 82 88 84 254 84.66667 84.66667
## 8 8 H 女 90 91 93 274 91.33333 91.33333
## math_minus_chinese chinese_ratio chinese_percent
## 1 5 0.3231939 32.31939
## 2 -7 0.3458647 34.58647
## 3 2 0.3333333 33.33333
## 4 4 0.3259259 32.59259
## 5 1 0.3333333 33.33333
## 6 5 0.3225806 32.25806
## 7 6 0.3228346 32.28346
## 8 1 0.3284672 32.84672
比例经常会有很多小数,可以用 round() 保留小数位。
student$chinese_percent_round <- round(student$chinese_percent, 2)
student
## id name gender chinese math english total average average2
## 1 1 A 女 85 90 88 263 87.66667 87.66667
## 2 2 B 男 92 85 89 266 88.66667 88.66667
## 3 3 C 男 78 80 76 234 78.00000 78.00000
## 4 4 D 女 88 92 90 270 90.00000 90.00000
## 5 5 E 男 95 96 94 285 95.00000 95.00000
## 6 6 F 女 70 75 72 217 72.33333 72.33333
## 7 7 G 男 82 88 84 254 84.66667 84.66667
## 8 8 H 女 90 91 93 274 91.33333 91.33333
## math_minus_chinese chinese_ratio chinese_percent chinese_percent_round
## 1 5 0.3231939 32.31939 32.32
## 2 -7 0.3458647 34.58647 34.59
## 3 2 0.3333333 33.33333 33.33
## 4 4 0.3259259 32.59259 32.59
## 5 1 0.3333333 33.33333 33.33
## 6 5 0.3225806 32.25806 32.26
## 7 6 0.3228346 32.28346 32.28
## 8 1 0.3284672 32.84672 32.85
解释:
round(x, 2)
表示保留 2 位小数。
| 函数 | 作用 |
|---|---|
+ |
加法 |
- |
减法 |
* |
乘法 |
/ |
除法 |
round(x, 2) |
保留 2 位小数 |
sqrt(x) |
开平方 |
log(x) |
取自然对数 |
abs(x) |
绝对值 |
mean(x) |
平均值 |
sum(x) |
求和 |
ifelse() 可以根据条件生成新变量。
语法:
ifelse(条件, 条件成立时的结果, 条件不成立时的结果)
例如:
ifelse(score >= 60, "及格", "不及格")
根据平均分判断是否及格:
student$pass <- ifelse(student$average >= 60, "及格", "不及格")
student
## id name gender chinese math english total average average2
## 1 1 A 女 85 90 88 263 87.66667 87.66667
## 2 2 B 男 92 85 89 266 88.66667 88.66667
## 3 3 C 男 78 80 76 234 78.00000 78.00000
## 4 4 D 女 88 92 90 270 90.00000 90.00000
## 5 5 E 男 95 96 94 285 95.00000 95.00000
## 6 6 F 女 70 75 72 217 72.33333 72.33333
## 7 7 G 男 82 88 84 254 84.66667 84.66667
## 8 8 H 女 90 91 93 274 91.33333 91.33333
## math_minus_chinese chinese_ratio chinese_percent chinese_percent_round pass
## 1 5 0.3231939 32.31939 32.32 及格
## 2 -7 0.3458647 34.58647 34.59 及格
## 3 2 0.3333333 33.33333 33.33 及格
## 4 4 0.3259259 32.59259 32.59 及格
## 5 1 0.3333333 33.33333 33.33 及格
## 6 5 0.3225806 32.25806 32.26 及格
## 7 6 0.3228346 32.28346 32.28 及格
## 8 1 0.3284672 32.84672 32.85 及格
假设平均分大于等于 90 是优秀:
student$excellent <- ifelse(student$average >= 90, "优秀", "非优秀")
student
## id name gender chinese math english total average average2
## 1 1 A 女 85 90 88 263 87.66667 87.66667
## 2 2 B 男 92 85 89 266 88.66667 88.66667
## 3 3 C 男 78 80 76 234 78.00000 78.00000
## 4 4 D 女 88 92 90 270 90.00000 90.00000
## 5 5 E 男 95 96 94 285 95.00000 95.00000
## 6 6 F 女 70 75 72 217 72.33333 72.33333
## 7 7 G 男 82 88 84 254 84.66667 84.66667
## 8 8 H 女 90 91 93 274 91.33333 91.33333
## math_minus_chinese chinese_ratio chinese_percent chinese_percent_round pass
## 1 5 0.3231939 32.31939 32.32 及格
## 2 -7 0.3458647 34.58647 34.59 及格
## 3 2 0.3333333 33.33333 33.33 及格
## 4 4 0.3259259 32.59259 32.59 及格
## 5 1 0.3333333 33.33333 33.33 及格
## 6 5 0.3225806 32.25806 32.26 及格
## 7 6 0.3228346 32.28346 32.28 及格
## 8 1 0.3284672 32.84672 32.85 及格
## excellent
## 1 非优秀
## 2 非优秀
## 3 非优秀
## 4 优秀
## 5 优秀
## 6 非优秀
## 7 非优秀
## 8 优秀
如果要分成多个等级,比如:
| 平均分 | 等级 |
|---|---|
| >= 90 | A |
| >= 80 且 < 90 | B |
| >= 70 且 < 80 | C |
| < 70 | D |
可以嵌套使用 ifelse():
student$grade <- ifelse(
student$average >= 90, "A",
ifelse(
student$average >= 80, "B",
ifelse(student$average >= 70, "C", "D")
)
)
student
## id name gender chinese math english total average average2
## 1 1 A 女 85 90 88 263 87.66667 87.66667
## 2 2 B 男 92 85 89 266 88.66667 88.66667
## 3 3 C 男 78 80 76 234 78.00000 78.00000
## 4 4 D 女 88 92 90 270 90.00000 90.00000
## 5 5 E 男 95 96 94 285 95.00000 95.00000
## 6 6 F 女 70 75 72 217 72.33333 72.33333
## 7 7 G 男 82 88 84 254 84.66667 84.66667
## 8 8 H 女 90 91 93 274 91.33333 91.33333
## math_minus_chinese chinese_ratio chinese_percent chinese_percent_round pass
## 1 5 0.3231939 32.31939 32.32 及格
## 2 -7 0.3458647 34.58647 34.59 及格
## 3 2 0.3333333 33.33333 33.33 及格
## 4 4 0.3259259 32.59259 32.59 及格
## 5 1 0.3333333 33.33333 33.33 及格
## 6 5 0.3225806 32.25806 32.26 及格
## 7 6 0.3228346 32.28346 32.28 及格
## 8 1 0.3284672 32.84672 32.85 及格
## excellent grade
## 1 非优秀 B
## 2 非优秀 B
## 3 非优秀 C
## 4 优秀 A
## 5 优秀 A
## 6 非优秀 C
## 7 非优秀 B
## 8 优秀 A
解释:
>= 90,是就是 A;>= 80,是就是 B;>= 70,是就是 C;等级 A、B、C、D 是有顺序的。
student$grade_f <- factor(
student$grade,
levels = c("D", "C", "B", "A"),
ordered = TRUE
)
student
## id name gender chinese math english total average average2
## 1 1 A 女 85 90 88 263 87.66667 87.66667
## 2 2 B 男 92 85 89 266 88.66667 88.66667
## 3 3 C 男 78 80 76 234 78.00000 78.00000
## 4 4 D 女 88 92 90 270 90.00000 90.00000
## 5 5 E 男 95 96 94 285 95.00000 95.00000
## 6 6 F 女 70 75 72 217 72.33333 72.33333
## 7 7 G 男 82 88 84 254 84.66667 84.66667
## 8 8 H 女 90 91 93 274 91.33333 91.33333
## math_minus_chinese chinese_ratio chinese_percent chinese_percent_round pass
## 1 5 0.3231939 32.31939 32.32 及格
## 2 -7 0.3458647 34.58647 34.59 及格
## 3 2 0.3333333 33.33333 33.33 及格
## 4 4 0.3259259 32.59259 32.59 及格
## 5 1 0.3333333 33.33333 33.33 及格
## 6 5 0.3225806 32.25806 32.26 及格
## 7 6 0.3228346 32.28346 32.28 及格
## 8 1 0.3284672 32.84672 32.85 及格
## excellent grade grade_f
## 1 非优秀 B B
## 2 非优秀 B B
## 3 非优秀 C C
## 4 优秀 A A
## 5 优秀 A A
## 6 非优秀 C C
## 7 非优秀 B B
## 8 优秀 A A
查看水平:
levels(student$grade_f)
## [1] "D" "C" "B" "A"
如果有很多列要相加,用 rowSums() 更方便。
先创建一个新数据:
score_df <- data.frame(
id = 1:5,
chinese = c(80, 90, 75, 88, 95),
math = c(85, 92, 78, 90, 96),
english = c(82, 89, 77, 91, 93)
)
score_df
## id chinese math english
## 1 1 80 85 82
## 2 2 90 92 89
## 3 3 75 78 77
## 4 4 88 90 91
## 5 5 95 96 93
计算三科总分:
score_df$total <- rowSums(score_df[, c("chinese", "math", "english")])
score_df
## id chinese math english total
## 1 1 80 85 82 247
## 2 2 90 92 89 271
## 3 3 75 78 77 230
## 4 4 88 90 91 269
## 5 5 95 96 93 284
解释:
score_df[, c("chinese", "math", "english")]
表示选中这三列。
rowSums()
表示按行求和。
score_df$average <- rowMeans(score_df[, c("chinese", "math", "english")])
score_df
## id chinese math english total average
## 1 1 80 85 82 247 82.33333
## 2 2 90 92 89 271 90.33333
## 3 3 75 78 77 230 76.66667
## 4 4 88 90 91 269 89.66667
## 5 5 95 96 93 284 94.66667
解释:
rowMeans()
表示按行计算平均值。
如果数据里有缺失值,计算时要注意。
score_na <- data.frame(
id = 1:5,
chinese = c(80, 90, NA, 88, 95),
math = c(85, NA, 78, 90, 96),
english = c(82, 89, 77, NA, 93)
)
score_na
## id chinese math english
## 1 1 80 85 82
## 2 2 90 NA 89
## 3 3 NA 78 77
## 4 4 88 90 NA
## 5 5 95 96 93
直接计算总分:
score_na$total_wrong <- rowSums(score_na[, c("chinese", "math", "english")])
score_na
## id chinese math english total_wrong
## 1 1 80 85 82 247
## 2 2 90 NA 89 NA
## 3 3 NA 78 77 NA
## 4 4 88 90 NA NA
## 5 5 95 96 93 284
你会发现只要某一行有 NA,总分就会变成 NA。
如果想忽略缺失值,可以加:
na.rm = TRUE
score_na$total_ignore_na <- rowSums(
score_na[, c("chinese", "math", "english")],
na.rm = TRUE
)
score_na
## id chinese math english total_wrong total_ignore_na
## 1 1 80 85 82 247 247
## 2 2 90 NA 89 NA 179
## 3 3 NA 78 77 NA 155
## 4 4 88 90 NA NA 178
## 5 5 95 96 93 284 284
平均分也一样:
score_na$average_ignore_na <- rowMeans(
score_na[, c("chinese", "math", "english")],
na.rm = TRUE
)
score_na
## id chinese math english total_wrong total_ignore_na average_ignore_na
## 1 1 80 85 82 247 247 82.33333
## 2 2 90 NA 89 NA 179 89.50000
## 3 3 NA 78 77 NA 155 77.50000
## 4 4 88 90 NA NA 178 89.00000
## 5 5 95 96 93 284 284 94.66667
如果一名学生缺考一科,直接忽略 NA 计算平均分,可能会让结果偏高。
例如:
80, 85, NA
如果忽略 NA:
平均分 = (80 + 85) / 2
但如果把缺考看作 0 分:
平均分 = (80 + 85 + 0) / 3
所以是否使用 na.rm = TRUE,要看具体业务含义。
可以先把 NA 替换为 0。
score_zero <- score_na
score_zero[is.na(score_zero)] <- 0
score_zero
## id chinese math english total_wrong total_ignore_na average_ignore_na
## 1 1 80 85 82 247 247 82.33333
## 2 2 90 0 89 0 179 89.50000
## 3 3 0 78 77 0 155 77.50000
## 4 4 88 90 0 0 178 89.00000
## 5 5 95 96 93 284 284 94.66667
重新计算总分:
score_zero$total <- rowSums(score_zero[, c("chinese", "math", "english")])
score_zero
## id chinese math english total_wrong total_ignore_na average_ignore_na total
## 1 1 80 85 82 247 247 82.33333 247
## 2 2 90 0 89 0 179 89.50000 179
## 3 3 0 78 77 0 155 77.50000 155
## 4 4 88 90 0 0 178 89.00000 178
## 5 5 95 96 93 284 284 94.66667 284
在实际数据处理中,dplyr 的 mutate()
很常用。
如果没有安装,可以先运行:
install.packages("dplyr")
加载包:
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
student2 <- student %>%
mutate(
total2 = chinese + math + english,
average3 = total2 / 3
)
student2
## id name gender chinese math english total average average2
## 1 1 A 女 85 90 88 263 87.66667 87.66667
## 2 2 B 男 92 85 89 266 88.66667 88.66667
## 3 3 C 男 78 80 76 234 78.00000 78.00000
## 4 4 D 女 88 92 90 270 90.00000 90.00000
## 5 5 E 男 95 96 94 285 95.00000 95.00000
## 6 6 F 女 70 75 72 217 72.33333 72.33333
## 7 7 G 男 82 88 84 254 84.66667 84.66667
## 8 8 H 女 90 91 93 274 91.33333 91.33333
## math_minus_chinese chinese_ratio chinese_percent chinese_percent_round pass
## 1 5 0.3231939 32.31939 32.32 及格
## 2 -7 0.3458647 34.58647 34.59 及格
## 3 2 0.3333333 33.33333 33.33 及格
## 4 4 0.3259259 32.59259 32.59 及格
## 5 1 0.3333333 33.33333 33.33 及格
## 6 5 0.3225806 32.25806 32.26 及格
## 7 6 0.3228346 32.28346 32.28 及格
## 8 1 0.3284672 32.84672 32.85 及格
## excellent grade grade_f total2 average3
## 1 非优秀 B B 263 87.66667
## 2 非优秀 B B 266 88.66667
## 3 非优秀 C C 234 78.00000
## 4 优秀 A A 270 90.00000
## 5 优秀 A A 285 95.00000
## 6 非优秀 C C 217 72.33333
## 7 非优秀 B B 254 84.66667
## 8 优秀 A A 274 91.33333
解释:
mutate()
表示在数据框中新增或修改变量。
在同一个 mutate() 中,后面可以使用前面刚创建的变量。
比如把平均分保留 1 位小数:
student2 <- student2 %>%
mutate(
average3 = round(average3, 1)
)
student2
## id name gender chinese math english total average average2
## 1 1 A 女 85 90 88 263 87.66667 87.66667
## 2 2 B 男 92 85 89 266 88.66667 88.66667
## 3 3 C 男 78 80 76 234 78.00000 78.00000
## 4 4 D 女 88 92 90 270 90.00000 90.00000
## 5 5 E 男 95 96 94 285 95.00000 95.00000
## 6 6 F 女 70 75 72 217 72.33333 72.33333
## 7 7 G 男 82 88 84 254 84.66667 84.66667
## 8 8 H 女 90 91 93 274 91.33333 91.33333
## math_minus_chinese chinese_ratio chinese_percent chinese_percent_round pass
## 1 5 0.3231939 32.31939 32.32 及格
## 2 -7 0.3458647 34.58647 34.59 及格
## 3 2 0.3333333 33.33333 33.33 及格
## 4 4 0.3259259 32.59259 32.59 及格
## 5 1 0.3333333 33.33333 33.33 及格
## 6 5 0.3225806 32.25806 32.26 及格
## 7 6 0.3228346 32.28346 32.28 及格
## 8 1 0.3284672 32.84672 32.85 及格
## excellent grade grade_f total2 average3
## 1 非优秀 B B 263 87.7
## 2 非优秀 B B 266 88.7
## 3 非优秀 C C 234 78.0
## 4 优秀 A A 270 90.0
## 5 优秀 A A 285 95.0
## 6 非优秀 C C 217 72.3
## 7 非优秀 B B 254 84.7
## 8 优秀 A A 274 91.3
如果变量名已经存在,mutate() 会覆盖原变量。
student2 <- student2 %>%
mutate(
pass2 = ifelse(average3 >= 60, "及格", "不及格"),
excellent2 = ifelse(average3 >= 90, "优秀", "非优秀")
)
student2
## id name gender chinese math english total average average2
## 1 1 A 女 85 90 88 263 87.66667 87.66667
## 2 2 B 男 92 85 89 266 88.66667 88.66667
## 3 3 C 男 78 80 76 234 78.00000 78.00000
## 4 4 D 女 88 92 90 270 90.00000 90.00000
## 5 5 E 男 95 96 94 285 95.00000 95.00000
## 6 6 F 女 70 75 72 217 72.33333 72.33333
## 7 7 G 男 82 88 84 254 84.66667 84.66667
## 8 8 H 女 90 91 93 274 91.33333 91.33333
## math_minus_chinese chinese_ratio chinese_percent chinese_percent_round pass
## 1 5 0.3231939 32.31939 32.32 及格
## 2 -7 0.3458647 34.58647 34.59 及格
## 3 2 0.3333333 33.33333 33.33 及格
## 4 4 0.3259259 32.59259 32.59 及格
## 5 1 0.3333333 33.33333 33.33 及格
## 6 5 0.3225806 32.25806 32.26 及格
## 7 6 0.3228346 32.28346 32.28 及格
## 8 1 0.3284672 32.84672 32.85 及格
## excellent grade grade_f total2 average3 pass2 excellent2
## 1 非优秀 B B 263 87.7 及格 非优秀
## 2 非优秀 B B 266 88.7 及格 非优秀
## 3 非优秀 C C 234 78.0 及格 非优秀
## 4 优秀 A A 270 90.0 及格 优秀
## 5 优秀 A A 285 95.0 及格 优秀
## 6 非优秀 C C 217 72.3 及格 非优秀
## 7 非优秀 B B 254 84.7 及格 非优秀
## 8 优秀 A A 274 91.3 及格 优秀
如果条件很多,case_when() 比嵌套 ifelse()
更清楚。
student2 <- student2 %>%
mutate(
grade2 = case_when(
average3 >= 90 ~ "A",
average3 >= 80 ~ "B",
average3 >= 70 ~ "C",
TRUE ~ "D"
)
)
student2
## id name gender chinese math english total average average2
## 1 1 A 女 85 90 88 263 87.66667 87.66667
## 2 2 B 男 92 85 89 266 88.66667 88.66667
## 3 3 C 男 78 80 76 234 78.00000 78.00000
## 4 4 D 女 88 92 90 270 90.00000 90.00000
## 5 5 E 男 95 96 94 285 95.00000 95.00000
## 6 6 F 女 70 75 72 217 72.33333 72.33333
## 7 7 G 男 82 88 84 254 84.66667 84.66667
## 8 8 H 女 90 91 93 274 91.33333 91.33333
## math_minus_chinese chinese_ratio chinese_percent chinese_percent_round pass
## 1 5 0.3231939 32.31939 32.32 及格
## 2 -7 0.3458647 34.58647 34.59 及格
## 3 2 0.3333333 33.33333 33.33 及格
## 4 4 0.3259259 32.59259 32.59 及格
## 5 1 0.3333333 33.33333 33.33 及格
## 6 5 0.3225806 32.25806 32.26 及格
## 7 6 0.3228346 32.28346 32.28 及格
## 8 1 0.3284672 32.84672 32.85 及格
## excellent grade grade_f total2 average3 pass2 excellent2 grade2
## 1 非优秀 B B 263 87.7 及格 非优秀 B
## 2 非优秀 B B 266 88.7 及格 非优秀 B
## 3 非优秀 C C 234 78.0 及格 非优秀 C
## 4 优秀 A A 270 90.0 及格 优秀 A
## 5 优秀 A A 285 95.0 及格 优秀 A
## 6 非优秀 C C 217 72.3 及格 非优秀 C
## 7 非优秀 B B 254 84.7 及格 非优秀 B
## 8 优秀 A A 274 91.3 及格 优秀 A
解释:
条件 ~ 结果
最后的:
TRUE ~ "D"
表示上面条件都不满足时,结果为 D。
判断等级时,必须先写高分条件:
average >= 90
average >= 80
average >= 70
不能先写:
average >= 70
否则 90 分也会先被归为 C。
再看一个常见例子:用身高和体重计算 BMI。
BMI 公式:
BMI = 体重kg / 身高m ^ 2
创建数据:
person <- data.frame(
id = 1:6,
height_cm = c(160, 172, 168, 180, 155, 175),
weight_kg = c(50, 70, 65, 85, 48, 90)
)
person
## id height_cm weight_kg
## 1 1 160 50
## 2 2 172 70
## 3 3 168 65
## 4 4 180 85
## 5 5 155 48
## 6 6 175 90
先把厘米转成米:
person$height_m <- person$height_cm / 100
person$bmi <- person$weight_kg / person$height_m^2
person$bmi <- round(person$bmi, 2)
person
## id height_cm weight_kg height_m bmi
## 1 1 160 50 1.60 19.53
## 2 2 172 70 1.72 23.66
## 3 3 168 65 1.68 23.03
## 4 4 180 85 1.80 26.23
## 5 5 155 48 1.55 19.98
## 6 6 175 90 1.75 29.39
这里用一个简单分类:
| BMI | 分类 |
|---|---|
| < 18.5 | 偏瘦 |
| 18.5 - 23.9 | 正常 |
| 24 - 27.9 | 超重 |
| >= 28 | 肥胖 |
person$bmi_group <- ifelse(
person$bmi < 18.5, "偏瘦",
ifelse(
person$bmi < 24, "正常",
ifelse(person$bmi < 28, "超重", "肥胖")
)
)
person
## id height_cm weight_kg height_m bmi bmi_group
## 1 1 160 50 1.60 19.53 正常
## 2 2 172 70 1.72 23.66 正常
## 3 3 168 65 1.68 23.03 正常
## 4 4 180 85 1.80 26.23 超重
## 5 5 155 48 1.55 19.98 正常
## 6 6 175 90 1.75 29.39 肥胖
用 case_when() 写会更清楚:
person2 <- person %>%
mutate(
bmi_group2 = case_when(
bmi < 18.5 ~ "偏瘦",
bmi < 24 ~ "正常",
bmi < 28 ~ "超重",
TRUE ~ "肥胖"
)
)
person2
## id height_cm weight_kg height_m bmi bmi_group bmi_group2
## 1 1 160 50 1.60 19.53 正常 正常
## 2 2 172 70 1.72 23.66 正常 正常
## 3 3 168 65 1.68 23.03 正常 正常
## 4 4 180 85 1.80 26.23 超重 超重
## 5 5 155 48 1.55 19.98 正常 正常
## 6 6 175 90 1.75 29.39 肥胖 肥胖
创建订单数据:
orders <- data.frame(
order_id = 1:6,
price = c(100, 200, 150, 300, 500, 80),
quantity = c(2, 1, 3, 1, 2, 5),
discount = c(0.9, 0.8, 1.0, 0.85, 0.75, 1.0)
)
orders
## order_id price quantity discount
## 1 1 100 2 0.90
## 2 2 200 1 0.80
## 3 3 150 3 1.00
## 4 4 300 1 0.85
## 5 5 500 2 0.75
## 6 6 80 5 1.00
计算原价金额:
orders$amount <- orders$price * orders$quantity
orders
## order_id price quantity discount amount
## 1 1 100 2 0.90 200
## 2 2 200 1 0.80 200
## 3 3 150 3 1.00 450
## 4 4 300 1 0.85 300
## 5 5 500 2 0.75 1000
## 6 6 80 5 1.00 400
计算折后金额:
orders$pay <- orders$amount * orders$discount
orders
## order_id price quantity discount amount pay
## 1 1 100 2 0.90 200 180
## 2 2 200 1 0.80 200 160
## 3 3 150 3 1.00 450 450
## 4 4 300 1 0.85 300 255
## 5 5 500 2 0.75 1000 750
## 6 6 80 5 1.00 400 400
保留两位小数:
orders$pay <- round(orders$pay, 2)
orders
## order_id price quantity discount amount pay
## 1 1 100 2 0.90 200 180
## 2 2 200 1 0.80 200 160
## 3 3 150 3 1.00 450 450
## 4 4 300 1 0.85 300 255
## 5 5 500 2 0.75 1000 750
## 6 6 80 5 1.00 400 400
orders2 <- orders %>%
mutate(
amount2 = price * quantity,
pay2 = round(amount2 * discount, 2)
)
orders2
## order_id price quantity discount amount pay amount2 pay2
## 1 1 100 2 0.90 200 180 200 180
## 2 2 200 1 0.80 200 160 200 160
## 3 3 150 3 1.00 450 450 450 450
## 4 4 300 1 0.85 300 255 300 255
## 5 5 500 2 0.75 1000 750 1000 750
## 6 6 80 5 1.00 400 400 400 400
下面有一个员工数据:
employee <- data.frame(
id = 1:8,
name = c("A", "B", "C", "D", "E", "F", "G", "H"),
base_salary = c(5000, 6000, 5500, 8000, 7500, 6200, 9000, 7000),
bonus = c(500, 800, 600, 1200, 1000, 700, 1500, 900),
tax_rate = c(0.05, 0.08, 0.06, 0.10, 0.09, 0.07, 0.12, 0.08)
)
employee
## id name base_salary bonus tax_rate
## 1 1 A 5000 500 0.05
## 2 2 B 6000 800 0.08
## 3 3 C 5500 600 0.06
## 4 4 D 8000 1200 0.10
## 5 5 E 7500 1000 0.09
## 6 6 F 6200 700 0.07
## 7 7 G 9000 1500 0.12
## 8 8 H 7000 900 0.08
要求:
gross_salary = base_salary + bonustax = gross_salary * tax_ratenet_salary = gross_salary - tax>= 8000:高>= 6000 且 < 8000:中< 6000:低employee$gross_salary <- employee$base_salary + employee$bonus
employee$tax <- employee$gross_salary * employee$tax_rate
employee$net_salary <- employee$gross_salary - employee$tax
employee$net_salary <- round(employee$net_salary, 2)
employee$salary_level <- ifelse(
employee$net_salary >= 8000, "高",
ifelse(employee$net_salary >= 6000, "中", "低")
)
employee
## id name base_salary bonus tax_rate gross_salary tax net_salary salary_level
## 1 1 A 5000 500 0.05 5500 275 5225 低
## 2 2 B 6000 800 0.08 6800 544 6256 中
## 3 3 C 5500 600 0.06 6100 366 5734 低
## 4 4 D 8000 1200 0.10 9200 920 8280 高
## 5 5 E 7500 1000 0.09 8500 765 7735 中
## 6 6 F 6200 700 0.07 6900 483 6417 中
## 7 7 G 9000 1500 0.12 10500 1260 9240 高
## 8 8 H 7000 900 0.08 7900 632 7268 中
employee2 <- employee %>%
mutate(
gross_salary2 = base_salary + bonus,
tax2 = gross_salary2 * tax_rate,
net_salary2 = round(gross_salary2 - tax2, 2),
salary_level2 = case_when(
net_salary2 >= 8000 ~ "高",
net_salary2 >= 6000 ~ "中",
TRUE ~ "低"
)
)
employee2
## id name base_salary bonus tax_rate gross_salary tax net_salary salary_level
## 1 1 A 5000 500 0.05 5500 275 5225 低
## 2 2 B 6000 800 0.08 6800 544 6256 中
## 3 3 C 5500 600 0.06 6100 366 5734 低
## 4 4 D 8000 1200 0.10 9200 920 8280 高
## 5 5 E 7500 1000 0.09 8500 765 7735 中
## 6 6 F 6200 700 0.07 6900 483 6417 中
## 7 7 G 9000 1500 0.12 10500 1260 9240 高
## 8 8 H 7000 900 0.08 7900 632 7268 中
## gross_salary2 tax2 net_salary2 salary_level2
## 1 5500 275 5225 低
## 2 6800 544 6256 中
## 3 6100 366 5734 低
## 4 9200 920 8280 高
## 5 8500 765 7735 中
## 6 6900 483 6417 中
## 7 10500 1260 9240 高
## 8 7900 632 7268 中
下面这段是模板代码,所以设置为
eval=FALSE,只展示,不运行。
# 基础R:新增变量
df$new_var <- df$var1 + df$var2
# 基础R:计算平均值
df$average <- (df$x1 + df$x2 + df$x3) / 3
# 保留小数
df$average <- round(df$average, 2)
# ifelse 二分类
df$pass <- ifelse(df$score >= 60, "及格", "不及格")
# ifelse 多分类
df$grade <- ifelse(
df$score >= 90, "A",
ifelse(df$score >= 80, "B",
ifelse(df$score >= 70, "C", "D"))
)
# rowSums 按行求和
df$total <- rowSums(df[, c("x1", "x2", "x3")], na.rm = TRUE)
# rowMeans 按行求平均
df$average <- rowMeans(df[, c("x1", "x2", "x3")], na.rm = TRUE)
# dplyr:mutate 新增变量
df2 <- df %>%
mutate(
new_var = var1 + var2,
average = round((x1 + x2 + x3) / 3, 2)
)
# dplyr:case_when 多条件分类
df2 <- df %>%
mutate(
grade = case_when(
score >= 90 ~ "A",
score >= 80 ~ "B",
score >= 70 ~ "C",
TRUE ~ "D"
)
)
计算变量最核心的意思是:
用已有变量生成新变量。
基础R常用:
df$new_var <- df$var1 + df$var2
dplyr 常用:
df %>%
mutate(new_var = var1 + var2)
如果要根据条件生成变量,常用:
ifelse()
或者:
case_when()