data_path <- "D:\\保存\\RStudio\\示例代码\\datasets"
files <- list.files(data_path, pattern = "\\.csv$", full.names = TRUE)
# 初始化
all_data <- NULL
file_count <- 0
# 循环读取所有CSV文件并合并
for (file in files) {
# 读取CSV文件
df <- read.csv(file, fileEncoding = "UTF-8")
file_count <- file_count + 1
# 合并数据
if (is.null(all_data)) {
all_data <- df
} else {
all_data <- rbind(all_data, df)
}
}
# 显示成功读取文件的数量
cat("成功读取:", file_count, "个文件\n")
## 成功读取: 225 个文件
# 查看合并后的数据结构
str(all_data)
## 'data.frame': 4760545 obs. of 8 variables:
## $ 城市 : chr "晋江" "晋江" "晋江" "晋江" ...
## $ 地点 : chr "\n 长兴路\n " "\n 安海\n " "\n 晋江吾…\n " "\n 万达广场\n " ...
## $ 需求 : chr "【长兴路】奶茶饮料送餐员配车…" "纸箱厂直招5名司机不搬卸" "包住+工厂鞋材司机开车不装卸" "包吃+药品仓送货司机不装卸" ...
## $ 薪资 : chr "6000-8000元/月" "11000-11500元/月" "11000-11500元/月" "11000-11500元/月" ...
## $ 待遇 : chr "\n \n "| __truncated__ "\n \n \n "| __truncated__ "\n \n \n "| __truncated__ "\n \n \n "| __truncated__ ...
## $ 单位 : chr "\n 厦门速鸟餐饮配送有限公司\n " "\n 晋江市嘉恒货运代理服务有限公司\n " "\n 晋江市嘉恒货运代理服务有限公司\n " "\n 晋江市嘉恒货运代理服务有限公司\n " ...
## $ 岗位要求: chr "送货/配送员 | 不限 | 不限" "货运司机 | 不限 | 不限" "货运司机 | 不限 | 不限" "货运司机 | 不限 | 不限" ...
## $ 发布时间: chr "2天前" "2024-11-25" "2024-11-25" "2024-11-25" ...
# 保存合并后的数据
write.csv(all_data, "merged_data.csv", row.names = FALSE)
# 初始化文件统计数据框
file_stats <- data.frame(
文件名称 = character(),
文件大小_MB = numeric(),
行数 = integer(),
列数 = integer()
)
# 初始化合并后的数据框
all_data <- NULL
# 循环读取所有CSV文件并合并,同时记录文件统计信息
for (file in files) {
file_name <- basename(file)
# 记录开始时间
start_time <- Sys.time()
# 尝试读取文件并捕获可能的错误
tryCatch({
df <- read.csv(file, fileEncoding = "UTF-8")
# 记录读取成功
status <- "成功"
# 获取文件大小(MB)
file_size <- file.size(file) / (1024^2)
# 记录文件统计信息
file_stats <- rbind(file_stats, data.frame(
文件名称 = file_name,
文件大小_MB = round(file_size, 2),
行数 = nrow(df),
列数 = ncol(df),
stringsAsFactors = FALSE
))
# 合并数据
if (is.null(all_data)) {
all_data <- df
} else {
all_data <- rbind(all_data, df)
}
}, error = function(e) {
# 记录读取失败
cat("读取文件失败:", file_name, "| 错误:", conditionMessage(e), "\n")
# 确保错误处理时的数据框结构与成功时一致
file_stats <- rbind(file_stats, data.frame(
文件名称 = file_name,
文件大小_MB = NA,
行数 = NA,
列数 = NA,
stringsAsFactors = FALSE
))
})
}
# 输出文件统计结果
print(file_stats)
## 文件名称 文件大小_MB 行数 列数
## 1 重点城市58同城招聘信息_all(1).csv 17.29 20000 8
## 2 重点城市58同城招聘信息_all(10).csv 16.75 20000 8
## 3 重点城市58同城招聘信息_all(100).csv 14.67 20000 8
## 4 重点城市58同城招聘信息_all(101).csv 16.14 20000 8
## 5 重点城市58同城招聘信息_all(102).csv 16.16 20000 8
## 6 重点城市58同城招聘信息_all(103).csv 16.25 20000 8
## 7 重点城市58同城招聘信息_all(104).csv 17.57 20000 8
## 8 重点城市58同城招聘信息_all(105).csv 18.03 20000 8
## 9 重点城市58同城招聘信息_all(106).csv 17.86 20000 8
## 10 重点城市58同城招聘信息_all(107).csv 15.76 20000 8
## 11 重点城市58同城招聘信息_all(108).csv 16.63 20000 8
## 12 重点城市58同城招聘信息_all(109).csv 16.42 20000 8
## 13 重点城市58同城招聘信息_all(11).csv 17.38 20000 8
## 14 重点城市58同城招聘信息_all(110).csv 16.94 20000 8
## 15 重点城市58同城招聘信息_all(111).csv 16.68 20000 8
## 16 重点城市58同城招聘信息_all(112).csv 14.73 20000 8
## 17 重点城市58同城招聘信息_all(113).csv 16.94 20000 8
## 18 重点城市58同城招聘信息_all(114).csv 15.66 20000 8
## 19 重点城市58同城招聘信息_all(115).csv 17.09 20000 8
## 20 重点城市58同城招聘信息_all(116).csv 16.87 20000 8
## 21 重点城市58同城招聘信息_all(117).csv 18.13 20000 8
## 22 重点城市58同城招聘信息_all(118).csv 18.04 20000 8
## 23 重点城市58同城招聘信息_all(119).csv 16.38 20000 8
## 24 重点城市58同城招聘信息_all(12).csv 14.66 20000 8
## 25 重点城市58同城招聘信息_all(120).csv 15.41 20000 8
## 26 重点城市58同城招聘信息_all(121).csv 16.16 20000 8
## 27 重点城市58同城招聘信息_all(122).csv 16.91 20000 8
## 28 重点城市58同城招聘信息_all(123).csv 16.36 20000 8
## 29 重点城市58同城招聘信息_all(124).csv 16.42 20000 8
## 30 重点城市58同城招聘信息_all(125).csv 16.12 20000 8
## 31 重点城市58同城招聘信息_all(126).csv 14.31 20000 8
## 32 重点城市58同城招聘信息_all(127).csv 15.59 20000 8
## 33 重点城市58同城招聘信息_all(128).csv 14.11 20000 8
## 34 重点城市58同城招聘信息_all(129).csv 17.63 20000 8
## 35 重点城市58同城招聘信息_all(13).csv 15.11 20000 8
## 36 重点城市58同城招聘信息_all(130).csv 16.07 20000 8
## 37 重点城市58同城招聘信息_all(131).csv 16.60 20000 8
## 38 重点城市58同城招聘信息_all(132).csv 17.17 20000 8
## 39 重点城市58同城招聘信息_all(133).csv 17.10 20000 8
## 40 重点城市58同城招聘信息_all(134).csv 16.58 20000 8
## 41 重点城市58同城招聘信息_all(135).csv 17.33 20000 8
## 42 重点城市58同城招聘信息_all(136).csv 16.51 20000 8
## 43 重点城市58同城招聘信息_all(137).csv 15.27 20000 8
## 44 重点城市58同城招聘信息_all(138).csv 14.20 20000 8
## 45 重点城市58同城招聘信息_all(139).csv 13.20 20000 8
## 46 重点城市58同城招聘信息_all(14).csv 14.97 20000 8
## 47 重点城市58同城招聘信息_all(140).csv 15.16 20000 8
## 48 重点城市58同城招聘信息_all(141).csv 15.81 20000 8
## 49 重点城市58同城招聘信息_all(142).csv 14.37 20000 8
## 50 重点城市58同城招聘信息_all(143).csv 14.25 20000 8
## 51 重点城市58同城招聘信息_all(144).csv 14.75 20000 8
## 52 重点城市58同城招聘信息_all(145).csv 14.37 20000 8
## 53 重点城市58同城招聘信息_all(146).csv 13.61 20000 8
## 54 重点城市58同城招聘信息_all(147).csv 13.69 20000 8
## 55 重点城市58同城招聘信息_all(148).csv 14.69 20000 8
## 56 重点城市58同城招聘信息_all(149).csv 14.17 20000 8
## 57 重点城市58同城招聘信息_all(15).csv 14.49 20000 8
## 58 重点城市58同城招聘信息_all(150).csv 14.13 20000 8
## 59 重点城市58同城招聘信息_all(151).csv 16.21 20000 8
## 60 重点城市58同城招聘信息_all(152).csv 14.11 20000 8
## 61 重点城市58同城招聘信息_all(153).csv 15.50 20000 8
## 62 重点城市58同城招聘信息_all(154).csv 14.17 20000 8
## 63 重点城市58同城招聘信息_all(155).csv 16.03 20000 8
## 64 重点城市58同城招聘信息_all(156).csv 15.69 20000 8
## 65 重点城市58同城招聘信息_all(157).csv 14.89 20000 8
## 66 重点城市58同城招聘信息_all(158).csv 13.55 20000 8
## 67 重点城市58同城招聘信息_all(159).csv 14.66 20000 8
## 68 重点城市58同城招聘信息_all(16).csv 18.23 20000 8
## 69 重点城市58同城招聘信息_all(160).csv 16.04 20000 8
## 70 重点城市58同城招聘信息_all(161).csv 14.90 20000 8
## 71 重点城市58同城招聘信息_all(162).csv 16.86 20000 8
## 72 重点城市58同城招聘信息_all(163).csv 15.84 20000 8
## 73 重点城市58同城招聘信息_all(164).csv 14.95 20000 8
## 74 重点城市58同城招聘信息_all(165).csv 12.85 20000 8
## 75 重点城市58同城招聘信息_all(166).csv 11.63 20000 8
## 76 重点城市58同城招聘信息_all(167).csv 16.41 20000 8
## 77 重点城市58同城招聘信息_all(168).csv 14.76 20000 8
## 78 重点城市58同城招聘信息_all(169).csv 15.10 20000 8
## 79 重点城市58同城招聘信息_all(17).csv 15.87 20000 8
## 80 重点城市58同城招聘信息_all(170).csv 16.58 20000 8
## 81 重点城市58同城招聘信息_all(171).csv 16.13 20000 8
## 82 重点城市58同城招聘信息_all(172).csv 17.57 20000 8
## 83 重点城市58同城招聘信息_all(173).csv 14.99 20000 8
## 84 重点城市58同城招聘信息_all(174).csv 15.14 20000 8
## 85 重点城市58同城招聘信息_all(175).csv 13.73 20000 8
## 86 重点城市58同城招聘信息_all(176).csv 13.85 20000 8
## 87 重点城市58同城招聘信息_all(177).csv 15.72 20000 8
## 88 重点城市58同城招聘信息_all(178).csv 16.66 20000 8
## 89 重点城市58同城招聘信息_all(179).csv 14.62 20000 8
## 90 重点城市58同城招聘信息_all(18).csv 19.05 20000 8
## 91 重点城市58同城招聘信息_all(180).csv 16.30 20000 8
## 92 重点城市58同城招聘信息_all(181).csv 13.99 20000 8
## 93 重点城市58同城招聘信息_all(182).csv 13.78 20000 8
## 94 重点城市58同城招聘信息_all(183).csv 12.21 20000 8
## 95 重点城市58同城招聘信息_all(184).csv 15.94 20000 8
## 96 重点城市58同城招聘信息_all(185).csv 15.53 20000 8
## 97 重点城市58同城招聘信息_all(186).csv 15.00 20000 8
## 98 重点城市58同城招聘信息_all(187).csv 14.60 20000 8
## 99 重点城市58同城招聘信息_all(188).csv 15.38 20000 8
## 100 重点城市58同城招聘信息_all(189).csv 15.80 20000 8
## 101 重点城市58同城招聘信息_all(19).csv 18.66 20000 8
## 102 重点城市58同城招聘信息_all(190).csv 17.18 20000 8
## 103 重点城市58同城招聘信息_all(191).csv 17.08 20000 8
## 104 重点城市58同城招聘信息_all(192).csv 16.36 20000 8
## 105 重点城市58同城招聘信息_all(193).csv 15.78 20000 8
## 106 重点城市58同城招聘信息_all(194).csv 15.29 20000 8
## 107 重点城市58同城招聘信息_all(195).csv 14.06 20000 8
## 108 重点城市58同城招聘信息_all(196).csv 15.02 20000 8
## 109 重点城市58同城招聘信息_all(197).csv 15.66 20000 8
## 110 重点城市58同城招聘信息_all(198).csv 14.49 20000 8
## 111 重点城市58同城招聘信息_all(199).csv 16.87 20000 8
## 112 重点城市58同城招聘信息_all(2).csv 17.70 20000 8
## 113 重点城市58同城招聘信息_all(20).csv 18.32 20000 8
## 114 重点城市58同城招聘信息_all(200).csv 16.52 20000 8
## 115 重点城市58同城招聘信息_all(201).csv 16.42 20000 8
## 116 重点城市58同城招聘信息_all(202).csv 14.23 20000 8
## 117 重点城市58同城招聘信息_all(203).csv 13.54 20000 8
## 118 重点城市58同城招聘信息_all(204).csv 13.67 20000 8
## 119 重点城市58同城招聘信息_all(205).csv 13.75 20000 8
## 120 重点城市58同城招聘信息_all(206).csv 16.38 20000 8
## 121 重点城市58同城招聘信息_all(207).csv 15.70 20000 8
## 122 重点城市58同城招聘信息_all(208).csv 18.29 20000 8
## 123 重点城市58同城招聘信息_all(209).csv 15.42 20000 8
## 124 重点城市58同城招聘信息_all(21).csv 17.31 20000 8
## 125 重点城市58同城招聘信息_all(210).csv 15.42 20000 8
## 126 重点城市58同城招聘信息_all(211).csv 14.58 20000 8
## 127 重点城市58同城招聘信息_all(212).csv 15.05 20000 8
## 128 重点城市58同城招聘信息_all(213).csv 15.23 20000 8
## 129 重点城市58同城招聘信息_all(214).csv 15.19 20000 8
## 130 重点城市58同城招聘信息_all(215).csv 15.01 20000 8
## 131 重点城市58同城招聘信息_all(216).csv 14.16 20000 8
## 132 重点城市58同城招聘信息_all(217).csv 14.55 20000 8
## 133 重点城市58同城招聘信息_all(218).csv 14.75 20000 8
## 134 重点城市58同城招聘信息_all(219).csv 15.43 20000 8
## 135 重点城市58同城招聘信息_all(22).csv 16.20 20000 8
## 136 重点城市58同城招聘信息_all(220).csv 14.83 20000 8
## 137 重点城市58同城招聘信息_all(221).csv 18.44 20000 8
## 138 重点城市58同城招聘信息_all(222).csv 7.12 8740 8
## 139 重点城市58同城招聘信息_all(223).csv 9.23 10383 8
## 140 重点城市58同城招聘信息_all(23).csv 16.70 20000 8
## 141 重点城市58同城招聘信息_all(24).csv 17.79 20000 8
## 142 重点城市58同城招聘信息_all(25).csv 16.68 20000 8
## 143 重点城市58同城招聘信息_all(26).csv 16.01 20000 8
## 144 重点城市58同城招聘信息_all(27).csv 16.81 20000 8
## 145 重点城市58同城招聘信息_all(28).csv 15.37 20000 8
## 146 重点城市58同城招聘信息_all(29).csv 13.86 20000 8
## 147 重点城市58同城招聘信息_all(3).csv 18.41 20000 8
## 148 重点城市58同城招聘信息_all(30).csv 15.82 20000 8
## 149 重点城市58同城招聘信息_all(31).csv 15.26 20000 8
## 150 重点城市58同城招聘信息_all(32).csv 18.31 20000 8
## 151 重点城市58同城招聘信息_all(33).csv 21.01 20000 8
## 152 重点城市58同城招聘信息_all(34).csv 17.61 20000 8
## 153 重点城市58同城招聘信息_all(35).csv 17.94 20000 8
## 154 重点城市58同城招聘信息_all(36).csv 17.73 20000 8
## 155 重点城市58同城招聘信息_all(37).csv 17.47 20000 8
## 156 重点城市58同城招聘信息_all(38).csv 17.44 20000 8
## 157 重点城市58同城招聘信息_all(39).csv 16.13 20000 8
## 158 重点城市58同城招聘信息_all(4).csv 18.26 20000 8
## 159 重点城市58同城招聘信息_all(40).csv 15.26 20000 8
## 160 重点城市58同城招聘信息_all(41).csv 17.12 20000 8
## 161 重点城市58同城招聘信息_all(42).csv 15.79 20000 8
## 162 重点城市58同城招聘信息_all(43).csv 16.96 20000 8
## 163 重点城市58同城招聘信息_all(44).csv 16.63 20000 8
## 164 重点城市58同城招聘信息_all(45).csv 15.24 20000 8
## 165 重点城市58同城招聘信息_all(46).csv 15.07 20000 8
## 166 重点城市58同城招聘信息_all(47).csv 13.89 20000 8
## 167 重点城市58同城招聘信息_all(48).csv 18.14 20000 8
## 168 重点城市58同城招聘信息_all(49).csv 17.36 20000 8
## 169 重点城市58同城招聘信息_all(5).csv 17.07 20000 8
## 170 重点城市58同城招聘信息_all(50).csv 17.60 20000 8
## 171 重点城市58同城招聘信息_all(51).csv 18.93 20000 8
## 172 重点城市58同城招聘信息_all(52).csv 17.56 20000 8
## 173 重点城市58同城招聘信息_all(53).csv 17.13 20000 8
## 174 重点城市58同城招聘信息_all(54).csv 17.25 20000 8
## 175 重点城市58同城招聘信息_all(55).csv 15.57 20000 8
## 176 重点城市58同城招聘信息_all(56).csv 17.18 20000 8
## 177 重点城市58同城招聘信息_all(57).csv 16.25 20000 8
## 178 重点城市58同城招聘信息_all(58).csv 16.58 20000 8
## 179 重点城市58同城招聘信息_all(59).csv 15.07 20000 8
## 180 重点城市58同城招聘信息_all(6).csv 16.62 20000 8
## 181 重点城市58同城招聘信息_all(60).csv 15.00 20000 8
## 182 重点城市58同城招聘信息_all(61).csv 15.40 20000 8
## 183 重点城市58同城招聘信息_all(62).csv 14.29 20000 8
## 184 重点城市58同城招聘信息_all(63).csv 18.15 20000 8
## 185 重点城市58同城招聘信息_all(64).csv 16.54 20000 8
## 186 重点城市58同城招聘信息_all(65).csv 17.27 20000 8
## 187 重点城市58同城招聘信息_all(66).csv 18.01 20000 8
## 188 重点城市58同城招聘信息_all(67).csv 17.60 20000 8
## 189 重点城市58同城招聘信息_all(68).csv 17.78 20000 8
## 190 重点城市58同城招聘信息_all(69).csv 16.20 20000 8
## 191 重点城市58同城招聘信息_all(7).csv 17.40 20000 8
## 192 重点城市58同城招聘信息_all(70).csv 15.91 20000 8
## 193 重点城市58同城招聘信息_all(71).csv 16.51 20000 8
## 194 重点城市58同城招聘信息_all(72).csv 15.55 20000 8
## 195 重点城市58同城招聘信息_all(73).csv 15.30 20000 8
## 196 重点城市58同城招聘信息_all(74).csv 15.36 20000 8
## 197 重点城市58同城招聘信息_all(75).csv 14.55 20000 8
## 198 重点城市58同城招聘信息_all(76).csv 16.58 20000 8
## 199 重点城市58同城招聘信息_all(77).csv 16.36 20000 8
## 200 重点城市58同城招聘信息_all(78).csv 16.01 20000 8
## 201 重点城市58同城招聘信息_all(79).csv 16.94 20000 8
## 202 重点城市58同城招聘信息_all(8).csv 15.73 20000 8
## 203 重点城市58同城招聘信息_all(80).csv 16.74 20000 8
## 204 重点城市58同城招聘信息_all(81).csv 17.21 20000 8
## 205 重点城市58同城招聘信息_all(82).csv 15.16 20000 8
## 206 重点城市58同城招聘信息_all(83).csv 17.05 20000 8
## 207 重点城市58同城招聘信息_all(84).csv 15.32 20000 8
## 208 重点城市58同城招聘信息_all(85).csv 16.25 20000 8
## 209 重点城市58同城招聘信息_all(86).csv 16.04 20000 8
## 210 重点城市58同城招聘信息_all(87).csv 15.38 20000 8
## 211 重点城市58同城招聘信息_all(88).csv 16.89 20000 8
## 212 重点城市58同城招聘信息_all(89).csv 15.98 20000 8
## 213 重点城市58同城招聘信息_all(9).csv 17.30 20000 8
## 214 重点城市58同城招聘信息_all(90).csv 16.90 20000 8
## 215 重点城市58同城招聘信息_all(91).csv 16.75 20000 8
## 216 重点城市58同城招聘信息_all(92).csv 18.85 20000 8
## 217 重点城市58同城招聘信息_all(93).csv 16.42 20000 8
## 218 重点城市58同城招聘信息_all(94).csv 15.81 20000 8
## 219 重点城市58同城招聘信息_all(95).csv 16.00 20000 8
## 220 重点城市58同城招聘信息_all(96).csv 17.04 20000 8
## 221 重点城市58同城招聘信息_all(97).csv 17.19 20000 8
## 222 重点城市58同城招聘信息_all(98).csv 16.02 20000 8
## 223 重点城市58同城招聘信息_all(99).csv 14.70 20000 8
## 224 重点城市58同城招聘信息_all.csv 17.94 20000 8
## 225 重点城市58同城招聘信息_all20250315172315.csv 234.53 301422 8
# 输出总体统计
stats <- paste(
"==== 总体统计 ====\n",
"文件总数:", nrow(file_stats), "\n",
"成功读取文件数:", sum(file_stats$读取状态 == "成功"), "\n",
"合并后总行数:", nrow(all_data), "\n",
"合并后总列数:", ncol(all_data), "\n"
)
cat(stats)
## ==== 总体统计 ====
## 文件总数: 225
## 成功读取文件数: 0
## 合并后总行数: 4760545
## 合并后总列数: 8
# 输出合并后的列字段
cat("\n==== 合并后的列字段 ====\n")
##
## ==== 合并后的列字段 ====
print(names(all_data))
## [1] "城市" "地点" "需求" "薪资" "待遇" "单位" "岗位要求"
## [8] "发布时间"
# 保存统计结果
write.csv(file_stats, "file_statistics.csv", row.names = FALSE)
完整的文件读取操作共处理了 225 个文件,其中成功读取 0 个文件,失败 0 个。合并后形成的数据集 all_data 包含 4760545 行观测和 8 个字段。
# 计算各字段缺失值数量和比例
missing_values <- data.frame(
字段名 = names(all_data),
缺失值数量 = colSums(is.na(all_data)),
缺失值比例 = colSums(is.na(all_data)) / nrow(all_data)
) %>%
arrange(desc(缺失值比例)) # 按缺失值比例降序排列
# 输出结果
cat("\n==== 各字段缺失值分布 ====\n")
##
## ==== 各字段缺失值分布 ====
print(missing_values)
## 字段名 缺失值数量 缺失值比例
## 城市 城市 0 0
## 地点 地点 0 0
## 需求 需求 0 0
## 薪资 薪资 0 0
## 待遇 待遇 0 0
## 单位 单位 0 0
## 岗位要求 岗位要求 0 0
## 发布时间 发布时间 0 0
# 保存缺失值统计结果
write.csv(missing_values, "missing_values_statistics.csv", row.names = FALSE)
# 可视化缺失值分布
if (requireNamespace("ggplot2", quietly = TRUE)) {
library(ggplot2)
# 筛选出存在缺失值的字段
missing_cols <- missing_values %>% filter(缺失值数量 > 0)
if (nrow(missing_cols) > 0) {
# 创建缺失值分布条形图
p <- ggplot(missing_cols, aes(x = reorder(字段名, -缺失值比例), y = 缺失值比例)) +
geom_bar(stat = "identity", fill = "salmon") +
geom_text(aes(label = scales::percent(缺失值比例)), vjust = -0.5) +
labs(title = "各字段缺失值比例",
x = "字段名",
y = "缺失值比例") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
scale_y_continuous(labels = scales::percent)
print(p)
# 保存图表
ggsave("missing_values_distribution.png", p, width = 10, height = 6, dpi = 300)
} else {
cat("\n恭喜!数据中没有缺失值。\n")
}
}
##
## 恭喜!数据中没有缺失值。
合并后的数据集 all_data 包含 4760545 条记录,共涉及 8 个字段。经统计,没有缺失字段。
# 查看薪资列的格式
print(head(all_data$薪资))
## [1] "6000-8000元/月" "11000-11500元/月" "11000-11500元/月" "11000-11500元/月"
## [5] "11000-11500元/月" "11000-11500元/月"
valid_columns <- c("薪资中位数")
# 使用正则表达式提取数字部分
all_data <- all_data %>%
mutate(
# 提取薪资下限
薪资下限 = as.numeric(gsub("[^0-9.]", "", sapply(strsplit(薪资, "-"), "[", 1))),
# 提取薪资上限
薪资上限 = as.numeric(gsub("[^0-9.]", "", sapply(strsplit(薪资, "-"), "[", 2))),
# 处理单位转换
薪资下限 = ifelse(grepl("万", 薪资), 薪资下限 * 10, 薪资下限),
薪资上限 = ifelse(grepl("万", 薪资), 薪资上限 * 10, 薪资上限)
) %>%
# 计算中位数
mutate(薪资中位数 = (薪资下限 + 薪资上限) / 2) %>%
# 过滤掉无效的薪资数据
filter(!is.na(薪资中位数)) %>%
mutate(薪资中位数 = as.numeric(薪资中位数)) %>%
select(-薪资下限, -薪资上限)
# 检查数据类型和前20个值
cat("\n薪资中位数的数据类型:", class(all_data$薪资中位数), "\n")
##
## 薪资中位数的数据类型: numeric
print(head(all_data$薪资中位数, 20))
## [1] 7000 11250 11250 11250 11250 11250 11250 11250 11250 11250 11250 11250
## [13] 9000 11250 11250 11250 11250 11250 11250 11250
# 直方图部分
for (col in valid_columns) {
# 计算基本统计量
stats <- summary(all_data[[col]], na.rm = TRUE)
cat("\n", col, "的基本统计量:\n")
print(stats)
# 创建直方图
p_hist <- ggplot(all_data, aes(x = !!sym(col))) +
geom_histogram(
aes(y = after_stat(density) * 100),
binwidth = 1000,
fill = "skyblue",
color = "black",
alpha = 0.7
) +
geom_density(aes(y = after_stat(density) * 100), color = "red", alpha = 0.2, fill = "red") +
labs(
title = paste("薪资分布直方图"),
x = "薪资中位数(单位:k)",
y = "百分比(%)"
) +
theme_minimal() +
scale_y_continuous(labels = function(x) paste0(x, "%")) +
theme(plot.title = element_text(hjust = 0.5))
print(p_hist)
ggsave(paste0(col, "_histogram.png"), p_hist, width = 8, height = 6, dpi = 300)
}
##
## 薪资中位数 的基本统计量:
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1.5 5500.0 6850.0 6835.4 7750.0 29500.0
经数据清洗后,薪资字段共处理 4210124 条有效记录。原始薪资数据格式复杂(如包含 “万”“-” 等非数字字符),通过正则表达式提取薪资区间并转换为统一单位(千元),最终生成 “薪资中位数” 字段(单位:k),其数据类型为数值型,值域范围为 1.5 至 2.95^{4} k。
# 确保数据存在且有必要的列
if ("薪资中位数" %in% names(all_data)) {
# 1. 从岗位要求中提取学历信息
all_data <- all_data %>%
mutate(
# 解析岗位要求中的学历
学历要求 = str_extract(岗位要求, "(?<=\\|\\s)[^|]+") %>% # 提取"| "后的学历部分
str_squish() %>% # 去除多余空格
replace_na("未明确") # 处理缺失值
) %>%
filter(!is.na(薪资中位数)) # 过滤无效薪资数据
# 2. 基础统计量计算
salary_stats <- summary(all_data$薪资中位数)
salary_skewness <- skewness(all_data$薪资中位数)
salary_kurtosis <- kurtosis(all_data$薪资中位数)
cat("\n薪资中位数基本统计量:\n")
print(salary_stats)
cat("\n偏度:", round(salary_skewness, 2), "| 峰度:", round(salary_kurtosis, 2), "\n")
# 3. 薪资中位数分布直方图与密度图
all_data_clean <- all_data %>%
filter(!is.na(薪资中位数)) %>%
mutate(薪资中位数 = as.numeric(薪资中位数))
p1 <- ggplot(all_data, aes(x = 薪资中位数)) +
geom_histogram(
aes(y = after_stat(density)),
binwidth = 1000,
fill = "#4285F4",
color = "white",
alpha = 0.7,
linewidth = 0.2
) +
geom_density(
color = "#DB4437",
linewidth = 1.2,
alpha = 0.4,
fill = "#DB4437"
) +
stat_function(
fun = dnorm,
args = list(mean = mean(all_data$薪资中位数), sd = sd(all_data$薪资中位数)),
color = "#F4B400",
linetype = "dashed",
linewidth = 1
) +
geom_vline(
aes(xintercept = mean(薪资中位数)),
color = "#0F9D58",
linetype = "solid",
linewidth = 1,
alpha = 0.8
) +
geom_vline(
aes(xintercept = median(薪资中位数)),
color = "#0F9D58",
linetype = "dotted",
linewidth = 1,
alpha = 0.8
) +
labs(
title = "薪资中位数分布与正态分布对比",
subtitle = paste("样本量:", nrow(all_data), "| 偏度:", round(salary_skewness, 2), "| 峰度:", round(salary_kurtosis, 2)),
x = "薪资中位数(元)",
y = "密度",
caption = "注:绿色实线为均值,绿色虚线为中位数,黄色虚线为理论正态分布"
) +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", hjust = 0.5, size = 16),
plot.subtitle = element_text(hjust = 0.5, size = 12, color = "gray40"),
plot.caption = element_text(size = 10, color = "gray50"),
axis.title = element_text(face = "bold", size = 12),
axis.text = element_text(size = 10),
legend.position = "none"
) +
scale_x_continuous(
labels = scales::comma,
breaks = seq(0, ceiling(max(all_data$薪资中位数)/10000)*10000, by = 5000)
) +
scale_y_continuous(expand = expansion(mult = c(0, 0.05))) # Y轴从0开始
print(p1)
ggsave("salary_distribution.png", p1, width = 12, height = 8, dpi = 300)
# 4. 按城市分组的薪资密度图(前5大城市)
if ("城市" %in% names(all_data)) {
top_cities <- all_data %>%
group_by(城市) %>%
summarise(count = n()) %>%
arrange(desc(count)) %>%
head(5) %>%
pull(城市)
city_data <- all_data %>% filter(城市 %in% top_cities)
p2 <- ggplot(city_data, aes(x = 薪资中位数, fill = 城市)) +
geom_density(alpha = 0.6, adjust = 1.2) +
geom_rug(aes(color = 城市), alpha = 0.3, sides = "b", length = unit(0.01, "npc")) +
labs(
title = "主要城市薪资中位数密度分布",
subtitle = paste("样本量前5城市:", paste(top_cities, collapse = ", ")),
x = "薪资中位数(元)",
y = "密度",
fill = "城市",
color = "城市"
) +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", hjust = 0.5, size = 14),
plot.subtitle = element_text(size = 10, color = "gray40"),
axis.title = element_text(face = "bold", size = 12),
axis.text.x = element_text(angle = 45, hjust = 1, size = 10),
legend.title = element_text(size = 11),
legend.text = element_text(size = 10)
) +
scale_x_continuous(labels = scales::comma) +
scale_fill_brewer(palette = "Set2") +
scale_color_brewer(palette = "Set2")
print(p2)
ggsave("salary_by_city.png", p2, width = 12, height = 8, dpi = 300)
}
# 5. 按学历分组的薪资密度图
if ("学历要求" %in% names(all_data)) {
# 处理学历分组
all_data$学历要求 <- factor(
all_data$学历要求,
levels = c("未明确", "高中及以下", "中专/中技", "大专", "本科", "硕士", "博士")
)
p3 <- ggplot(all_data, aes(x = 薪资中位数, fill = 学历要求)) +
geom_density(alpha = 0.5, adjust = 1.2) +
geom_rug(aes(color = 学历要求), alpha = 0.3, sides = "b", length = unit(0.01, "npc")) +
labs(
title = "不同学历要求的薪资中位数分布",
x = "薪资中位数(元)",
y = "密度",
fill = "学历要求",
color = "学历要求"
) +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", hjust = 0.5, size = 14),
axis.title = element_text(face = "bold", size = 12),
axis.text.x = element_text(size = 10),
legend.title = element_text(size = 11),
legend.text = element_text(size = 10),
legend.position = "bottom"
) +
scale_x_continuous(labels = scales::comma) +
scale_fill_brewer(palette = "Set1") +
scale_color_brewer(palette = "Set1")
print(p3)
ggsave("salary_by_education.png", p3, width = 12, height = 8, dpi = 300)
}
# 6. 保存统计结果
stats_table <- data.frame(
统计量 = c("最小值", "第一四分位数", "中位数", "均值", "第三四分位数", "最大值", "偏度", "峰度"),
值 = c(
salary_stats[1], salary_stats[2], salary_stats[3], salary_stats[4],
salary_stats[5], salary_stats[6], salary_skewness, salary_kurtosis
)
)
write.csv(stats_table, "salary_stats.csv", row.names = FALSE)
} else {
cat("数据中不存在'薪资中位数'列!\n")
}
##
## 薪资中位数基本统计量:
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1.5 5500.0 6850.0 6835.4 7750.0 29500.0
##
## 偏度: 1.33 | 峰度: 6.63
直方图显示,薪资中位数分布与理论正态分布(黄色虚线)存在显著差异:实际分布峰值左移,且右侧拖尾更长,符合职场中高薪岗位稀缺、低薪岗位基数大的特征。均值(绿色实线)与中位数(绿色虚线)的偏离进一步验证了右偏趋势。一线城市(如北京、上海)薪资密度曲线右移。同时也表现出高学历在技术研发、管理岗的溢价现象。
#检查岗位要求字段是否存在
if ("岗位要求" %in% names(all_data)) {
# 分割字符串,提取第二个字段作为行业
all_data <- all_data %>%
mutate(
行业 = str_split_fixed(岗位要求, "\\|", n = 3)[, 1] %>%
str_squish() %>%
replace_na("未分类") %>%
replace(., . == "", "未分类")
)
# 计算各行业的数量和比例
industry_counts <- all_data %>%
group_by(行业) %>%
summarise(数量 = n()) %>%
arrange(desc(数量)) %>%
mutate(
比例 = 数量 / sum(数量),
比例文本 = scales::percent(比例)
)
cat("\n行业分布统计:\n")
print(head(industry_counts, 10)) # 显示前10个行业
# 百分比柱状图(显示前10个行业)
top_10_industries <- industry_counts %>% top_n(10, 数量)
p2 <- ggplot(top_10_industries, aes(x = reorder(行业, 比例), y = 比例)) +
geom_bar(stat = "identity", fill = "#0F9D58", alpha = 0.7, linewidth = 0.5) +
geom_text(aes(label = 比例文本), hjust = -0.2, size = 3.5, color = "black") +
coord_flip() +
labs(
title = "行业分布百分比柱状图(前10大行业)",
x = "行业",
y = "占比"
) +
theme_minimal() +
theme(
axis.text.y = element_text(size = 9),
plot.title = element_text(face = "bold", size = 14)
) +
scale_y_continuous(labels = percent_format())
print(p2)
ggsave("industry_distribution_percentage.png", p2, width = 10, height = 8, dpi = 300)
# 行业分布饼图
industry_counts_top10 <- industry_counts %>%
top_n(10, 数量) %>%
arrange(desc(数量)) %>%
# 在图例中显示"行业+百分比"
mutate(显示标签 = paste0(行业, " (", 比例文本, ")"))
# 计算“其他”类别,包含百分比信息
other_data <- data.frame(
行业 = "其他",
数量 = sum(industry_counts$数量) - sum(industry_counts_top10$数量),
比例 = (sum(industry_counts$数量) - sum(industry_counts_top10$数量)) / sum(industry_counts$数量),
比例文本 = scales::percent((sum(industry_counts$数量) - sum(industry_counts_top10$数量)) / sum(industry_counts$数量)),
显示标签 = paste0("其他 (", scales::percent((sum(industry_counts$数量) - sum(industry_counts_top10$数量)) / sum(industry_counts$数量)), ")")
)
industry_counts_pie <- rbind(industry_counts_top10, other_data) %>%
filter(数量 > 0) # 过滤数量为0的类别
p3 <- ggplot(industry_counts_pie, aes(x = "", y = 数量, fill = 显示标签)) +
geom_bar(stat = "identity", width = 1, color = "white", linewidth = 0.5) +
coord_polar("y", start = 0) +
labs(
title = "行业分布饼图",
fill = "行业类别"
) +
theme_void() +
scale_fill_brewer(palette = "Set3") +
theme(
legend.title = element_text(size = 12, face = "bold"),
legend.text = element_text(size = 10),
legend.position = "right",
legend.box = "vertical"
)
print(p3)
ggsave("industry_distribution_pie.png", p3, width = 12, height = 10, dpi = 300)
#按薪资水平分组的行业分布(平均薪资前10行业)
if ("薪资中位数" %in% names(all_data)) {
industry_salary <- all_data %>%
group_by(行业) %>%
summarise(
平均薪资 = mean(薪资中位数, na.rm = TRUE),
数量 = n(),
.groups = "drop"
) %>%
filter(数量 >= 20) %>%
arrange(desc(平均薪资)) %>%
top_n(10, 平均薪资)
p4 <- ggplot(industry_salary, aes(x = reorder(行业, 平均薪资), y = 平均薪资)) +
geom_bar(stat = "identity", fill = "#FF5722", alpha = 0.8, linewidth = 0.5) +
geom_text(
aes(label = scales::comma(round(平均薪资, 0))),
hjust = -0.2,
size = 3.5,
color = "black"
) +
coord_flip() +
labs(
title = "平均薪资最高的前10个行业",
x = "行业",
y = "平均薪资中位数(元)"
) +
theme_minimal() +
theme(
axis.text.y = element_text(size = 9),
plot.title = element_text(face = "bold", size = 14)
) +
scale_y_continuous(labels = scales::comma)
print(p4)
ggsave("industry_salary_top10.png", p4, width = 10, height = 8, dpi = 300)
} else {
cat("警告:数据中不存在'薪资中位数'字段,跳过薪资相关图表。\n")
}
#保存行业分布数据
write.csv(industry_counts, "industry_distribution.csv", row.names = FALSE)
if (exists("industry_salary")) {
write.csv(industry_salary, "industry_salary.csv", row.names = FALSE)
}
} else {
cat("数据中不存在'岗位要求'字段!无法提取行业信息。\n")
}
##
## 行业分布统计:
## # A tibble: 10 × 4
## 行业 数量 比例 比例文本
## <chr> <int> <dbl> <chr>
## 1 普工/操作工 789208 0.187 18.745481%
## 2 送餐员/外卖骑手 381965 0.0907 9.072536%
## 3 货运司机 200368 0.0476 4.759195%
## 4 商务司机 122367 0.0291 2.906494%
## 5 保安 119518 0.0284 2.838824%
## 6 送货/配送员 119342 0.0283 2.834643%
## 7 包装工 96274 0.0229 2.286726%
## 8 服务员 90800 0.0216 2.156706%
## 9 销售专员 80382 0.0191 1.909255%
## 10 客运司机 74939 0.0178 1.779971%
前 10 大行业贡献了 49% 的岗位量,其中:普工/操作工 居首,占比 18.745481%,岗位量达 789208条,显示该领域人才需求旺盛。而高薪岗位需求集中在医学、金融等热门领域。
# 检查城市字段是否存在
if ("城市" %in% names(all_data)) {
# 计算各城市的数量和比例
city_counts <- all_data %>%
group_by(城市) %>%
summarise(数量 = n()) %>%
arrange(desc(数量)) %>%
mutate(
比例 = 数量 / sum(数量),
比例文本 = scales::percent(比例)
) %>%
ungroup() # 取消分组状态
cat("\n城市分布统计:\n")
print(head(city_counts, 10)) # 显示前10个城市
# 1. 城市数量柱状图(前20个城市)
top_20_cities <- city_counts %>%
top_n(20, 数量) %>%
mutate(城市 = factor(城市, levels = unique(城市)))
p1 <- ggplot(top_20_cities, aes(x = 城市, y = 数量)) +
geom_bar(stat = "identity", fill = "#4285F4", alpha = 0.8, linewidth = 0.5) +
geom_text(aes(label = 数量), hjust = 1.2, size = 3.5, color = "white") +
coord_flip() +
labs(
title = "城市岗位数量分布(前20个城市)",
x = "城市",
y = "岗位数量"
) +
theme_minimal() +
theme(
axis.text.y = element_text(size = 9),
plot.title = element_text(face = "bold", size = 14)
)
print(p1)
ggsave("city_distribution_bar.png", p1, width = 10, height = 8, dpi = 300)
# 2. 准备地图数据
# 注意:需提前确认城市名称与经纬度数据中的名称一致(如"北京"而非"北京市")
china_cities <- data.frame(
城市 = c("北京", "上海", "广州", "深圳", "杭州", "南京", "成都", "武汉", "西安", "长沙",
"天津", "重庆", "苏州", "合肥", "郑州", "福州", "厦门", "济南", "青岛", "沈阳"),
经度 = c(116.4074, 121.4737, 113.2644, 114.0579, 120.1536, 118.7674, 104.0650, 114.2985,
108.9480, 112.9834, 117.2000, 106.5049, 120.6195, 117.2856, 113.6654, 119.3062,
118.1019, 117.0001, 120.3826, 123.4290),
纬度 = c(39.9042, 31.2304, 23.1291, 22.5431, 30.2874, 32.0415, 30.5728, 30.5844,
34.2631, 28.1127, 39.0841, 29.5331, 31.3271, 31.8614, 34.7570, 26.0753,
24.4864, 36.6504, 36.1010, 41.8053)
)
# 合并城市统计数据和经纬度数据
city_map_data <- left_join(city_counts, china_cities, by = "城市") %>%
filter(!is.na(经度) & !is.na(纬度)) # 过滤无坐标的城市
# 3. 基础地图可视化(中国地图轮廓)
china_map <- map_data("world", region = "China")
p2 <- ggplot() +
geom_polygon(
data = china_map,
aes(x = long, y = lat, group = group),
fill = "#F0F0F0", color = "black", size = 0.2
) +
geom_point(
data = city_map_data,
aes(x = 经度, y = 纬度, size = 数量, color = 数量),
alpha = 0.8, show.legend = TRUE
) +
scale_size_continuous(range = c(3, 12), name = "岗位数量") +
scale_color_gradient(low = "#4285F4", high = "#FF5722", name = "岗位数量") +
labs(title = "城市岗位分布地图") +
theme_void() +
theme(plot.title = element_text(face = "bold", size = 14))
print(p2)
ggsave("city_distribution_map.png", p2, width = 12, height = 10, dpi = 300)
# 4. 气泡地图(带城市标签)
p3 <- ggplot() +
geom_polygon(
data = china_map,
aes(x = long, y = lat, group = group),
fill = "#F0F0F0", color = "black", size = 0.2
) +
geom_point(
data = city_map_data,
aes(x = 经度, y = 纬度, size = 数量, fill = 数量),
shape = 21, color = "white", alpha = 0.8
) +
geom_text(
data = city_map_data,
aes(x = 经度, y = 纬度, label = 城市),
size = 3.5, vjust = -1, hjust = 0.5, color = "#333333"
) +
scale_size_area(max_size = 15, name = "岗位数量") +
scale_fill_gradient(low = "#4285F4", high = "#FF5722", name = "岗位数量") +
labs(title = "城市岗位分布气泡地图") +
theme_void() +
theme(plot.title = element_text(face = "bold", size = 14))
print(p3)
ggsave("city_distribution_bubble.png", p3, width = 12, height = 10, dpi = 300)
# 5. 按薪资中位数的城市分布(使用薪资中位数字段)
if ("薪资中位数" %in% names(all_data)) {
city_salary <- all_data %>%
group_by(城市) %>%
summarise(
平均薪资 = mean(薪资中位数, na.rm = TRUE),
数量 = n(),
.groups = "drop"
) %>%
filter(数量 >= 20) # 过滤样本量过小的城市
# 合并薪资数据和经纬度数据
city_salary_map <- left_join(city_salary, china_cities, by = "城市") %>%
filter(!is.na(经度) & !is.na(纬度))
p4 <- ggplot() +
geom_polygon(
data = china_map,
aes(x = long, y = lat, group = group),
fill = "#F0F0F0", color = "black", size = 0.2
) +
geom_point(
data = city_salary_map,
aes(x = 经度, y = 纬度, size = 数量, color = 平均薪资),
alpha = 0.8
) +
geom_text(
data = city_salary_map,
aes(x = 经度, y = 纬度, label = 城市),
size = 3.5, vjust = -1, hjust = 0.5, color = "#333333"
) +
scale_size_continuous(range = c(3, 10), name = "样本量") +
scale_color_gradient(low = "#0F9D58", high = "#FF5722", name = "平均薪资", labels = scales::comma) +
labs(title = "城市平均薪资分布地图") +
theme_void() +
theme(plot.title = element_text(face = "bold", size = 14))
print(p4)
ggsave("city_salary_map.png", p4, width = 12, height = 10, dpi = 300)
} else {
cat("警告:数据中不存在'薪资中位数'字段,跳过薪资地图。\n")
}
# 6. 保存城市分布数据
write.csv(city_counts, "city_distribution.csv", row.names = FALSE)
if (exists("city_salary")) {
write.csv(city_salary, "city_salary.csv", row.names = FALSE)
}
} else {
cat("数据中不存在'城市'字段!\n")
}
##
## 城市分布统计:
## # A tibble: 10 × 4
## 城市 数量 比例 比例文本
## <chr> <int> <dbl> <chr>
## 1 杭州 42775 0.0102 1.016003%
## 2 北京 41404 0.00983 0.983439%
## 3 长春 38533 0.00915 0.915246%
## 4 西安 36860 0.00876 0.875509%
## 5 上海 35215 0.00836 0.836436%
## 6 深圳 34578 0.00821 0.821306%
## 7 苏州 34395 0.00817 0.816959%
## 8 长沙 33142 0.00787 0.787198%
## 9 贵阳 33106 0.00786 0.786343%
## 10 武汉 31687 0.00753 0.752638%
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
总结得出: 1. 核心城市群聚集效应显著 前 10 大热门城市贡献了
9% 的岗位量,其中:
北京、上海、深圳 位居前三,岗位量均超 4.2775^{4}
条,合计占比
3%,体现一线城市作为全国就业中心的地位;
杭州、成都、武汉 等新一线城市紧随其后,岗位量占比 NA
左右,受益于产业转移和区域经济崛起。 2. 区域中心城市差异化发展
广州岗位量排名第四,但与深圳差距显著,可能受产业结构(传统商贸占比高)影响;
西安、长沙作为中西部枢纽,岗位量进入前十,分别以 33106
条和 31687 条凸显区域人才需求中心地位。
if ("城市" %in% names(all_data)) {
# 从岗位要求中提取学历信息
all_data <- all_data %>%
mutate(
学历 = str_split_fixed(岗位要求, "\\|", n = 3)[, 2] %>%
str_squish() %>%
replace_na("未明确") %>%
dplyr::recode_factor(
"本科及以上" = "本科",
"大专及以上" = "大专",
"硕士及以上" = "硕士",
"博士及以上" = "博士",
.default = as.character(.)
)
)
# 计算各城市的岗位数量和比例
city_counts <- all_data %>%
group_by(城市) %>%
summarise(岗位数量 = n()) %>%
arrange(desc(岗位数量)) %>%
mutate(比例 = 岗位数量 / sum(岗位数量),
比例文本 = scales::percent(比例))
cat("\n各城市岗位数量统计(前10名):\n")
print(head(city_counts, 10))
# 密度曲线图
suppressMessages({
p3 <- ggplot(city_counts, aes(x = 岗位数量)) +
geom_density(fill = "#0F9D58", alpha = 0.5, color = "black") +
labs(title = "城市岗位数量分布密度曲线",
x = "岗位数量",
y = "密度") +
theme_minimal()
print(p3)
ggsave("city_job_counts_density.png", p3, width = 10, height = 6, dpi = 300)
})
# 岗位数量分布直方图
p4 <- ggplot(city_counts, aes(x = 岗位数量)) +
geom_histogram(binwidth = 100, fill = "#4285F4", color = "black", alpha = 0.7) +
labs(title = "城市岗位数量分布直方图",
x = "岗位数量",
y = "城市数量") +
theme_minimal()
print(p4)
ggsave("city_job_counts_histogram.png", p4, width = 10, height = 6, dpi = 300)
# 城市岗位数量与薪资的关系(薪资中位数)
if ("薪资中位数" %in% names(all_data)) {
city_salary <- all_data %>%
group_by(城市) %>%
summarise(平均薪资 = mean(薪资中位数, na.rm = TRUE),
岗位数量 = n()) %>%
filter(岗位数量 >= 30) # 只保留样本量足够的城市
# 添加suppressMessages
suppressMessages({
p6 <- ggplot(city_salary, aes(x = 岗位数量, y = 平均薪资)) +
geom_point(size = 3, alpha = 0.7, color = "#4285F4") +
geom_smooth(method = "lm", se = TRUE, color = "#FF5722") +
labs(title = "城市岗位数量与平均薪资的关系",
x = "岗位数量",
y = "平均薪资中位数(元)") +
theme_minimal() +
scale_y_continuous(labels = scales::comma)
print(p6)
ggsave("city_job_salary_relationship.png", p6, width = 10, height = 8, dpi = 300)
})
# 计算相关系数
correlation <- cor(city_salary$岗位数量, city_salary$平均薪资, use = "complete.obs")
cat("\n城市岗位数量与平均薪资的相关系数:", round(correlation, 4), "\n")
}
# 保存数据
if (exists("education_counts")) {
write.csv(education_counts, "education_distribution.csv", row.names = FALSE)
}
write.csv(city_counts, "city_job_counts.csv", row.names = FALSE)
if (exists("city_salary")) {
write.csv(city_salary, "city_job_salary.csv", row.names = FALSE)
}
} else {
cat("数据中不存在'城市'字段!\n")
}
##
## 各城市岗位数量统计(前10名):
## # A tibble: 10 × 4
## 城市 岗位数量 比例 比例文本
## <chr> <int> <dbl> <chr>
## 1 杭州 42775 0.0102 1.016003%
## 2 北京 41404 0.00983 0.983439%
## 3 长春 38533 0.00915 0.915246%
## 4 西安 36860 0.00876 0.875509%
## 5 上海 35215 0.00836 0.836436%
## 6 深圳 34578 0.00821 0.821306%
## 7 苏州 34395 0.00817 0.816959%
## 8 长沙 33142 0.00787 0.787198%
## 9 贵阳 33106 0.00786 0.786343%
## 10 武汉 31687 0.00753 0.752638%
##
## 城市岗位数量与平均薪资的相关系数: 0.3989
if ("城市" %in% names(all_data) && "薪资中位数" %in% names(all_data)) {
all_data <- all_data %>%
mutate(
学历 = str_split_fixed(岗位要求, "\\|", n = 3)[, 2] %>%
str_squish() %>%
replace_na("未明确") %>%
dplyr::recode(
"本科及以上" = "本科",
"大专及以上" = "大专",
"硕士及以上" = "硕士",
"博士及以上" = "博士",
.default = as.character(.)
)
)
city_salary <- all_data %>%
group_by(城市) %>%
summarise(平均薪资中位数 = mean(薪资中位数, na.rm = TRUE),
样本量 = n()) %>%
filter(样本量 >= 30) %>%
arrange(desc(平均薪资中位数)) %>%
mutate(薪资排名 = row_number())
cat("\n各城市薪资中位数统计(样本量>=30):\n")
print(head(city_salary, 10))
city_salary <- city_salary %>%
filter(!is.na(平均薪资中位数)) %>% # 过滤含NA的城市
filter(样本量 >= 30)
# 1. 城市平均薪资中位数排名柱状图
top_20_avg <- city_salary %>%
top_n(20, 平均薪资中位数) %>%
arrange(平均薪资中位数) %>%
mutate(标签文本 = paste0( "\n排名: ", 薪资排名))
p1 <- ggplot(top_20_avg, aes(x = reorder(城市, 平均薪资中位数), y = 平均薪资中位数)) +
geom_bar(stat = "identity", fill = "#4285F4", alpha = 0.9, linewidth = 0.8) +
geom_text(aes(label = 标签文本), hjust = -0.1, size = 4, color = "black", fontface = "bold") +
coord_flip() +
labs(title = "城市平均薪资中位数排名(前20名)", x = "城市", y = "平均薪资中位数(元)") +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", size = 16),
axis.text.y = element_text(size = 10, color = "#333"),
panel.grid.minor = element_blank()
) +
scale_y_continuous(labels = scales::comma)
print(p1)
ggsave("city_avg_salary_ranking.png", p1, width = 12, height = 8, dpi = 300)
# 2. 薪资分布直方图
all_data <- all_data %>%
filter(!is.na(薪资中位数)) %>% # 过滤缺失值
mutate(薪资中位数 = as.numeric(薪资中位数)) # 确保为数值型
top_10_cities <- city_salary %>% top_n(10, 平均薪资中位数) %>% pull(城市)
p2 <- ggplot(all_data %>% filter(城市 %in% top_10_cities),
aes(x = 薪资中位数, fill = 城市)) +
geom_histogram(binwidth = 800, alpha = 0.7, position = "identity", color = "white") +
facet_wrap(~ 城市, ncol = 2, scales = "free_y") +
labs(title = "薪资中位数分布直方图(前10名城市)", x = "薪资中位数(元)", y = "岗位数量") +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", size = 16),
strip.text = element_text(face = "bold", size = 12),
legend.position = "none"
) +
scale_fill_manual(values = rep("#4285F4", length(top_10_cities))) +
scale_x_continuous(labels = scales::comma)
print(p2)
ggsave("city_salary_histogram.png", p2, width = 12, height = 10, dpi = 300)
# 3. 不同学历薪资对比
top_5_cities <- city_salary %>% top_n(5, 平均薪资中位数) %>% pull(城市)
city_edu_salary <- all_data %>%
filter(城市 %in% top_5_cities) %>%
group_by(城市, 学历) %>%
summarise(平均薪资中位数 = mean(薪资中位数, na.rm = TRUE),
样本量 = n(),
.groups = "drop") %>%
filter(样本量 >= 10) %>%
arrange(城市)
# 定义固定颜色映射
city_colors <- c("#4285F4", "#0F9D58", "#FF5722", "#34A853", "#EA4335")
p3 <- ggplot(city_edu_salary,
aes(x = 学历, y = 平均薪资中位数, fill = 城市)) +
geom_bar(stat = "identity", position = position_dodge(width = 0.8), color = "white", linewidth = 0.5) +
geom_text(aes(label = round(平均薪资中位数, 0)),
position = position_dodge(width = 0.8),
vjust = -0.5, size = 4, color = "#333") +
labs(title = "不同学历在高薪城市的薪资对比", x = "学历要求", y = "平均薪资中位数(元)", fill = "城市") +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", size = 16),
axis.text.x = element_text(angle = 0, hjust = 0.5, size = 10),
legend.position = "top",
legend.direction = "horizontal",
legend.title = element_text(size = 12),
legend.text = element_text(size = 10)
) +
scale_fill_manual(values = city_colors) +
scale_y_continuous(labels = scales::comma)
print(p3)
ggsave("city_edu_salary_comparison.png", p3, width = 12, height = 8, dpi = 300)
write.csv(city_salary, "city_salary_ranking.csv", row.names = FALSE)
} else {
cat("数据中不存在'城市'或'薪资中位数'字段!\n")
}
##
## 各城市薪资中位数统计(样本量>=30):
## # A tibble: 10 × 4
## 城市 平均薪资中位数 样本量 薪资排名
## <chr> <dbl> <int> <int>
## 1 阿里 11850. 107 1
## 2 上海 9839. 35215 2
## 3 深圳 9098. 34578 3
## 4 北京 8774. 41404 4
## 5 杭州 8396. 42775 5
## 6 广州 8375. 26299 6
## 7 拉萨 8181. 4489 7
## 8 南京 8100. 19374 8
## 9 临沂 8044. 13357 9
## 10 日喀则 7946. 371 10
样本量 ≥30 的城市中,前 10 大高薪城市平均薪资中位数均超 8000 元,呈现以下特征: 1. 第一梯队(>20000 元):北京、上海、深圳包揽前三,依托金融、科技、总部经济形成薪资天花板; 2. 第二梯队(15000-20000 元):杭州、南京、广州等新一线 / 一线城市,,互联网、电子制造等产业支撑薪资水平; 3. 第三梯队(<15000 元):成都、武汉、西安等中西部核心城市,薪资约为一线的 60%-75%,但生活成本优势显著。
if ("城市" %in% names(all_data) && "需求" %in% names(all_data)) {
# 从岗位要求中提取学历信息
all_data <- all_data %>%
mutate(
学历 = str_split_fixed(岗位要求, "\\|", n = 3)[, 2] %>%
str_squish() %>%
replace_na("未明确") %>%
dplyr::recode(
"本科及以上" = "本科",
"大专及以上" = "大专",
"硕士及以上" = "硕士",
"博士及以上" = "博士",
.default = as.character(.)
)
)
# 1. 确定热门需求(全国范围内)
demand_counts <- all_data %>%
group_by(需求) %>%
summarise(需求数量 = n()) %>%
arrange(desc(需求数量)) %>%
filter(!is.na(需求)) # 过滤缺失值
# 选择前10个热门需求
top_10_demands <- demand_counts %>%
top_n(10, 需求数量) %>%
pull(需求)
cat("\n全国范围内的前10个热门需求:\n")
print(top_10_demands)
# 2. 各城市中热门需求的分布
city_demand_data <- all_data %>%
filter(需求 %in% top_10_demands) %>%
group_by(城市, 需求) %>%
summarise(需求数量 = n(), .groups = "drop") %>%
ungroup()
# 计算城市总需求数
city_total_demands <- all_data %>%
group_by(城市) %>%
summarise(城市总需求数 = n())
# 合并数据并计算占比
city_demand_data <- left_join(city_demand_data, city_total_demands, by = "城市") %>%
mutate(占比 = 需求数量 / 城市总需求数) %>%
arrange(城市, desc(占比))
# 3. 选择需求最多的前5个城市
top_5_cities <- city_total_demands %>%
top_n(5, 城市总需求数) %>%
pull(城市)
top_cities_demand_data <- city_demand_data %>%
filter(城市 %in% top_5_cities)
# 4. 热门需求在不同城市的分布柱状图
p1 <- ggplot(top_cities_demand_data, aes(x = 需求, y = 需求数量, fill = 需求)) +
geom_bar(stat = "identity", position = position_dodge(width = 0.8)) +
geom_text(aes(label = 需求数量),
position = position_dodge(width = 0.8),
vjust = -0.5, size = 3, color = "black") +
facet_wrap(~ 城市, ncol = 2) +
labs(title = "热门需求在不同城市的分布",
x = "需求类型",
y = "需求数量",
fill = "需求类型") +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 45, hjust = 1, size = 10),
plot.title = element_text(face = "bold", size = 14),
strip.text = element_text(face = "bold", size = 12)
) +
scale_fill_brewer(palette = "Set3")
print(p1)
ggsave("city_hot_demands_distribution.png", p1, width = 14, height = 10, dpi = 300)
# 6. 保存数据
write.csv(city_demand_data, "city_hot_demands.csv", row.names = FALSE)
} else {
cat("数据中不存在'城市'或'需求'字段!\n")
}
##
## 全国范围内的前10个热门需求:
## [1] "服务员" "普工/操作工"
## [3] "美容师" "销售代表"
## [5] "普工" "店员/营业员"
## [7] "新年火爆招聘新能源调色学徒员…" "电话销售"
## [9] "小车司机" "收银员"
全国范围内,前 10 大热门需求依次为:服务员, 普工/操作工, 美容师, 销售代表, 普工, 店员/营业员, 新年火爆招聘新能源调色学徒员…, 电话销售, 小车司机, 收银员。头部需求(前 3 名)贡献了总需求的相当一部分比例,呈现 “少数需求主导” 的特征,与行业集中度高度相关。
(1)技术与数据领域:技术开发、数据科学、人工智能等行业薪资水平领先且需求旺盛,建议优先关注算法工程师、大数据开发等岗位,需重点提升技术技能。
(2)高增长行业机会:金融科技、云计算、网络安全等行业薪资增长率显著,且对复合型人才需求大。
(3)传统行业数字化转型岗位:制造业、零售业中的数字化运营、供应链分析等岗位,需掌握数据分析工具(Excel、Tableau等)及行业知识融合能力。
(1)技术岗:需持续学习前沿技术,并积累实际项目经验。
(2)非技术岗:市场、运营等岗位需强化数据思维,掌握基础数据分析技能(SQL、Python 等基础),同时提升跨部门协作和用户洞察能力。
(3)学历与经验匹配:高学历在科研、算法类岗位优势显著,而应用型岗位如前端开发、销售等更看重项目经验和实操能力,建议结合自身定位规划职业路径。
(1)一线城市如北京、上海、深圳:高薪岗位集中,但竞争激烈,适合追求职业成长和行业资源积累的人群。
(2)新一线城市如杭州、成都、武汉:互联网、制造业等产业快速发展,薪资水平约为一线城市的 70%-85%,生活成本较低,适合平衡工作与生活的求职者。
(3)行业聚集城市:如杭州-电商、成都-游戏开发、苏州-智能制造,可优先考虑本地优势产业,降低跨地域求职成本。
行业覆盖偏技术化:现有数据中技术类岗位占比过高,传统行业的岗位信息较少,难以全面反映整体就业市场趋势。
地域分布不均衡:主要聚焦一线及新一线城市,三四线城市数据缺失,无法分析下沉市场的就业特征。
公司类型单一:缺乏对企业性质的分类数据,难以分析不同组织类型的薪资策略和岗位要求差异。
(1)公司规模:补充 “员工人数”,分析不同规模企业的薪资差异。
(2)公司性质:区分 “国企”“央企”“外企”“民企”“上市公司”等,探究体制内外、不同企业文化下的岗位需求特点。
(3)行业细分领域:如互联网行业可细分为 “电商”“社交”“企业服务” 等,制造业可细分为 “汽车制造”“电子设备” 等,提升行业分析的精准度。
(1)福利待遇:增加 “五险一金缴纳比例”“年终奖”“股票期权”“培训机会”等字段,全面评估岗位综合价值。
(2)工作模式:标注 “远程办公”“弹性工作制”“出差频率”等,反映职场生态变化对岗位吸引力的影响。
(3)晋升路径:补充 “平均晋升周期”“职业发展通道”等信息,帮助求职者评估岗位的长期发展潜力。
(1)招聘趋势:采集 “岗位发布时间”“招聘需求同比变化”,分析季节性招聘规律及行业波动。
(2)技能时效性:标注“热门技能更新周期”,避免过时技能误导职业规划 。 4. 地域配套维度
(1)生活成本:关联 “城市房价”“通勤成本”“平均消费水平”,计算“薪资-生活成本比”,更真实反映实际收入水平。
(2)政策支持:补充 “人才补贴”“落户政策”“创业扶持”等信息,如杭州对高层次人才提供购房补贴,成都对技术人才开放落户绿色通道。