使用R中lm()与bs()绘制样条遇行数不匹配错误及相关咨询
问题解决与答疑
错误原因分析
你遇到的'newdata' has 38 rows, but the variables found have 700 rows错误,根源有两个:
- 构建模型时直接用了
data_clean$visits、data_clean$discount这种硬编码列引用,而非数据框内的变量名。这会让模型把这些向量当作全局变量,而非数据框的列,导致predict无法匹配newdata里的变量。 - 模型包含
discount变量,但调用predict时的newdata只提供了visits,缺少discount的取值,模型无法完成预测。
另外,bs()里同时设置df=3和knots=3存在冲突:df=3的样条默认只需要2个内部节点,手动设knots=3会导致自由度不匹配,建议只保留df参数。
疑问解答
1. 如何从样条模型生成可用于绘图的曲线?
按以下步骤操作:
- 修正模型定义:使用变量名而非硬编码列引用,让模型正确关联数据框变量:
spline_model <- lm(sales ~ bs(visits, df = 3) + discount, data = data_clean) - 生成连续预测序列:为
visits生成覆盖全范围的连续序列,同时给discount指定固定值(比如均值,或你想观察的特定折扣率):visits_seq <- seq(min(data_clean$visits), max(data_clean$visits), length.out = 100) # 生成更平滑的序列 # 固定discount为均值,也可设为特定值如0.1 new_data <- data.frame(visits = visits_seq, discount = mean(data_clean$discount)) - 预测拟合值:
spline_pred <- predict(spline_model, newdata = new_data) - 绘图:用基础绘图或ggplot2绘制原始点+拟合曲线:
# 基础绘图 plot(data_clean$visits, data_clean$sales, main = "观测值 vs 拟合值", xlab = "访问量", ylab = "销售额") lines(visits_seq, spline_pred, col = "red", lwd = 3) # ggplot2版本 library(ggplot2) ggplot(data_clean, aes(x = visits, y = sales)) + geom_point(alpha = 0.5) + geom_line(data = new_data, aes(y = spline_pred), color = "red", linewidth = 1) + labs(title = "观测值 vs 样条拟合曲线", x = "访问量", y = "销售额")
2. 为什么需要用seq生成序列+predict绘图,而非直接用原模型?
核心原因有两点:
- 原始数据离散且分布不均:你的原始
visits数据可能存在重复值、稀疏区间,直接用模型对原始数据的拟合值绘图,得到的是零散的点而非平滑曲线,无法直观展示变量间的整体趋势。 - 样条模型拟合连续关系:样条的核心是捕捉变量间的非线性连续趋势,生成覆盖全范围的连续
visits序列,能让模型预测出每个点的拟合值,从而绘制出平滑的趋势曲线,完整展示变量间的非线性关联。
3. 如何绘制3D图展示sales、visits、discount的关联?
可以用plotly包绘制交互式3D曲面图,直观展示三个变量的关系:
library(plotly) # 生成visits和discount的网格数据 visits_grid <- seq(min(data_clean$visits), max(data_clean$visits), length.out = 50) discount_grid <- seq(min(data_clean$discount), max(data_clean$discount), length.out = 50) grid_data <- expand.grid(visits = visits_grid, discount = discount_grid) # 预测所有网格点的sales值 grid_data$sales_pred <- predict(spline_model, newdata = grid_data) # 绘制3D曲面图 plot_ly(grid_data, x = ~visits, y = ~discount, z = ~sales_pred) %>% add_surface(colorscale = "Viridis") %>% add_markers(data = data_clean, x = ~visits, y = ~discount, z = ~sales, marker = list(color = "black", size = 3)) %>% layout(scene = list(xaxis = list(title = "访问量"), yaxis = list(title = "折扣率"), zaxis = list(title = "销售额")))
这个图会显示样条模型拟合的3D曲面,同时叠加原始数据点,清晰展示两个自变量对因变量的联合影响。
完整修正代码
library(dplyr) library(splines) library(ggplot2) library(plotly) # 加载数据 data_path <- "https://raw.githubusercontent.com/juanitorduz/website_projects/master/data/sales_dag.csv" data <- read.csv(data_path) data_clean <- data %>% select(visits, discount, sales) # 修正后的样条模型 spline_model <- lm(sales ~ bs(visits, df = 3) + discount, data = data_clean) # 生成预测序列并绘图(2D) visits_seq <- seq(min(data_clean$visits), max(data_clean$visits), length.out = 100) new_data <- data.frame(visits = visits_seq, discount = mean(data_clean$discount)) spline_pred <- predict(spline_model, newdata = new_data) ggplot(data_clean, aes(x = visits, y = sales)) + geom_point(alpha = 0.5) + geom_line(data = new_data, aes(y = spline_pred), color = "red", linewidth = 1) + labs(title = "观测销售额 vs 样条拟合曲线", x = "网站访问量", y = "销售额") # 绘制3D关联图 visits_grid <- seq(min(data_clean$visits), max(data_clean$visits), length.out = 50) discount_grid <- seq(min(data_clean$discount), max(data_clean$discount), length.out = 50) grid_data <- expand.grid(visits = visits_grid, discount = discount_grid) grid_data$sales_pred <- predict(spline_model, newdata = grid_data) plot_ly(grid_data, x = ~visits, y = ~discount, z = ~sales_pred) %>% add_surface(colorscale = "Viridis") %>% add_markers(data = data_clean, x = ~visits, y = ~discount, z = ~sales, marker = list(color = "black", size = 3)) %>% layout(scene = list(xaxis = list(title = "访问量"), yaxis = list(title = "折扣率"), zaxis = list(title = "销售额")))
内容的提问来源于stack exchange,提问作者tt33tt
相关产品推荐
相关产品推荐

