在R中向条形图添加正态分布

时间:2018-10-29 17:33:50

标签: r bar-chart normal-distribution

我想绘制一个直方图,然后将其与表示数据分布的正态分布重叠。但是,我的数据已经计算在内:

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")

enter image description here

两个问题:

  1. 在这种情况下,是否可以直接绘制直方图而不使用barplot
  2. 如何用适合我数据的正态分布线覆盖此条形图?

2 个答案:

答案 0 :(得分:1)

在这里您可以很好地解决您的问题:

Overlay normal curve to histogram in R

回答您的问题:

1-是的,您应该将dist.km和trips作为1变量,然后调用函数hist(),但是使用数据格式,您的方法就很酷。

2-如链接所示使用curve()和lines()。

答案 1 :(得分:1)

问题1:如果您没有原始数据,则不能使用hist

第二季度:有一些工作。

首先,barplot不提供离散的x轴。从图中可以清楚地看到,前两列之间的间距(2-0.5 = 1.5)与后两列之间的间距相同(42.5-32.5 = 10)。您可以通过查看barplot的(不可见的)返回值来获得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有效地假设了分类数据,而不是连续数据。

为了弥补这一点,我们可以调整图的宽度或它们之间的间隔。如果我们改变宽度,那么我们将宽度与视觉上的重要性混为一谈,我们应该避免的事情,所以让我们来谈谈“空间”:

(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

barplot adjusted for non-equi-distant spacing

为了绘制正态曲线,我们需要原始分布的均值和标准差。如果没有原始数据,我们可以使用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

请注意,我们不会调整偏差:回想一下(从您的统计信息类中),当您向源中的所有数据添加值时,“位置”统计信息(例如,均值,中位数)也会类似地进行调整(添加值),但方差不变。

所以我们现在可以使用curve将其添加到绘图中:

curve(dnorm(x, mean=mu2, sd=sigma2),
      col = "red", lwd = 2, add=TRUE)

barplot with un-scaled normal curve

注意:我们作为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)

barplot with adjusted normal curve

最后一点:这条法线是一个近似值,尽管很好,但仍不完美。如果您有原始数据,最好使用hist和实际的mu / sigma值。