Розсіювач з граничними гістограмами в ggplot2


137

Чи існує спосіб створення розсипань з граничними гістограмами, як у зразку нижче ggplot2? У Matlab це scatterhist()функція, а також існують еквіваленти для R. Однак я не бачив цього для ggplot2.

розсіювач з граничними гістограмами

Я розпочав спробу, створивши окремі графіки, але не знаю, як їх правильно розташувати.

 require(ggplot2)
 x<-rnorm(300)
 y<-rt(300,df=2)
 xy<-data.frame(x,y)
     xhist <- qplot(x, geom="histogram") + scale_x_continuous(limits=c(min(x),max(x))) + opts(axis.text.x = theme_blank(), axis.title.x=theme_blank(), axis.ticks = theme_blank(), aspect.ratio = 5/16, axis.text.y = theme_blank(), axis.title.y=theme_blank(), background.colour="white")
     yhist <- qplot(y, geom="histogram") + coord_flip() + opts(background.fill = "white", background.color ="black")

     yhist <- yhist + scale_x_continuous(limits=c(min(x),max(x))) + opts(axis.text.x = theme_blank(), axis.title.x=theme_blank(), axis.ticks = theme_blank(), aspect.ratio = 16/5, axis.text.y = theme_blank(), axis.title.y=theme_blank() )


     scatter <- qplot(x,y, data=xy)  + scale_x_continuous(limits=c(min(x),max(x))) + scale_y_continuous(limits=c(min(y),max(y)))
none <- qplot(x,y, data=xy) + geom_blank()

і упорядкування їх за допомогою функції, розміщеної тут . Але коротко кажучи: чи існує спосіб створення цих графіків?


@DWin правильно дякую - але я думаю, що це майже рішення, яке я дав у своєму питанні. однак, мені подобається, що geom_rag () думаю, що вам дуже дано нижче!
Себ

1
з недавньої публікації в блозі , який показує ту ж тему: blog.mckuhn.de/2009/09/learning-ggplot2-2d-plot-with.html виглядає також дуже приємно :)
Seb

Новий веб-сайт для графічної галереї: gallery.r-enthusiasts.com
IRTFM

@Seb ви можете подумати про зміну "прийнятої відповіді" на відповідь про пакет ggExtra, якщо ви вважаєте, що це має сенс
DeanAttali

Відповіді:


93

gridExtraПакет повинен працювати тут. Почніть із створення кожного з ggplot об’єктів:

hist_top <- ggplot()+geom_histogram(aes(rnorm(100)))
empty <- ggplot()+geom_point(aes(1,1), colour="white")+
         theme(axis.ticks=element_blank(), 
               panel.background=element_blank(), 
               axis.text.x=element_blank(), axis.text.y=element_blank(),           
               axis.title.x=element_blank(), axis.title.y=element_blank())

scatter <- ggplot()+geom_point(aes(rnorm(100), rnorm(100)))
hist_right <- ggplot()+geom_histogram(aes(rnorm(100)))+coord_flip()

Потім скористайтеся функцією grid.arrange:

grid.arrange(hist_top, empty, scatter, hist_right, ncol=2, nrow=2, widths=c(4, 1), heights=c(1, 4))

сюжет


6
1+ для демонстрації місця розташування, але вам не слід повторно робити випадкову вибірку, якщо ви хочете, щоб внутрішній розкид "вирівнявся" з граничними гістограмами.
IRTFM

1
Ти маєш рацію. Вони відібрані з одного розподілу, тому граничні гістограми теоретично повинні відповідати графіку розсіювання.
oeo4b

8
У "теорії" вони будуть асимптотично "відповідати"; на практиці кількість разів, коли вони будуть відповідати, нескінченно мала. Дуже легко використовувати приклад, що надається, xy <- data.frame(x=rnorm(300), y=rt(300,df=2) )та використовувати data=xyу викликах ggplot.
IRTFM

7
Я б не рекомендував це рішення, оскільки осі ділянок зазвичай точно не вирівнюються. Будемо сподіватися, що майбутні версії ggplot2 полегшать вирівнювання осей або навіть дозволять надати спеціальні анотації на сторонах панельної ділянки (наприклад, спеціальні функції вторинної осі у ґратах).
баптист

9
Ні, вони взагалі не хотіли б. ggplot2 в даний час виводить різну ширину панелі, яка змінюється залежно від масштабу міток осі тощо. Погляньте на ggExtra :: align.plots, щоб побачити тип злому, який зараз потрібен для вирівнювання осей.
баптист

115

