Biplot или построение частичного RDA с помощью ggplot2. что не работает со стрелками, вычисленными с помощью var_fit?

Я пытаюсь построить частичный RDA и некоторые параметры окружающей среды поверх этого.

Я последовал идее этого поста построить график RDA с помощью ggplot2. Я также нашел еще один пост, который вместо этого позволяет строить графики с использованием базы R. Я изменил его, добавив стрелки для параметров окружающей среды.

Мне известно о существовании ggvegan, но даже используя fortify, как было предложено, я не могу добиться совпадения двух графиков, в частности длины стрелок. Что я делаю не так?

Если я посмотрю на содержимое arrows, то окажется, что ggplot2 выполняет свою работу. Так это база R неправильно рисует стрелки?

Вот код:

library("ggplot2")
library("phyloseq")
library("vegan")
library("scales")

# load data
data(soilrep)

# add some random env vars
meta_data <- as.data.frame(sample_data(soilrep))
meta_data$env_var_one <- sample(100, nrow(meta_data), replace=T)
meta_data$env_var_two <- sample(100, nrow(meta_data), replace=T)
meta_data$env_var_three <- sample(100, nrow(meta_data), replace=T)

set.seed(1)

# compute partial RDA using formula
prda <- ordinate(soilrep, "RDA", formula=as.formula("soilrep~Treatment+warmed+Condition(clipped)"))

# compute fitting
var_fit <- envfit(prda, meta_data[, c("env_var_one", "env_var_two", "env_var_three")], perm=1000, na.rm=TRUE)

# define shapes for treatment legend
shape_legend <- c(15, 16, 17, 18)
# define shapes for treatment in plot
shape <- c(21, 22, 23, 24)
names(shape) <- unique(meta_data$Treatment)

# define colours for warmed
warmed_palette <- hue_pal()(2)[c(2, 1)]
names(warmed_palette) <- c("no", "yes")

# base plotting
dev.new()

par(mar=c(5, 5, 4, 2))
pl <- plot(prda, type = "none", scaling=1)
with(meta_data, plot(var_fit, add=T, col = "black", p.max=0.99))
with(meta_data, points(prda, "sites", pch=shape[Treatment], bg=warmed_palette[warmed], col = "black", scaling=1))
with(meta_data, legend("topright", legend=levels(warmed), fill=warmed_palette, title = "warmed"))
with(meta_data, legend("topleft", legend=levels(Treatment), pch=shape_legend, title = "Treatment"))

# create data frame for ggplot plotting
# get scores for sites
ggplot_rda <- as.data.frame(vegan::scores(prda, display = "sites"))

# add some columns for plotting purposes
ggplot_rda$label <- rownames(ggplot_rda)
ggplot_rda$treatment <- meta_data$Treatment
ggplot_rda$warmed <- meta_data$warmed

# as factor
ggplot_rda$treatment <- factor(ggplot_rda$treatment, levels=unique(ggplot_rda$treatment))
ggplot_rda$warmed <- factor(ggplot_rda$warmed, levels=unique(ggplot_rda$warmed))

# get scores for arrows
arrows <- as.data.frame(vegan::scores(var_fit, display = "vectors"))
arrows$names <- rownames(arrows)

# ggplotting
dev.new()

ggplot(ggplot_rda) +
    geom_point(mapping = aes(x=RDA1, y=RDA2, fill=warmed, shape=treatment)) +
    coord_fixed(ratio=1) +
    scale_shape_manual(values=shape) +
    scale_fill_manual(values=warmed_palette) +
    geom_segment(data=arrows, aes(x = 0, xend = RDA1, y = 0, yend = RDA2), arrow = arrow(length = unit(0.25, "cm"))) +
    geom_text(data=arrows, aes(x = RDA1, y = RDA2, label = names))+
    guides(fill=guide_legend(override.aes=list(shape=21)))

Вы там не вписываете частичную RDA... И я не понимаю, почему вы здесь используете phyloseq::ordinate() вместо того, чтобы просто вызвать rda()...

