Поверните гистограмму в R или наложите плотность в штриховой планке

Я хотел бы повернуть гистограмму в R, построенную по hist(). Вопрос не новый, и на нескольких форумах я обнаружил, что это невозможно. Однако все эти ответы датируются 2010 годом или даже позже.

Кто-нибудь нашел решение тем временем?

Один из способов обойти проблему заключается в построении гистограммы через barplot(), которая предлагает вариант "horiz = TRUE". Сюжет отлично работает, но я не могу наложить плотность в баррелях. Проблема, вероятно, лежит в оси х, так как в вертикальном графике плотность центрируется в первом бункере, тогда как в горизонтальном графике кривая плотности перепутана.

Любая помощь очень ценится!

Спасибо,

Нильс

код:

require(MASS)
Sigma <- matrix(c(2.25, 0.8, 0.8, 1), 2, 2)
mvnorm <- mvrnorm(1000, c(0,0), Sigma)

scatterHist.Norm <- function(x,y) {
 zones <- matrix(c(2,0,1,3), ncol=2, byrow=TRUE)
 layout(zones, widths=c(2/3,1/3), heights=c(1/3,2/3))
 xrange <- range(x) ; yrange <- range(y)
 par(mar=c(3,3,1,1))
 plot(x, y, xlim=xrange, ylim=yrange, xlab="", ylab="", cex=0.5)
 xhist <- hist(x, plot=FALSE, breaks=seq(from=min(x), to=max(x), length.out=20))
 yhist <- hist(y, plot=FALSE, breaks=seq(from=min(y), to=max(y), length.out=20))
 top <- max(c(xhist$counts, yhist$counts))
 par(mar=c(0,3,1,1))
 plot(xhist, axes=FALSE, ylim=c(0,top), main="", col="grey")
 x.xfit <- seq(min(x),max(x),length.out=40)
 x.yfit <- dnorm(x.xfit,mean=mean(x),sd=sd(x))
 x.yfit <- x.yfit*diff(xhist$mids[1:2])*length(x)
 lines(x.xfit, x.yfit, col="red")
 par(mar=c(0,3,1,1))
 plot(yhist, axes=FALSE, ylim=c(0,top), main="", col="grey", horiz=TRUE)
 y.xfit <- seq(min(x),max(x),length.out=40)
 y.yfit <- dnorm(y.xfit,mean=mean(x),sd=sd(x))
 y.yfit <- y.yfit*diff(yhist$mids[1:2])*length(x)
 lines(y.xfit, y.yfit, col="red")
}
scatterHist.Norm(mvnorm[,1], mvnorm[,2])


scatterBar.Norm <- function(x,y) {
 zones <- matrix(c(2,0,1,3), ncol=2, byrow=TRUE)
 layout(zones, widths=c(2/3,1/3), heights=c(1/3,2/3))
 xrange <- range(x) ; yrange <- range(y)
 par(mar=c(3,3,1,1))
 plot(x, y, xlim=xrange, ylim=yrange, xlab="", ylab="", cex=0.5)
 xhist <- hist(x, plot=FALSE, breaks=seq(from=min(x), to=max(x), length.out=20))
 yhist <- hist(y, plot=FALSE, breaks=seq(from=min(y), to=max(y), length.out=20))
 top <- max(c(xhist$counts, yhist$counts))
 par(mar=c(0,3,1,1))
 barplot(xhist$counts, axes=FALSE, ylim=c(0, top), space=0)
 x.xfit <- seq(min(x),max(x),length.out=40)
 x.yfit <- dnorm(x.xfit,mean=mean(x),sd=sd(x))
 x.yfit <- x.yfit*diff(xhist$mids[1:2])*length(x)
 lines(x.xfit, x.yfit, col="red")
 par(mar=c(3,0,1,1))
 barplot(yhist$counts, axes=FALSE, xlim=c(0, top), space=0, horiz=TRUE)
 y.xfit <- seq(min(x),max(x),length.out=40)
 y.yfit <- dnorm(y.xfit,mean=mean(x),sd=sd(x))
 y.yfit <- y.yfit*diff(yhist$mids[1:2])*length(x)
 lines(y.xfit, y.yfit, col="red")
}
scatterBar.Norm(mvnorm[,1], mvnorm[,2])
#

Источник графика рассеяния с маргинальными гистограммами (нажмите первую ссылку после "адаптировано из..." ):

http://r.789695.n4.nabble.com/newbie-scatterplot-with-marginal-histograms-done-and-axes-labels-td872589.html

Источник плотности в диаграмме рассеяния:

http://www.statmethods.net/graphs/density.html