Це не зовсім чуйна відповідь, але це дуже просто. Він ілюструє альтернативний метод відображення граничної щільності, а також як використовувати альфа-рівні для графічного виводу, що підтримує прозорість:

scatter <- qplot(x,y, data=xy)  + 
         scale_x_continuous(limits=c(min(x),max(x))) + 
         scale_y_continuous(limits=c(min(y),max(y))) + 
         geom_rug(col=rgb(.5,0,0,alpha=.2))
scatter

введіть тут опис зображення


5
Це цікавий спосіб показати щільність. Дякуємо, що додали цю відповідь. :)
Мішель

21
Слід зазначити, що цей метод набагато звичніший, ніж введення граничних гістограм. Насправді, сюжети килимів є поширеними в опублікованих статтях, де я ніколи не бачив опублікованої статті з маргінальними істограмами.
Сю Ван

Дуже цікава та інтуїтивно зрозуміла альтернативна відповідь! І дуже просто! Недарма вона отримує ще більше голосів, ніж правильна відповідь. Я розумію, що це по суті одновимірна теплова карта : килими темніші, де б не було людно. Єдине моє хвилювання було б, щоб роздільна здатність теплової карти була не такою високою, як гістограма. напр. коли ділянка невелика, всі килимки будуть стиснуті між собою, що ускладнює сприйняття розподілу. Хоча гістограма не страждає від обмеження. Дякую за ідею!
HongboZhu

94

Це може бути трохи пізно, але я вирішив зробити пакет ( ggExtra) для цього, оскільки він містив трохи коду і може бути стомлюючим для написання. Пакет також намагається вирішити деякі поширені проблеми, такі як забезпечення того, що навіть якщо є заголовок або текст збільшено, сюжети все одно будуть узгоджуватися один з одним.

Основна ідея схожа на відповіді, які тут давали, але це трохи перевищує це. Ось приклад того, як додати граничні гістограми до випадкового набору з 1000 балів. Сподіваємось, це полегшить додавання гістограм / графіків щільності в майбутньому.

Посилання на пакет ggExtra

library(ggplot2)
df <- data.frame(x = rnorm(1000, 50, 10), y = rnorm(1000, 50, 10))
p <- ggplot(df, aes(x, y)) + geom_point() + theme_classic()
ggExtra::ggMarginal(p, type = "histogram")

введіть тут опис зображення


1
Дякую за пакет. Це працює з коробки!
heroxbd

Чи можна намалювати графіки граничної щільності для об'єктів, згрупованих за кольором за допомогою цього пакета?
GegznaV

Ні, у неї немає такої логіки
DeanAttali

1
@jjrr Я не впевнений, що не працює, і які проблеми у вас виникають, але нещодавно в Github виникла проблема про візуалізацію в блокноті, і також є рішення, це може бути корисним github.com/daattali/ ggExtra / issues / 89
DeanAttali

1
@GegznaV, якщо ви все ще шукаєте спосіб мати графіки граничної щільності, згруповані за кольором, можна з ggExtra 0,9: ggMarginal (p, type = "щільність", розмір = 5, groupColour = TRUE)
MartineJ

46

Одне доповнення - просто заощадити час пошуку людей, які роблять це після нас.

Легенди, позначки на осі, тексти осі, галочки роблять сюжети відстороненими один від одного, тому ваш сюжет буде виглядати некрасиво та непослідовно.

Ви можете виправити це за допомогою деяких із цих параметрів теми,

+theme(legend.position = "none",          
       axis.title.x = element_blank(),
       axis.title.y = element_blank(),
       axis.text.x = element_blank(),
       axis.text.y = element_blank(), 
       plot.margin = unit(c(3,-5.5,4,3), "mm"))

і вирівняти ваги,

+scale_x_continuous(breaks = 0:6,
                    limits = c(0,6),
                    expand = c(.05,.05))

тому результати будуть виглядати нормально:

приклад


3
див. це для більш надійного рішення для вирівнювання сюжетних панелей
баптист

Так. Моя відповідь застаріла, скористайтеся запропонованим рішенням @baptiste.
Лорінк Ніттрай

@LorincNyitrai Ви можете поділитися своїм кодом для створення цього сюжету. У мене також є умова, коли я хочу зробити графік розсіювання точності-нагадування в ggplot2 з граничним розподілом на 2 групи, але я не в змозі зробити граничний розподіл для 2 груп. Спасибі
Новачок

@Newbie, цій відповіді 3 роки, як можна застарілі. Використовуйте rdocumentation.org/packages/gtable/versions/0.2.0/topics/gtable або щось подібне.
Lorinc Nyitrai

29

