Как построить две гистограммы вместе в R?

178

Я использую R, и у меня есть два фрейма данных: морковь и огурцы. Каждый кадр данных имеет один числовой столбец, в котором указаны длина всей измеренной моркови (всего: 100 тыс. Моркови) и огурцов (всего: 50 тыс. Огурцов).

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

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

Изображение 297

  • 0
    Кстати, какое программное обеспечение вы планируете использовать? Для открытого кода я бы порекомендовал gnuplot.info [gnuplot]. Я уверен, что в его документации вы найдете определенную технику и примеры сценариев, которые будут делать то, что вы хотите.
  • 1
    Я использую R, как подсказывает тег (отредактированный пост, чтобы прояснить это)
Показать ещё 1 комментарий
Теги:
plot
histogram

8 ответов

148
Лучший ответ

Это изображение, к которому вы привязались, предназначалось для кривых плотности, а не для гистограмм.

Если вы читали ggplot, возможно, единственное, чего вам не хватает, это комбинирование двух фреймов данных в один длинный.

Итак, начнем с чего-то вроде того, что у вас есть, двух отдельных наборов данных и объедините их.

carrots <- data.frame(length = rnorm(100000, 6, 2))
cukes <- data.frame(length = rnorm(50000, 7, 2.5))

#Now, combine your two dataframes into one.  First make a new column in each that will be a variable to identify where they came from later.
carrots$veg <- 'carrot'
cukes$veg <- 'cuke'

#and combine into your new data frame vegLengths
vegLengths <- rbind(carrots, cukes)

После этого, что необязательно, если ваши данные уже давно формальны, вам нужна только одна строка для создания вашего сюжета.

ggplot(vegLengths, aes(length, fill = veg)) + geom_density(alpha = 0.2)

Изображение 4350

Теперь, если вам действительно нужны гистограммы, то будет работать следующее. Обратите внимание, что вы должны изменить позицию из аргумента "stack" по умолчанию. Вы можете пропустить это, если у вас действительно нет представления о том, как выглядят ваши данные. Здесь лучше выглядит альфа. Также обратите внимание, что я сделал гистограммы плотности. Легко удалить y = ..density.., чтобы вернуть его в счет.

ggplot(vegLengths, aes(length, fill = veg)) + geom_histogram(alpha = 0.5, aes(y = ..density..), position = 'identity')

Изображение 4351

  • 8
    Если вы хотите остаться с гистограммами, используйте ggplot(vegLengths, aes(length, fill = veg)) + geom_bar(pos="dodge") . Это создаст чересстрочные гистограммы, как в MATLAB.
  • 1
    Спасибо за ответ! Часть 'position = "identity"' на самом деле важна, так как в противном случае столбцы сложены, что вводит в заблуждение, когда объединено с плотностью, которая по умолчанию кажется "идентичностью", то есть наложена в отличие от сложенной.
217

Вот еще более простое решение с использованием базовой графики и альфа-смешивания (которая не работает на всех графических устройствах):

set.seed(42)
p1 <- hist(rnorm(500,4))                     # centered at 4
p2 <- hist(rnorm(500,6))                     # centered at 6
plot( p1, col=rgb(0,0,1,1/4), xlim=c(0,10))  # first histogram
plot( p2, col=rgb(1,0,0,1/4), xlim=c(0,10), add=T)  # second

Ключ в том, что цвета полупрозрачны.

Изменить, более двух лет спустя. Поскольку это только что получило верхнюю часть, я полагаю, что я могу добавить визуальное представление о том, что генерирует код, поскольку альфа-смешение настолько полезно:

Изображение 4352

  • 5
    +1 спасибо всем, это можно преобразовать в более гладкую гистограмму (например, had.co.nz/ggplot2/graphics/55078149a733dd1a0b42a57faf847036.png )?
  • 3
    Почему вы разделили команды plot ? Вы можете поместить все эти параметры в команды hist и просто поместить их в две строки.
Показать ещё 2 комментария
35

Здесь функция, которую я написал, что использует псевдопрозрачность для представления перекрывающихся гистограмм

