将正态分布添加到 R 中的条形图
Adding a normal distribution to a bar chart in R
我想绘制一个直方图,然后用表示数据分布的正态分布覆盖它。但是,我的数据已经被统计了:
df<- structure(list(trips = c(12955L, 36890L, 47035L, 48650L, 70910L,
93755L, 45315L, 16565L, 4725L, 9460L), dist.km = c(0.5, 2, 4,
8.5, 12.5, 17.5, 22.5, 27.5, 32.5, 42.5), share = c(0.03, 0.09,
0.12, 0.13, 0.18, 0.24, 0.12, 0.04, 0.01, 0.02)), row.names = c(NA,
-10L), class = c("tbl_df", "tbl", "data.frame"))
既然数据已经统计好了,我可以用barplot
代替hist
:
barplot(df$share,
names.arg=census.car$dist.km,
col="orange",
xlab="km",
ylab="trips")
两个问题:
- 在这种情况下,有没有办法直接绘制直方图而不是使用
barplot
?
- 如何用适合我的数据的正态分布线覆盖此条形图?
这里有一个很棒的 link 可以解决您的问题:
Overlay normal curve to histogram in R
回答您的问题:
1- 是的,您应该将 dist.km 和 trips 作为 1 个变量,然后调用函数 hist(),但是您的数据格式非常酷。
2- 如 link 所述使用 curve() 和 lines()。
Q1:如果没有原始数据,那么就不能使用hist
。
Q2:有一些工作。
首先,barplot
不提供离散的 x 轴。查看您的绘图,可以清楚地看到前两列 (2-0.5 = 1.5) 与后两列 (42.5-32.5 = 10) 之间的间距相同。您可以通过查看 barplot
:
的(不可见)return 值来获取 x 轴中点
(barplot(df$share, names.arg=df$dist.km,
col="orange", xlab="km", ylab="trips"))
# [,1]
# [1,] 0.7
# [2,] 1.9
# [3,] 3.1
# [4,] 4.3
# [5,] 5.5
# [6,] 6.7
# [7,] 7.9
# [8,] 9.1
# [9,] 10.3
# [10,] 11.5
尽管实际点没有这样做,但这些点是等距的。这种等距离是因为 R 有效地假设分类数据,而不是连续的。
为了弥补这一点,我们可以调整绘图的宽度或它们之间的 space。如果我们改变宽度,那么我们就会将宽度与视觉重要性混为一谈,这是我们应该避免的事情,所以让我们使用 "space":
(bp <- barplot(df$share, names.arg=df$dist.km,
space = c(0, diff(df$dist.km)),
col="orange", xlab="km", ylab="trips"))
# [,1]
# [1,] 0.5
# [2,] 3.0
# [3,] 6.0
# [4,] 11.5
# [5,] 16.5
# [6,] 22.5
# [7,] 28.5
# [8,] 34.5
# [9,] 40.5
# [10,] 51.5
为了绘制正态曲线,我们需要原始分布的均值和标准差。在没有原始数据的情况下,我们可以用 Hmisc
包提供的加权平均值和加权标准差对其进行近似。
mu <- Hmisc::wtd.mean(df$dist.km, df$trips)
sigma <- sqrt(Hmisc::wtd.var(df$dist.km, weights = df$trips))
c(mu, sigma)
# [1] 13.565338 8.911899
不幸的是,正如我们在上面第二个 barplot
的输出中看到的,x 轴与数据的比例不同。幸运的是,它对我们来说仍然是连续的和线性的,所以我们只需要为此进行调整。我们可以手动计算,但为了论证,这里有一个反向转换函数:
func <- function(a) {
(min(df$dist.km) - bp[1,1]) + # the offset, happens to be 0 here since
# the first datapoint is exactly 0.5
a * diff(range(bp[,1])) / diff(range(df$dist.km))
}
mu2 <- func(mu)
sigma2 <- sigma
c(mu2, sigma2)
# [1] 16.472196 8.911899
请注意,我们不会调整偏差:回想一下(根据您的统计数据 class),当您向源中的所有数据添加一个值时,"location" 统计数据(例如,均值,中位数)进行类似调整(添加值)但方差不变。
所以我们现在可以使用 curve
将其添加到绘图中:
curve(dnorm(x, mean=mu2, sd=sigma2),
col = "red", lwd = 2, add=TRUE)
注意:我们作为第一个参数给 curve
的函数调用需要那里的 x
变量,即使我们还没有定义它。这在内部用于 curve
并替换为实际的值向量。它可以不同,也许 curve(dnorm(yy,...), xname="yy")
.
从美学上讲它不够高......我们可以用最大频率缩放它:
# start over
bp <- barplot(df$share, names.arg=df$dist.km,
space = c(0, diff(df$dist.km)),
col="orange", xlab="km", ylab="trips")
curve(dnorm(x, mean=mu2, sd=sigma2) / max(df$share),
col = "red", lwd = 2, add=TRUE)
最后一点:这个正态曲线是一个近似值,虽然很好但仍然不完美。如果您有原始数据,使用 hist
和实际 mu/sigma 值会更好。
我想绘制一个直方图,然后用表示数据分布的正态分布覆盖它。但是,我的数据已经被统计了:
df<- structure(list(trips = c(12955L, 36890L, 47035L, 48650L, 70910L,
93755L, 45315L, 16565L, 4725L, 9460L), dist.km = c(0.5, 2, 4,
8.5, 12.5, 17.5, 22.5, 27.5, 32.5, 42.5), share = c(0.03, 0.09,
0.12, 0.13, 0.18, 0.24, 0.12, 0.04, 0.01, 0.02)), row.names = c(NA,
-10L), class = c("tbl_df", "tbl", "data.frame"))
既然数据已经统计好了,我可以用barplot
代替hist
:
barplot(df$share,
names.arg=census.car$dist.km,
col="orange",
xlab="km",
ylab="trips")
两个问题:
- 在这种情况下,有没有办法直接绘制直方图而不是使用
barplot
? - 如何用适合我的数据的正态分布线覆盖此条形图?
这里有一个很棒的 link 可以解决您的问题:
Overlay normal curve to histogram in R
回答您的问题:
1- 是的,您应该将 dist.km 和 trips 作为 1 个变量,然后调用函数 hist(),但是您的数据格式非常酷。
2- 如 link 所述使用 curve() 和 lines()。
Q1:如果没有原始数据,那么就不能使用hist
。
Q2:有一些工作。
首先,barplot
不提供离散的 x 轴。查看您的绘图,可以清楚地看到前两列 (2-0.5 = 1.5) 与后两列 (42.5-32.5 = 10) 之间的间距相同。您可以通过查看 barplot
:
(barplot(df$share, names.arg=df$dist.km,
col="orange", xlab="km", ylab="trips"))
# [,1]
# [1,] 0.7
# [2,] 1.9
# [3,] 3.1
# [4,] 4.3
# [5,] 5.5
# [6,] 6.7
# [7,] 7.9
# [8,] 9.1
# [9,] 10.3
# [10,] 11.5
尽管实际点没有这样做,但这些点是等距的。这种等距离是因为 R 有效地假设分类数据,而不是连续的。
为了弥补这一点,我们可以调整绘图的宽度或它们之间的 space。如果我们改变宽度,那么我们就会将宽度与视觉重要性混为一谈,这是我们应该避免的事情,所以让我们使用 "space":
(bp <- barplot(df$share, names.arg=df$dist.km,
space = c(0, diff(df$dist.km)),
col="orange", xlab="km", ylab="trips"))
# [,1]
# [1,] 0.5
# [2,] 3.0
# [3,] 6.0
# [4,] 11.5
# [5,] 16.5
# [6,] 22.5
# [7,] 28.5
# [8,] 34.5
# [9,] 40.5
# [10,] 51.5
为了绘制正态曲线,我们需要原始分布的均值和标准差。在没有原始数据的情况下,我们可以用 Hmisc
包提供的加权平均值和加权标准差对其进行近似。
mu <- Hmisc::wtd.mean(df$dist.km, df$trips)
sigma <- sqrt(Hmisc::wtd.var(df$dist.km, weights = df$trips))
c(mu, sigma)
# [1] 13.565338 8.911899
不幸的是,正如我们在上面第二个 barplot
的输出中看到的,x 轴与数据的比例不同。幸运的是,它对我们来说仍然是连续的和线性的,所以我们只需要为此进行调整。我们可以手动计算,但为了论证,这里有一个反向转换函数:
func <- function(a) {
(min(df$dist.km) - bp[1,1]) + # the offset, happens to be 0 here since
# the first datapoint is exactly 0.5
a * diff(range(bp[,1])) / diff(range(df$dist.km))
}
mu2 <- func(mu)
sigma2 <- sigma
c(mu2, sigma2)
# [1] 16.472196 8.911899
请注意,我们不会调整偏差:回想一下(根据您的统计数据 class),当您向源中的所有数据添加一个值时,"location" 统计数据(例如,均值,中位数)进行类似调整(添加值)但方差不变。
所以我们现在可以使用 curve
将其添加到绘图中:
curve(dnorm(x, mean=mu2, sd=sigma2),
col = "red", lwd = 2, add=TRUE)
注意:我们作为第一个参数给 curve
的函数调用需要那里的 x
变量,即使我们还没有定义它。这在内部用于 curve
并替换为实际的值向量。它可以不同,也许 curve(dnorm(yy,...), xname="yy")
.
从美学上讲它不够高......我们可以用最大频率缩放它:
# start over
bp <- barplot(df$share, names.arg=df$dist.km,
space = c(0, diff(df$dist.km)),
col="orange", xlab="km", ylab="trips")
curve(dnorm(x, mean=mu2, sd=sigma2) / max(df$share),
col = "red", lwd = 2, add=TRUE)
最后一点:这个正态曲线是一个近似值,虽然很好但仍然不完美。如果您有原始数据,使用 hist
和实际 mu/sigma 值会更好。