Лише незначна різниця у відповіді BondedDust у загальному дусі граничних показників розподілу.

Едвард Туфте назвав це використання графіків килимів «точковим графіком» і має в VDQI приклад використання ліній осі для позначення діапазону кожної змінної. У моєму прикладі мітки осі та лінії сітки також вказують на розподіл даних. Мітки розміщені за значеннями підсумків п'яти номерів Tukey (мінімум, нижній шарнір, медіана, верхній шарнір, максимум), що дає швидке враження про поширення кожної змінної.

Ці п'ять чисел, таким чином, є числовим представленням боксплоту. Це трохи хитро, тому що нерівномірно розташовані лінії сітки говорять про те, що осі мають нелінійний масштаб (у цьому прикладі вони лінійні). Можливо, було б краще пропустити лінії сітки або змусити їх знаходитись у звичайних місцях, а просто дозволити міткам відображати підсумки п’яти чисел.

x<-rnorm(300)
y<-rt(300,df=10)
xy<-data.frame(x,y)

require(ggplot2); require(grid)
# make the basic plot object
ggplot(xy, aes(x, y)) +        
  # set the locations of the x-axis labels as Tukey's five numbers   
  scale_x_continuous(limit=c(min(x), max(x)), 
                     breaks=round(fivenum(x),1)) +     
  # ditto for y-axis labels 
  scale_y_continuous(limit=c(min(y), max(y)),
                     breaks=round(fivenum(y),1)) +     
  # specify points
  geom_point() +
  # specify that we want the rug plot
  geom_rug(size=0.1) +   
  # improve the data/ink ratio
  theme_set(theme_minimal(base_size = 18))

введіть тут опис зображення


12

Оскільки не було задовольняючого рішення для такого роду сюжетів при порівнянні різних груп, я написав функцію для цього.

Він працює як для згрупованих, так і для негрупованих даних і приймає додаткові графічні параметри:

marginal_plot(x = iris$Sepal.Width, y = iris$Sepal.Length)

введіть тут опис зображення

marginal_plot(x = Sepal.Width, y = Sepal.Length, group = Species, data = iris, bw = "nrd", lm_formula = NULL, xlab = "Sepal width", ylab = "Sepal length", pch = 15, cex = 0.5)

введіть тут опис зображення


9

Я знайшов пакет (ggpubr ), який, здається, працює дуже добре для цієї проблеми, і він розглядає кілька можливостей для відображення даних.

Посилання на пакет знаходиться тут , і за цим посиланням ви знайдете приємний підручник з його використання. Для повноти додаю один із відтворених нами прикладів.

Я вперше встановив пакет (він вимагає devtools)

if(!require(devtools)) install.packages("devtools")
devtools::install_github("kassambara/ggpubr")

У конкретному прикладі відображення різних гістограм для різних груп, він згадується стосовно ggExtra: "Одне обмеження ggExtraполягає в тому, що він не може впоратися з декількома групами в діаграмі розсіяння та граничних ділянках. У R-коді, наведеному нижче, ми надаємо рішення за допомогою cowplotпакета. " У моєму випадку мені довелося встановити останній пакет:

install.packages("cowplot")

І я дотримувався цього коду:

# Scatter plot colored by groups ("Species")
sp <- ggscatter(iris, x = "Sepal.Length", y = "Sepal.Width",
            color = "Species", palette = "jco",
            size = 3, alpha = 0.6)+
border()                                         
# Marginal density plot of x (top panel) and y (right panel)
xplot <- ggdensity(iris, "Sepal.Length", fill = "Species",
               palette = "jco")
yplot <- ggdensity(iris, "Sepal.Width", fill = "Species", 
               palette = "jco")+
rotate()
# Cleaning the plots
sp <- sp + rremove("legend")
yplot <- yplot + clean_theme() + rremove("legend") 
xplot <- xplot + clean_theme() + rremove("legend")
# Arranging the plot using cowplot
library(cowplot)
plot_grid(xplot, NULL, sp, yplot, ncol = 2, align = "hv", 
      rel_widths = c(2, 1), rel_heights = c(1, 2))

Що добре працювало для мене:

Ірис встановив граничні гістограми розсіювача

введіть тут опис зображення


Що вам потрібно зробити, щоб сюжет посередині був квадратним?
JAQuent

Ви маєте на увазі форму крапок? Спробуйте додати аргумент shape = 19у ggscatter. Коди для фігур тут
Альф Паску

7

Ви можете легко створити привабливі розсіювачі з крайовими гістограмами за допомогою ggstatsplot (він також підходить і описує модель):

data(iris)

library(ggstatsplot)