Ответ 1

scatterBarNorm <- function(x, dcol="blue", lhist=20, num.dnorm=5*lhist, ...){
    ## check input
    stopifnot(ncol(x)==2)
    ## set up layout and graphical parameters
    layMat <- matrix(c(2,0,1,3), ncol=2, byrow=TRUE)
    layout(layMat, widths=c(5/7, 2/7), heights=c(2/7, 5/7))
    ospc <- 0.5 # outer space
    pext <- 4 # par extension down and to the left
    bspc <- 1 # space between scatter plot and bar plots
    par. <- par(mar=c(pext, pext, bspc, bspc),
                oma=rep(ospc, 4)) # plot parameters
    ## scatter plot
    plot(x, xlim=range(x[,1]), ylim=range(x[,2]), ...)
    ## 3) determine barplot and height parameter
    ## histogram (for barplot-ting the density)
    xhist <- hist(x[,1], plot=FALSE, breaks=seq(from=min(x[,1]), to=max(x[,1]),
                                     length.out=lhist))
    yhist <- hist(x[,2], plot=FALSE, breaks=seq(from=min(x[,2]), to=max(x[,2]),
                                     length.out=lhist)) # note: this uses probability=TRUE
    ## determine the plot range and all the things needed for the barplots and lines
    xx <- seq(min(x[,1]), max(x[,1]), length.out=num.dnorm) # evaluation points for the overlaid density
    xy <- dnorm(xx, mean=mean(x[,1]), sd=sd(x[,1])) # density points
    yx <- seq(min(x[,2]), max(x[,2]), length.out=num.dnorm)
    yy <- dnorm(yx, mean=mean(x[,2]), sd=sd(x[,2]))
    ## barplot and line for x (top)
    par(mar=c(0, pext, 0, 0))
    barplot(xhist$density, axes=FALSE, ylim=c(0, max(xhist$density, xy)),
            space=0) # barplot
    lines(seq(from=0, to=lhist-1, length.out=num.dnorm), xy, col=dcol) # line
    ## barplot and line for y (right)
    par(mar=c(pext, 0, 0, 0))
    barplot(yhist$density, axes=FALSE, xlim=c(0, max(yhist$density, yy)),
            space=0, horiz=TRUE) # barplot
    lines(yy, seq(from=0, to=lhist-1, length.out=num.dnorm), col=dcol) # line
    ## restore parameters
    par(par.)
}

require(mvtnorm)
X <- rmvnorm(1000, c(0,0), matrix(c(1, 0.8, 0.8, 1), 2, 2))
scatterBarNorm(X, xlab=expression(italic(X[1])), ylab=expression(italic(X[2])))

enter image description here

Ответ 2

Может быть полезно знать, что функция hist() невидимо возвращает всю информацию, необходимую для воспроизведения того, что она делает, используя более простые функции построения, например rect().

    vals <- rnorm(10)
    A <- hist(vals)
    A
    $breaks
    [1] -1.5 -1.0 -0.5  0.0  0.5  1.0  1.5

    $counts
    [1] 1 3 3 1 1 1

    $intensities
    [1] 0.2 0.6 0.6 0.2 0.2 0.2

    $density
    [1] 0.2 0.6 0.6 0.2 0.2 0.2

    $mids
    [1] -1.25 -0.75 -0.25  0.25  0.75  1.25

    $xname
    [1] "vals"

    $equidist
    [1] TRUE

    attr(,"class")
    [1] "histogram"

Вы можете создать такую ​​же гистограмму вручную, как это:

    plot(NULL, type = "n", ylim = c(0,max(A$counts)), xlim = c(range(A$breaks)))
    rect(A$breaks[1:(length(A$breaks) - 1)], 0, A$breaks[2:length(A$breaks)], A$counts)

С помощью этих частей вы можете перевернуть оси, как вам нравится:

    plot(NULL, type = "n", xlim = c(0, max(A$counts)), ylim = c(range(A$breaks)))
    rect(0, A$breaks[1:(length(A$breaks) - 1)], A$counts, A$breaks[2:length(A$breaks)])

Для подобных упражнений с density() см.: Осевая маркировка на графиках гистограммы и плотности R; несколько наложений графиков плотности

Ответ 3

Я не уверен, интересно ли это, но иногда я хочу использовать горизонтальные гистограммы без каких-либо пакетов и иметь возможность писать или рисовать в любой позиции графика.

Вот почему я написал следующую функцию с примерами, приведенными ниже. Если кто-нибудь знает пакет, к которому это подходит, напишите мне: berry-b at gmx.de