plotOverlappingHist <- function(a, b, colors=c("white","gray20","gray50"),
                                breaks=NULL, xlim=NULL, ylim=NULL){

  ahist=NULL
  bhist=NULL

  if(!(is.null(breaks))){
    ahist=hist(a,breaks=breaks,plot=F)
    bhist=hist(b,breaks=breaks,plot=F)
  } else {
    ahist=hist(a,plot=F)
    bhist=hist(b,plot=F)

    dist = ahist$breaks[2]-ahist$breaks[1]
    breaks = seq(min(ahist$breaks,bhist$breaks),max(ahist$breaks,bhist$breaks),dist)

    ahist=hist(a,breaks=breaks,plot=F)
    bhist=hist(b,breaks=breaks,plot=F)
  }

  if(is.null(xlim)){
    xlim = c(min(ahist$breaks,bhist$breaks),max(ahist$breaks,bhist$breaks))
  }

  if(is.null(ylim)){
    ylim = c(0,max(ahist$counts,bhist$counts))
  }

  overlap = ahist
  for(i in 1:length(overlap$counts)){
    if(ahist$counts[i] > 0 & bhist$counts[i] > 0){
      overlap$counts[i] = min(ahist$counts[i],bhist$counts[i])
    } else {
      overlap$counts[i] = 0
    }
  }

  plot(ahist, xlim=xlim, ylim=ylim, col=colors[1])
  plot(bhist, xlim=xlim, ylim=ylim, col=colors[2], add=T)
  plot(overlap, xlim=xlim, ylim=ylim, col=colors[3], add=T)
}

Здесь другой способ сделать это, используя поддержку R для прозрачных цветов

a=rnorm(1000, 3, 1)
b=rnorm(1000, 6, 1)
hist(a, xlim=c(0,10), col="red")
hist(b, add=T, col=rgb(0, 1, 0, 0.5) )

Результаты в конечном итоге выглядят примерно так: Изображение 4353

  • 0
    +1 для опции, доступной на всех графических устройствах (например, postscript )
23

Вот пример того, как вы можете это сделать в "классической" графике R:

## generate some random data
carrotLengths <- rnorm(1000,15,5)
cucumberLengths <- rnorm(200,20,7)
## calculate the histograms - don't plot yet
histCarrot <- hist(carrotLengths,plot = FALSE)
histCucumber <- hist(cucumberLengths,plot = FALSE)
## calculate the range of the graph
xlim <- range(histCucumber$breaks,histCarrot$breaks)
ylim <- range(0,histCucumber$density,
              histCarrot$density)
## plot the first graph
plot(histCarrot,xlim = xlim, ylim = ylim,
     col = rgb(1,0,0,0.4),xlab = 'Lengths',
     freq = FALSE, ## relative, not absolute frequency
     main = 'Distribution of carrots and cucumbers')
## plot the second graph on top of this
opar <- par(new = FALSE)
plot(histCucumber,xlim = xlim, ylim = ylim,
     xaxt = 'n', yaxt = 'n', ## don't add axes
     col = rgb(0,0,1,0.4), add = TRUE,
     freq = FALSE) ## relative, not absolute frequency
## add a legend in the corner
legend('topleft',c('Carrots','Cucumbers'),
       fill = rgb(1:0,0,0:1,0.4), bty = 'n',
       border = NA)
par(opar)

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

  • 0
    Очень хорошо. Это также напомнило мне о том, что один stackoverflow.com/questions/3485456/…
  • 2
    эй ... я обычно тот, кто выставляет базовую версию R! :) Вы забыли назначить опар.
Показать ещё 3 комментария
20

Уже есть красивые ответы, но я подумал добавить это. Выглядит хорошо. (Скопированные случайные числа из @Dirk). library(scales) требуется

set.seed(42)
hist(rnorm(500,4),xlim=c(0,10),col='skyblue',border=F)
hist(rnorm(500,6),add=T,col=scales::alpha('red',.5),border=F)

Результат...

Изображение 4354

Обновление: эта функция перекрытия может быть полезна для некоторых.

hist0 <- function(...,col='skyblue',border=T) hist(...,col=col,border=border) 

Я чувствую, что результат hist0 выглядит более красивым, чем hist