ggscatterstats(
  data = iris,                                          
  x = Sepal.Length,                                                  
  y = Sepal.Width,
  xlab = "Sepal Length",
  ylab = "Sepal Width",
  marginal = TRUE,
  marginal.type = "histogram",
  centrality.para = "mean",
  margins = "both",
  title = "Relationship between Sepal Length and Sepal Width",
  messages = FALSE
)

введіть тут опис зображення

Або трохи привабливіший (за замовчуванням) ggpubr :

devtools::install_github("kassambara/ggpubr")
library(ggpubr)

ggscatterhist(
  iris, x = "Sepal.Length", y = "Sepal.Width",
  color = "Species", # comment out this and last line to remove the split by species
  margin.plot = "histogram", # I'd suggest removing this line to get density plots
  margin.params = list(fill = "Species", color = "black", size = 0.2)
)

введіть тут опис зображення

ОНОВЛЕННЯ:

Як запропонував @aickley, я використовував розроблювальну версію для створення сюжету.


1
Гістограма на осі y неправильна, оскільки це лише копія тієї, що знаходиться на осі x. Це було виправлено лише нещодавно github.com/kassambara/ggpubr/isissue/85 .
aickley

7

Це старе питання, але я подумав, що було б корисно опублікувати оновлення тут, оскільки я нещодавно зіткнувся з цією ж проблемою (дякую Стефані Мюллер за допомогу!).

Відповідь, яка найбільше сприймається за допомогою gridExtra, працює, але вирівнювання осей складно / хитро, як було зазначено в коментарях. Тепер це можна вирішити за допомогою команди ggMarginal з пакету ggExtra як такої:

#load packages
library(tidyverse) #for creating dummy dataset only
library(ggExtra)

#create dummy data
a = round(rnorm(1000,mean=10,sd=6),digits=0)
b = runif(1000,min=1.0,max=1.6)*a
b = b+runif(1000,min=9,max=15)

DummyData <- data.frame(var1 = b, var2 = a) %>% 
  filter(var1 > 0 & var2 > 0)

#plot
p = ggplot(DummyData, aes(var1, var2)) + geom_point(alpha=0.3)
ggMarginal(p, type = "histogram")

введіть тут опис зображення


Щойно зрозумів, що це було опубліковано оригінальним розробником пакунків ggExtra в іншій відповіді. Рекомендую замість цього прийняти відповідь з тієї причини, яку я пояснив вище!
Victoria Auyeung

6

Я спробував ці варіанти, але не був задоволений результатами або брудним кодом, який потрібно було б використати, щоб потрапити туди. Пощастило мені, Томас Лін Педерсен щойно розробив пакет під назвою печворк , який виконує роботу в досить елегантній формі.

Якщо ви хочете створити розсіювач з граничними гістограмами, спершу вам доведеться створити ці три схеми окремо.

library(ggplot2)

x <- rnorm(300)
y <- rt(300, df = 2)
xy <- data.frame(x, y)

plot1 <- ggplot(xy, aes(x = x, y = y)) + 
  geom_point() 

dens1 <- ggplot(xy, aes(x = x)) + 
  geom_histogram(color = "black", fill = "white") + 
  theme_void()

dens2 <- ggplot(xy, aes(x = y)) + 
  geom_histogram(color = "black", fill = "white") + 
  theme_void() + 
  coord_flip()

Єдине, що залишилося зробити - це додати ці сюжети з простим +і вказати макет з функцією plot_layout().

library(patchwork)

dens1 + plot_spacer() + plot1 + dens2 + 
  plot_layout(
    ncol = 2, 
    nrow = 2, 
    widths = c(4, 1),
    heights = c(1, 4)
  ) 

Функція plot_spacer()додає порожній сюжет у верхній правий кут. Усі інші аргументи повинні бути роз'яснювальними.

введіть тут опис зображення

Оскільки гістограми сильно залежать від обраної ширини бін, можна стверджувати, що віддавати перевагу графікам щільності. З деякими невеликими модифікаціями можна отримати, наприклад, для даних відстеження очей гарний сюжет.

library(ggpubr)

plot1 <- ggplot(df, aes(x = Density, y = Face_sum, color = Group)) + 
  geom_point(aes(color = Group), size = 3) + 
  geom_point(shape = 1, color = "black", size = 3) + 
  stat_smooth(method = "lm", fullrange = TRUE) +
  geom_rug() + 
  scale_y_continuous(name = "Number of fixated faces", 
                     limits = c(0, 205), expand = c(0, 0)) + 
  scale_x_continuous(name = "Population density (lg10)", 
                     limits = c(1, 4), expand = c(0, 0)) + 
  theme_pubr() +
  theme(legend.position = c(0.15, 0.9)) 