Пожалуйста, не забудьте указать переменную hpos в вашей рабочей области, так как она будет перезаписана функцией. (Да, для пакета мне нужно будет вставить некоторые функции безопасности в функцию).

horiz.hist <- function(Data, breaks="Sturges", col="transparent", las=1, 
ylim=range(HBreaks), labelat=pretty(ylim), labels=labelat, border=par("fg"), ... )
  {a <- hist(Data, plot=FALSE, breaks=breaks)
  HBreaks <- a$breaks
  HBreak1 <- a$breaks[1]
  hpos <<- function(Pos) (Pos-HBreak1)*(length(HBreaks)-1)/ diff(range(HBreaks))
  barplot(a$counts, space=0, horiz=T, ylim=hpos(ylim), col=col, border=border,...)      
  axis(2, at=hpos(labelat), labels=labels, las=las, ...) 
  print("use hpos() to address y-coordinates") }

Для примеров

# Data and basic concept
set.seed(8); ExampleData <- rnorm(50,8,5)+5
hist(ExampleData)
horiz.hist(ExampleData, xlab="absolute frequency") 
# Caution: the labels at the y-axis are not the real coordinates!
# abline(h=2) will draw above the second bar, not at the label value 2. Use hpos:
abline(h=hpos(11), col=2)

# Further arguments
horiz.hist(ExampleData, xlim=c(-8,20)) 
horiz.hist(ExampleData, main="the ... argument worked!", col.axis=3) 
hist(ExampleData, xlim=c(-10,40)) # with xlim
horiz.hist(ExampleData, ylim=c(-10,40), border="red") # with ylim
horiz.hist(ExampleData, breaks=20, col="orange")
axis(2, hpos(0:10), labels=F, col=2) # another use of hpos()

Один недостаток: функция не работает с точками останова, представленными как вектор с разной шириной столбцов.

Ответ 4

Спасибо, Тим и Пол. Вы заставили меня задуматься и использовать то, что на самом деле предоставляет hist().

Это мое решение сейчас (с большой помощью от Alex Pl.):

scatterBar.Norm <- function(x,y) {
 zones <- matrix(c(2,0,1,3), ncol=2, byrow=TRUE)
 layout(zones, widths=c(5/7,2/7), heights=c(2/7,5/7))
 xrange <- range(x)
 yrange <- range(y)
 par(mar=c(3,3,1,1))
 plot(x, y, xlim=xrange, ylim=yrange, xlab="", ylab="", cex=0.5)
 xhist <- hist(x, plot=FALSE, breaks=seq(from=min(x), to=max(x), length.out=20))
 yhist <- hist(y, plot=FALSE, breaks=seq(from=min(y), to=max(y), length.out=20))
 top <- max(c(xhist$density, yhist$density))
 par(mar=c(0,3,1,1))
 barplot(xhist$density, axes=FALSE, ylim=c(0, top), space=0)
 x.xfit <- seq(min(x),max(x),length.out=40)
 x.yfit <- dnorm(x.xfit, mean=mean(x), sd=sd(x))
 x.xscalefactor <- x.xfit / seq(from=0, to=19, length.out=40)
 lines(x.xfit/x.xscalefactor, x.yfit, col="red")
 par(mar=c(3,0,1,1))
 barplot(yhist$density, axes=FALSE, xlim=c(0, top), space=0, horiz=TRUE)
 y.xfit <- seq(min(y),max(y),length.out=40)
 y.yfit <- dnorm(y.xfit, mean=mean(y), sd=sd(y))
 y.xscalefactor <- y.xfit / seq(from=0, to=19, length.out=40)
 lines(y.yfit, y.xfit/y.xscalefactor, col="red")
}

Примеры:

require(MASS)
#Sigma <- matrix(c(2.25, 0.8, 0.8, 1), 2, 2)
Sigma <- matrix(c(1, 0.8, 0.8, 1), 2, 2)
mvnorm <- mvrnorm(1000, c(0,0), Sigma) ; scatterBar.Norm(mvnorm[,1], mvnorm[,2])

Асимметричная сигма приводит к несколько громоздкой гистограмме соответствующей оси.

Код остается преднамеренно "неуравновешенным", чтобы повысить понятность (для себя, когда я снова его перейду позже).

Нильс

Ответ 5

При использовании ggplot топовые оси очень хорошо работают. См. Например этот пример, который показывает, как это сделать для boxplot, но он работает одинаково хорошо для гистограммы, которую я предполагаю. В ggplot можно легко наложить разные типы сюжетов или геометрию в ggplot2 jargon. Поэтому объединение графика плотности и гистограммы должно быть легким.