简介面向R语言初学者与数据分析入门者的描述性统计分析指南聚焦如何快速把握数据集中心趋势、离散程度与分布形状覆盖从基础函数到分组分析与可视化的完整技能链条。资源包共1个PDF文件约98KB以图文与代码示例结合的形式组织内容便于边读边在R环境中复现。已有553人学习下载具备一定的参考热度。内容围绕summary()、sapply()与自定义函数展开并延伸至Hmisc、psych包的describe()函数讲解偏度、峰度等统计量的计算同时介绍aggregate()、by()及doBy包summaryBy()的分组分析用法配合ggplot2绘制直方图与箱线图直观呈现数据分布。读者可据此掌握一套可复用的描述性统计流程为后续数据挖掘与建模打下基础。1. 拿到一份 R 描述性统计笔记先别急着跑summary()很多人第一次在 R 里做描述性统计习惯性敲一行summary(df)就以为完事了。结果把结果贴给同事对方一句“偏度多少、峰度多少、分组之后均值差多少”就把人问住了——summary()只给四分位数和均值形状指标和分组对比全得自己补。这份《R语言中描述性统计分析的全面指南》解决的正是这个断层它把从summary()、sapply()到自定义偏度峰度函数再到Hmisc、psych两个包的describe()以及aggregate()、by()、summaryBy()分组分析和ggplot2可视化串成一条完整链路。适合刚接手数据清洗、要做探索性分析EDA的从业者也适合带新人时当一份可复现的代码底稿。下面我按“先立住原理、再动手复现、最后说坑”的顺序拆一遍代码都能直接抄。2. 基础函数与自定义统计量从summary()到偏度峰度2.1 为什么summary()不够得先想清楚要哪些统计量描述性统计的核心就三块中心趋势均值、中位数、离散程度方差、标准差、四分位距、分布形状偏度、峰度。summary()一次给你最小值、下四分位、中位数、均值、上四分位、最大值对数值型变量够用对因子和逻辑型给频数。但它不给标准差、不给偏度峰度也不给样本量之外的离散信息。所以选型逻辑是先用summary()做一轮粗筛看有没有明显异常和缺失再用sapply()批量补标准差、方差、极差最后用自定义函数补形状指标。这个顺序别反反了容易在没看清缺失值的情况下算出带NA的均值后面全错。原料里用的mtcars内置数据集很适合练手三个变量mpg、hp、wt分别代表油耗、马力、车重量纲不同正好检验批量函数的通用性。# 加载内置数据集选三个数值变量做示范 data(mtcars) myvars - c(mpg, hp, wt) # 第一轮粗筛看分布范围和缺失情况 summary(mtcars[myvars])这段代码的逻辑很直白myvars是一个字符向量用列名索引子集避免把整张表所有列都塞进summary()导致输出刷屏。参数上唯一要注意的是mtcars本身没有缺失值真实数据里summary()会把NA数量单独列出来那一列NAs就是你要不要走na.omit分支的判断依据。2.2 用sapply()批量算离散指标别一个个手敲sapply()的作用是对列表或向量每个元素应用函数并简化返回结果。放到数据框上它会对每一列分别调用你给的函数返回一个命名向量或矩阵。这比写三行mean(mtcars$mpg)、mean(mtcars$hp)优雅得多变量一多优势更明显。# 对每个变量批量计算均值、标准差、方差、中位数 sapply(mtcars[myvars], mean) sapply(mtcars[myvars], sd) sapply(mtcars[myvars], var) sapply(mtcars[myvars], median) # 一次拿多个分位数注意 quantile 返回的是命名向量 sapply(mtcars[myvars], quantile, probs c(0.25, 0.5, 0.75))逻辑说明sapply第二个参数是函数对象第三个及以后是传给该函数的额外参数。quantile的probs参数就是典型例子不传默认给 0、25%、50%、75%、100% 五个分位。参数上要留意sd、var默认na.rm FALSE数据里有缺失就会返回NA这时得写成sapply(df, sd, na.rm TRUE)。这是新手最常翻车的地方之一后面避坑章节还会展开。2.3 自定义函数补偏度和峰度公式别抄错偏度衡量分布不对称方向峰度衡量尾部厚度。原料给的自定义函数用的是总体矩公式除以n而非n-1这点要心里有数它算的是总体偏度峰度样本量小时和e1071包的结果会有细微差异。函数里na.omit参数是个开关为TRUE时先剔除缺失再算避免NA污染整个结果。# 自定义描述性统计函数样本量、均值、标准差、偏度、峰度 mystats - function(x, na.omit FALSE) { if (na.omit) x - x[!is.na(x)] # 按需剔除缺失值 m - mean(x) n - length(x) s - sd(x) skew - sum((x - m)^3 / s^3) / n # 三阶标准矩 kurt - sum((x - m)^4 / s^4) / n - 3 # 四阶标准矩减 3 return(c(n n, mean m, stdev s, skew skew, kurtosis kurt)) } # 应用到三个变量返回矩阵每列一个变量 sapply(mtcars[myvars], mystats)逻辑说明return里用命名向量sapply会自动把它拼成“行是统计量、列是变量”的矩阵读起来一目了然。参数上na.omit默认FALSE是有意为之——先让你看到NA的存在而不是默默吞掉。峰度减 3 是为了让正态分布的峰度为 0这是超额峰度的标准做法不减 3 的话正态分布峰度是 3跟很多软件输出对不上汇报时容易闹误会。2.4Hmisc与psych的describe()一行出全表但别混用两个包都有describe()输出字段不同。Hmisc::describe()给样本量、缺失数、唯一值数、均值、分位数、标准差还会顺带画个迷你分布图psych::describe()给n、mean、sd、median、min、max、range、skew、kurtosis、se字段更贴近论文表格需求。选型上要往报告里贴表就用psych要快速扫一眼每列分布形态就用Hmisc。# Hmisc 版本信息偏“体检报告” install.packages(Hmisc) library(Hmisc) describe(mtcars[myvars]) # psych 版本字段规整适合直接进表格 install.packages(psych) library(psych) describe(mtcars[myvars])逻辑说明两个包函数同名加载顺序决定谁覆盖谁所以实际项目里我一般用Hmisc::describe()和psych::describe()显式加命名空间避免“到底调的哪个”这种黑匣子问题。参数上psych::describe()有fast TRUE选项大数据集下跳过一些耗时计算几万行以上建议打开。3. 分组描述性统计aggregate()、by()与summaryBy()怎么选3.1aggregate()公式接口单指标最快多指标要绕分组统计的现实需求是“按某个分类变量切开每组各算一遍”。aggregate()用公式接口y ~ group一行搞定单指标。它的优点是返回规整数据框缺点是每次只能算一个函数想同时要均值和标准差得调多次或换写法。# 按气缸数 cyl 分组算 mpg 均值 aggregate(mpg ~ cyl, data mtcars, mean) # 想一次算多个指标用 cbind 把多个变量放左边 aggregate(cbind(mpg, hp, wt) ~ cyl, data mtcars, mean)逻辑说明公式左边cbind(...)表示对多个因变量同时聚合右边是分组变量。参数上data指定数据框FUN位置传函数。注意aggregate默认会丢掉分组变量含NA的行如果分组列有缺失结果会少组这点在真实业务数据里很隐蔽得先用table(df$group, useNA ifany)确认一下。3.2by()分组套自定义函数灵活但返回列表by()的思路是把数据框按分组变量切成若干子集对每个子集调用函数。它适合把第 2 章那个mystats直接套上去一次拿到每组的全套统计量。代价是返回一个by类列表要转数据框得再加工。# 定义对单组数据框应用的函数对每列跑 mystats dstats - function(x) sapply(x, mystats) # 按 cyl 分组对 mpg/hp/wt 三列分别算全套统计量 by(mtcars[myvars], mtcars$cyl, dstats)逻辑说明by的第一个参数是数据第二个是分组因子第三个是函数。这里dstats内部又调了sapply形成“分组 → 逐列 → 逐统计量”的三层结构。参数上要注意by不自动简化结果输出是列表每组一个矩阵。想转成扁平表常见做法是do.call(rbind, ...)再补一列组名或者干脆换summaryBy。3.3doBy::summaryBy()多指标多函数一步到位summaryBy()是这三个里最贴近“出报表”需求的公式左边写多个变量FUN可以传一个函数或函数列表返回直接是数据框。原料里传的是mystats一次拿到每组每变量的五个统计量。# 安装并加载 doBy install.packages(doBy) library(doBy) # 按 cyl 分组对三个变量应用 mystats summaryBy(mpg hp wt ~ cyl, data mtcars, FUN mystats)逻辑说明公式左边用连接多个变量FUN传自定义函数时函数返回值最好是命名向量这样列名才可读。参数上summaryBy还有keep.names、na.rm等选项na.rm TRUE会在调用FUN前剔除缺失。三个函数的选择建议只要一个均值就用aggregate要套复杂自定义逻辑用by要直接出多指标报表用summaryBy。别为了统一全用by转列表那步会多写不少胶水代码。4. 可视化验证ggplot2直方图与箱线图不是装饰4.1 直方图看形状binwidth是第一个要调的参数数值统计量告诉你偏度是 0.6但你看不到它是左偏还是右偏、有没有双峰。直方图就是补这个信息。ggplot2的geom_histogram()里binwidth控制组距设太大看不出形状设太小全是毛刺。# 安装并加载 ggplot2 install.packages(ggplot2) library(ggplot2) # mpg 的直方图组距设为 1 ggplot(mtcars, aes(x mpg)) geom_histogram(binwidth 1, fill blue, color black)逻辑说明aes(x mpg)映射变量binwidth 1是组距fill和color分别控制填充色和边框色。参数上如果数据量差异大固定binwidth可能让某组柱子过密常见做法是先summary(mtcars$mpg)看极差再用极差除以 20 到 30 估一个初始组距。别用默认的 30 个 bins 直接出图量纲一变图就失真。4.2 箱线图看分组差异和异常值和分组统计量互相印证箱线图把中位数、四分位距、异常值一次画出来配合第 3 章的分组均值看能发现“均值被极端值拉高”这类问题。factor(cyl)是把数值型分组变量转成因子否则ggplot2会把它当连续变量处理箱线图就画不出来。# 按 cyl 分组的 mpg 箱线图 ggplot(mtcars, aes(x factor(cyl), y mpg)) geom_boxplot()逻辑说明x factor(cyl)是关键y mpg是观测值。箱体上下边是 25% 和 75% 分位中间粗线是中位数须线外的点就是按 1.5 倍四分位距判定的异常值。参数上想改异常值判定倍数用coef默认 1.5。实际排查时我一般把箱线图和psych::describe()的median、mad对照看如果箱线图显示某组中位数明显偏离均值说明该组分布偏斜汇报时就不能只报均值。5. 避坑与排查五个真实会翻车的点5.1 现象sapply(df, sd)返回一堆NA原因缺失值未处理解决显式传na.rm真实数据几乎不可能没有缺失。sd、var、mean默认na.rm FALSE只要该列有一个NA整个结果就是NA。很多人以为是函数坏了其实是参数没给。解决方式是sapply(df, sd, na.rm TRUE)或者在进sapply前统一df - na.omit(df)。但后者会整行删除样本量掉得厉害我一般按列处理只对参与计算的列剔除。5.2 现象自定义mystats算出的峰度和psych对不上原因总体矩与样本矩公式差异解决统一口径再汇报原料里的mystats除以npsych::describe()的峰度用的是样本调整公式小样本下两者能差零点几。这不是谁算错了是口径不同。解决方式是在报告里注明“采用总体矩公式”或者干脆统一用psych的输出别两个来源混着贴。血泪经验同一张表里混用两种口径审稿人一眼就能看出来。5.3 现象aggregate()结果少了几组原因分组变量含NA被默认丢弃解决先查table(..., useNA ifany)aggregate默认na.action na.omit分组列有缺失的行整行不参与聚合结果就少一组。这个坑很隐蔽因为不报错。解决方式是先table(df$group, useNA ifany)看有没有NA组需要保留就设na.action na.pass或者先把缺失组重编码成“未知”再聚合。5.4 现象Hmisc::describe()和psych::describe()互相覆盖原因同名函数加载顺序解决用包名::函数名显式调用两个包都导出describe后library的覆盖先library的。脚本里如果先加载Hmisc再加载psych你调describe()实际走的是psych版本输出字段对不上预期。解决方式是全程写Hmisc::describe()和psych::describe()别图省事。这也是我后来养成的习惯凡是跨包同名函数一律加命名空间。5.5 现象ggplot2箱线图 x 轴挤成一团或分组错乱原因分组变量没转因子解决factor()显式转换并检查水平顺序数值型分组变量直接进aes(x ...)ggplot2会按连续刻度处理箱线图要么画不出要么间距诡异。解决是factor(cyl)如果水平顺序有业务含义比如低、中、高再用factor(cyl, levels c(...))固定顺序。另外geom_boxplot()遇到每组样本量极小时箱体会退化这时得回头查数据而不是调图。6. 进阶技巧把描述性统计封装成可复用的一键函数做到这里单次分析已经能跑通。但真实项目里你会反复对不同的数据框、不同的变量子集做同一套统计每次手敲sapply加psych很烦。我的习惯是封装一个desc_report()输入数据框和变量名向量输出一张规整的表缺失处理、偏度峰度口径、分组逻辑全固定死避免每次重新决策。# 一键描述性统计输入数据框与变量名输出规整数据框 desc_report - function(df, vars, group NULL, na.rm TRUE) { sub - df[, vars, drop FALSE] # 只取目标列dropFALSE 防止单列退化成向量 if (!is.null(group)) { # 有分组时按组套 psych::describe再补组名 res - lapply(split(sub, df[[group]]), function(g) { as.data.frame(psych::describe(g, fast TRUE)) }) out - do.call(rbind, res) out$group - rep(names(res), each nrow(res[[1]])) out$variable - rownames(out) rownames(out) - NULL return(out[, c(group, variable, n, mean, sd, median, min, max, skew, kurtosis)]) } # 无分组时直接出全表 out - as.data.frame(psych::describe(sub, fast TRUE)) out$variable - rownames(out) rownames(out) - NULL out[, c(variable, n, mean, sd, median, min, max, skew, kurtosis)] } # 用法不分组 desc_report(mtcars, c(mpg, hp, wt)) # 用法按 cyl 分组 desc_report(mtcars, c(mpg, hp, wt), group cyl)逻辑说明drop FALSE是防止只传一个变量时子集退化成向量psych::describe对向量和数据框行为不同这个参数是后悔药。split按分组列切数据lapply逐组算do.call(rbind, ...)拼回长表再补group和variable两列。参数上fast TRUE跳过耗时项na.rm这里没直接传给describe因为psych内部对缺失有自己的处理实际项目里我会在进函数前先做一轮缺失筛查。字段裁剪那步只保留报告常用列避免输出太宽贴不进文档。验证方法很简单拿desc_report(mtcars, c(mpg,hp,wt))的输出和第 2 章sapply(mtcars[myvars], mystats)的结果对照均值和标准差应该一致偏度峰度因口径不同会有小差异这正好提醒你口径要写进注释。分组版本再和summaryBy的结果对一遍组数和每组样本量对得上才算过。从那以后我每次接手新数据集都强制先跑一遍这个封装函数加一张分组箱线图再决定要不要做更深的建模——描述性统计这步偷懒后面清洗和建模全是返工。希望帮到你。本文还有配套的精品资源点击获取