Gavin Simpson 28.06.2024 10:42

@GavinSimpson, я использовал функцию ординаты, так как использую объект phyloseq, просто для удобства. Могу ли я спросить вас, что вы имеете в виду, когда говорите, что мне не подходит частичная RDA?

gabt 28.06.2024 10:46

Частичный RDA – это метод, в котором вы удаляете (частично) влияние одной или нескольких ковариат, а затем оцениваете RDA по остаткам. Вы соответствуете частичному RDA, используя Condition() в формуле, например y ~ x1 + x2 + Condition(block + observer), чтобы оценить влияние ковариат x1 и x2 на ответ y после контроля (удаления; частичного исключения) эффектов block и observer. В коде вы не делаете ничего подобного.

Gavin Simpson 28.06.2024 10:48

Обратите внимание, что в моем ответе я не делаю последнее, что делает plot.envfit, а именно умножаю координаты стрелок после масштабирования на sqrt(r2) на некоторый множитель, чтобы векторы заполнили пространство графика. Я упоминаю, что plot.envfit делает это, но я не показал это в коде (хотя один из других ответов делает это)

Gavin Simpson 28.06.2024 10:51

@GavinSimpson, ты совершенно прав, мне очень жаль. я скопировал неправильный код с некоторыми ошибками. теперь это частичный RDA с правильными переменными в формуле.

gabt 28.06.2024 10:53

@GavinSimpson, ок, думаю, у меня есть решение. однако ответ, в котором используется множитель, я думаю, что он использует его неправильно. они умножают значения дважды: один при создании кадра данных, другой при построении графика с помощью ggplot2. я думаю, что умножение в ggplot2 неверно. см. прикрепленный ответ.

gabt 28.06.2024 11:25
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
60
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Частичное решение: как упоминалось в комментарии, чтобы имитировать поведение базы R, необходимо вычислить коэффициент умножения, чтобы правильно масштабировать стрелки. Это также было предложено в этом ответе из вопроса, ссылка на который есть в исходном посте.

Однако есть еще проблема, которую я не решил. Для базы R требуется коэффициент масштабирования, который может быть равен 1 или 2. Кажется, мое решение работает только с scaling = 2, как я показываю ниже. Я не уверен, почему scaling = 1 до сих пор не работает, но я веду расследование.

Обновлено: следуя комментарию ниже, я думаю, что решил проблему масштабирования. Кажется, теперь все работает:

# load libraries
library("ggplot2")
library("phyloseq")
library("vegan")
library("scales")

# set scaling factor
scaling_factor <- 1

graphics.off()

# load data
data(soilrep)

# add some random env vars
set.seed(0)
meta_data <- as(sample_data(soilrep), "data.frame")
meta_data$env_var_one <- sample(100, nrow(meta_data), replace=T)
meta_data$env_var_two <- sample(100, nrow(meta_data), replace=T)
meta_data$env_var_three <- sample(100, nrow(meta_data), replace=T)

# compute partial RDA using formula
prda <- ordinate(soilrep, "RDA", formula=as.formula("soilrep~Treatment+warmed+Condition(clipped)"))

# compute fitting
var_fit <- envfit(prda, meta_data[, c("env_var_one", "env_var_two", "env_var_three")], perm=1000, na.rm=TRUE, scaling=scaling_factor)

# define shapes for treatment legend
shape_legend <- c(15, 16, 17, 18)
# define shapes for treatment in plot
shape <- c(21, 22, 23, 24)
names(shape) <- unique(meta_data$Treatment)

# define colours for warmed
warmed_palette <- hue_pal()(2)[c(2, 1)]
names(warmed_palette) <- c("no", "yes")

# set levels
# as factor
meta_data$Treatment <- factor(meta_data$Treatment, levels=unique(meta_data$Treatment))
meta_data$warmed <- factor(meta_data$warmed, levels=unique(meta_data$warmed))