dens1 <- ggplot(df, aes(x = Density, fill = Group)) + 
  geom_density(alpha = 0.4) + 
  theme_void() + 
  theme(legend.position = "none")

dens2 <- ggplot(df, aes(x = Face_sum, fill = Group)) + 
  geom_density(alpha = 0.4) + 
  theme_void() + 
  theme(legend.position = "none") + 
  coord_flip()

dens1 + plot_spacer() + plot1 + dens2 + 
  plot_layout(ncol = 2, nrow = 2, widths = c(4, 1), heights = c(1, 4))

введіть тут опис зображення

Хоча дані на даний момент не надаються, основні принципи повинні бути зрозумілими.


4

На основі відповіді за допомогою @ alf-pascu, встановлення кожного сюжету вручну та впорядкування їх з cowplotгрантами великої гнучкості як щодо основних, так і граничних сюжетів (порівняно з деякими іншими рішеннями). Розподіл по групах - один із прикладів. Зміна основного сюжету на графік 2D-щільності - ще одна.

Далі створюється розсіювач з (правильно вирівняними) граничними гістограмами.

library("ggplot2")
library("cowplot")

# Set up scatterplot
scatterplot <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
  geom_point(size = 3, alpha = 0.6) +
  guides(color = FALSE) +
  theme(plot.margin = margin())


# Define marginal histogram
marginal_distribution <- function(x, var, group) {
  ggplot(x, aes_string(x = var, fill = group)) +
    geom_histogram(bins = 30, alpha = 0.4, position = "identity") +
    # geom_density(alpha = 0.4, size = 0.1) +
    guides(fill = FALSE) +
    theme_void() +
    theme(plot.margin = margin())
}

# Set up marginal histograms
x_hist <- marginal_distribution(iris, "Sepal.Length", "Species")
y_hist <- marginal_distribution(iris, "Sepal.Width", "Species") +
  coord_flip()

# Align histograms with scatterplot
aligned_x_hist <- align_plots(x_hist, scatterplot, align = "v")[[1]]
aligned_y_hist <- align_plots(y_hist, scatterplot, align = "h")[[1]]

# Arrange plots
plot_grid(
  aligned_x_hist
  , NULL
  , scatterplot
  , aligned_y_hist
  , ncol = 2
  , nrow = 2
  , rel_heights = c(0.2, 1)
  , rel_widths = c(1, 0.2)
)

розсіювач з граничними гістограмами

Щоб замість цього побудувати графік 2D-щільності, просто змініть основний сюжет.

# Set up 2D-density plot
contour_plot <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
  stat_density_2d(aes(alpha = ..piece..)) +
  guides(color = FALSE, alpha = FALSE) +
  theme(plot.margin = margin())

# Arrange plots
plot_grid(
  aligned_x_hist
  , NULL
  , contour_plot
  , aligned_y_hist
  , ncol = 2
  , nrow = 2
  , rel_heights = c(0.2, 1)
  , rel_widths = c(1, 0.2)
)

введіть тут опис зображення


3

Інше рішення з використанням ggpubrі cowplot, але тут ми створюємо сюжети, використовуючи cowplot::axis_canvasта додаючи їх до початкового сюжету за допомогою cowplot::insert_xaxis_grob:

library(cowplot) 
library(ggpubr)

# Create main plot
plot_main <- ggplot(faithful, aes(eruptions, waiting)) +
  geom_point()

# Create marginal plots
# Use geom_density/histogram for whatever you plotted on x/y axis 
plot_x <- axis_canvas(plot_main, axis = "x") +
  geom_density(aes(eruptions), faithful)
plot_y <- axis_canvas(plot_main, axis = "y", coord_flip = TRUE) +
  geom_density(aes(waiting), faithful) +
  coord_flip()

# Combine all plots into one
plot_final <- insert_xaxis_grob(plot_main, plot_x, position = "top")
plot_final <- insert_yaxis_grob(plot_final, plot_y, position = "right")
ggdraw(plot_final)

введіть тут опис зображення


2

На сьогодні існує щонайменше один пакет CRAN, який робить розсіювач за допомогою своїх граничних гістограм.

library(psych)
scatterHist(rnorm(1000), runif(1000))

Зразок сюжету з розсипуHist


0

Ви можете користуватися інтерактивною формою ggExtra::ggMarginalGadget(yourplot) та вибирати між скриньками, сюжетними скрипками, графіками щільності та гістограмами, що легко.

щось схоже на те

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.