hist2 <- function(var1, var2,name1='',name2='',
              breaks = min(max(length(var1), length(var2)),20), 
              main0 = "", alpha0 = 0.5,grey=0,border=F,...) {    

library(scales)
  colh <- c(rgb(0, 1, 0, alpha0), rgb(1, 0, 0, alpha0))
  if(grey) colh <- c(alpha(grey(0.1,alpha0)), alpha(grey(0.9,alpha0)))

  max0 = max(var1, var2)
  min0 = min(var1, var2)

  den1_max <- hist(var1, breaks = breaks, plot = F)$density %>% max
  den2_max <- hist(var2, breaks = breaks, plot = F)$density %>% max
  den_max <- max(den2_max, den1_max)*1.2
  var1 %>% hist0(xlim = c(min0 , max0) , breaks = breaks,
                 freq = F, col = colh[1], ylim = c(0, den_max), main = main0,border=border,...)
  var2 %>% hist0(xlim = c(min0 , max0),  breaks = breaks,
                 freq = F, col = colh[2], ylim = c(0, den_max), add = T,border=border,...)
  legend(min0,den_max, legend = c(
    ifelse(nchar(name1)==0,substitute(var1) %>% deparse,name1),
    ifelse(nchar(name2)==0,substitute(var2) %>% deparse,name2),
    "Overlap"), fill = c('white','white', colh[1]), bty = "n", cex=1,ncol=3)

  legend(min0,den_max, legend = c(
    ifelse(nchar(name1)==0,substitute(var1) %>% deparse,name1),
    ifelse(nchar(name2)==0,substitute(var2) %>% deparse,name2),
    "Overlap"), fill = c(colh, colh[2]), bty = "n", cex=1,ncol=3) }

Результат

par(mar=c(3, 4, 3, 2) + 0.1) 
set.seed(100) 
hist2(rnorm(10000,2),rnorm(10000,3),breaks = 50)

является

Изображение 4355

13

Здесь версия, подобная ggplot2, которую я дал только в базе R. Я скопировал некоторые из @nullglob.

сгенерировать данные

carrots <- rnorm(100000,5,2)
cukes <- rnorm(50000,7,2.5)

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

## calculate the density - don't plot yet
densCarrot <- density(carrots)
densCuke <- density(cukes)
## calculate the range of the graph
xlim <- range(densCuke$x,densCarrot$x)
ylim <- range(0,densCuke$y, densCarrot$y)
#pick the colours
carrotCol <- rgb(1,0,0,0.2)
cukeCol <- rgb(0,0,1,0.2)
## plot the carrots and set up most of the plot parameters
plot(densCarrot, xlim = xlim, ylim = ylim, xlab = 'Lengths',
     main = 'Distribution of carrots and cucumbers', 
     panel.first = grid())
#put our density plots in
polygon(densCarrot, density = -1, col = carrotCol)
polygon(densCuke, density = -1, col = cukeCol)
## add a legend in the corner
legend('topleft',c('Carrots','Cucumbers'),
       fill = c(carrotCol, cukeCol), bty = 'n',
       border = NA)

Изображение 4356

7

@Dirk Eddelbuettel: Основная идея отличная, но код, как показано, может быть улучшен. [Занимает много времени, чтобы объяснить, следовательно, отдельный ответ, а не комментарий.]

Функция hist() по умолчанию рисует графики, поэтому вам нужно добавить опцию plot=FALSE. Более того, яснее установить область графика вызовом plot(0,0,type="n",...), в который вы можете добавить метки оси, название сюжета и т.д. Наконец, я хотел бы упомянуть, что можно также использовать затенение для различения двух гистограмм. Вот код:

set.seed(42)
p1 <- hist(rnorm(500,4),plot=FALSE)
p2 <- hist(rnorm(500,6),plot=FALSE)
plot(0,0,type="n",xlim=c(0,10),ylim=c(0,100),xlab="x",ylab="freq",main="Two histograms")
plot(p1,col="green",density=10,angle=135,add=TRUE)
plot(p2,col="blue",density=10,angle=45,add=TRUE)

И вот результат (слишком большой из-за RStudio:-)):

Изображение 4357

  • 0
    Повышение этого, потому что это очень простой вариант, использующий base и жизнеспособный на устройствах postscript .
6

Plotly R API может быть вам полезен. Ниже представлен график .

library(plotly)
#add username and key
p <- plotly(username="Username", key="API_KEY")
#generate data
x0 = rnorm(500)
x1 = rnorm(500)+1
#arrange your graph
data0 = list(x=x0,
         name = "Carrots",
         type='histogramx',
         opacity = 0.8)

data1 = list(x=x1,
         name = "Cukes",
         type='histogramx',
         opacity = 0.8)
#specify type as 'overlay'
layout <- list(barmode='overlay',
               plot_bgcolor = 'rgba(249,249,251,.85)')  
#format response, and use 'browseURL' to open graph tab in your browser.
response = p$plotly(data0, data1, kwargs=list(layout=layout))

url = response$url
filename = response$filename

browseURL(response$url)

Полное раскрытие: я нахожусь в команде.

Изображение 4358

Ещё вопросы

Сообщество Overcoder
Наверх
Меню