# base plotting
dev.new()

plot(prda, type = "none", scaling=scaling_factor)
with(meta_data, plot(var_fit, add=T, col = "black", p.max=0.99))
with(meta_data, points(prda, "sites", pch=shape[Treatment], bg=warmed_palette[warmed], col = "black", scaling=scaling_factor))
with(meta_data, legend("topright", legend=levels(warmed), fill=warmed_palette, title = "warmed"))
with(meta_data, legend("topleft", legend=levels(Treatment), pch=shape_legend, title = "Treatment"))

# create data frame for ggplot plotting
# get scores for sites
ggplot_rda <- as.data.frame(vegan::scores(prda, display = "sites", scaling=scaling_factor))

# add some columns for plotting purposes
ggplot_rda$label <- rownames(ggplot_rda)
ggplot_rda$treatment <- meta_data$Treatment
ggplot_rda$warmed <- meta_data$warmed

# as factor
ggplot_rda$treatment <- factor(ggplot_rda$treatment, levels=unique(ggplot_rda$treatment))
ggplot_rda$warmed <- factor(ggplot_rda$warmed, levels=unique(ggplot_rda$warmed))

# get multiplying factors for the arrows
arrow_factor <- ordiArrowMul(var_fit)
# get arrows and scale them
arrows <- as.data.frame(vegan::scores(var_fit, display = "vectors"), scaling=scaling_factor) * arrow_factor
arrows$names <- rownames(arrows)

# select arrows which have meaningful p-value, if needed
# for this example, we keep a threshold of 0.99
threshold_p <- 0.99
signif_arrows <- names(which(var_fit$vectors$pvals<=threshold_p))
# subset
print_arrows <- arrows[signif_arrows, ]

# ggplotting
dev.new()

ggplot(ggplot_rda) +
    geom_point(mapping = aes(x=RDA1, y=RDA2, fill=warmed, shape=treatment)) +
    coord_fixed() +
    scale_shape_manual(values=shape) +
    scale_fill_manual(values=warmed_palette) +
    geom_segment(data=print_arrows, aes(x = 0, xend = RDA1, y = 0, yend = RDA2), arrow = arrow(length = unit(0.25, "cm")), colour = "black") +
    geom_text(data=print_arrows, aes(x = RDA1, y = RDA2, label = names))+
    guides(fill=guide_legend(override.aes=list(shape=21))) +
    theme_bw()

Обновлено:

дальнейшее дополнение, поскольку экспорт кажется сложным из-за некоторых других факторов, которые вводятся plot.envfit, как сообщается на странице справки. Проблема в том, что при построении графика с базой R есть параметр, т. е. arrow.mul, который, я думаю, должен быть установлен как коэффициент стрелки, используемый для умножения. Из help(envfit):

Длина стрелок для подобранных векторов регулируется автоматически. для физического размера графика, а длины стрелок не могут быть сравнивали по участкам. Для аналогичного масштабирования стрелок необходимо явно установите аргумент «arrow.mul» в команде «plot»; видеть «ordiArrowMul» и «ordiArrowTextXY».

Итак, это решение, которое даст один и тот же график, независимо от того, какой метод (базовый R или ggplot2) используется:

# load libraries
library("ggplot2")
library("phyloseq")
library("vegan")
library("scales")

# set scaling factor
scaling_factor <- 1

# load data
data(soilrep)

# add some random env vars
set.seed(0)
meta_data <- as(sample_data(soilrep), "data.frame")
meta_data$env_var_one <- sample(100, nrow(meta_data), replace=T)
meta_data$env_var_two <- sample(100, nrow(meta_data), replace=T)
meta_data$env_var_three <- sample(100, nrow(meta_data), replace=T)

# export metadata
write.table(meta_data, "metadata_prints_to_screen.tsv", quote=F, row.names=F, sep = "\t")

# compute partial RDA using formula
prda <- ordinate(soilrep, "RDA", formula=as.formula("soilrep~Treatment+warmed+Condition(clipped)"))

# compute fitting
var_fit <- envfit(prda, meta_data[, c("env_var_one", "env_var_two", "env_var_three")], perm=1000, na.rm=TRUE, scaling=scaling_factor)

# get multiplying factors for the arrows
arrow_factor <- ordiArrowMul(var_fit)

# define shapes for treatment legend
shape_legend <- c(15, 16, 17, 18)
# define shapes for treatment in plot
shape <- c(21, 22, 23, 24)
names(shape) <- unique(meta_data$Treatment)

# define colours for warmed
warmed_palette <- hue_pal()(2)[c(2, 1)]
names(warmed_palette) <- c("no", "yes")

# set levels
# as factor
meta_data$Treatment <- factor(meta_data$Treatment, levels=unique(meta_data$Treatment))
meta_data$warmed <- factor(meta_data$warmed, levels=unique(meta_data$warmed))

# new dev
dev.new()

# plot with base R
plot(prda, type = "none", scaling=scaling_factor)
with(meta_data, plot(var_fit, add=T, col = "black", p.max=0.99, arrow.mul=arrow_factor))
with(meta_data, points(prda, "sites", pch=shape[Treatment], bg=warmed_palette[warmed], col = "black", scaling=scaling_factor))
with(meta_data, legend("topright", legend=levels(warmed), fill=warmed_palette, title = "warmed"))
with(meta_data, legend("topleft", legend=levels(Treatment), pch=shape_legend, title = "Treatment"))

# create data frame for ggplot plotting
# get scores for sites
ggplot_rda <- as.data.frame(vegan::scores(prda, display = "sites", scaling=scaling_factor))

# add some columns for plotting purposes
ggplot_rda$label <- rownames(ggplot_rda)
ggplot_rda$treatment <- meta_data$Treatment
ggplot_rda$warmed <- meta_data$warmed

# as factor
ggplot_rda$treatment <- factor(ggplot_rda$treatment, levels=unique(ggplot_rda$treatment))
ggplot_rda$warmed <- factor(ggplot_rda$warmed, levels=unique(ggplot_rda$warmed))

# get arrows and scale them
arrows <- as.data.frame(vegan::scores(var_fit, display = "vectors"), scaling=scaling_factor) * arrow_factor
arrows$names <- rownames(arrows)

# select arrows which have meaningful p-value, if needed
# for this example, we keep a threshold of 0.99
threshold_p <- 0.99
signif_arrows <- names(which(var_fit$vectors$pvals<=threshold_p))
# subset
print_arrows <- arrows[signif_arrows, ]

# make the plot
export_ggplot <- ggplot(ggplot_rda) +
    geom_point(mapping = aes(x=RDA1, y=RDA2, fill=warmed, shape=treatment)) +
    coord_fixed() +
    scale_shape_manual(values=shape) +
    scale_fill_manual(values=warmed_palette) +
    geom_segment(data=print_arrows, aes(x = 0, xend = RDA1, y = 0, yend = RDA2), arrow = arrow(length = unit(0.25, "cm")), colour = "black") +
    geom_text(data=print_arrows, aes(x = RDA1, y = RDA2, label = names))+
    guides(fill=guide_legend(override.aes=list(shape=21))) +
    theme_bw()

# new dev
dev.new()
plot(export_ggplot)

Я подозреваю, что тот факт, что scaling = 1 у вас не работает, объясняется тем, что вы указываете это масштабирование только один раз. Когда вы изначально извлекаете оценки для формирования ggplot_rda, вы не указываете используемое масштабирование, поэтому используется значение по умолчанию, то есть 2. Но это также означает, что весь остальной ваш код неверен, поскольку вам нужно указывать scaling во всех вызовах plot()points() и т. д.

Gavin Simpson 28.06.2024 12:58

@GavinSimpson Я добавил это в код. кажется, теперь это работает.

gabt 28.06.2024 15:52

Другие вопросы по теме