geom_rect не учитывает ограничения x и y в цикле for - PullRequest
1 голос
/ 08 ноября 2019

Я пытаюсь использовать geom_rect в цикле for, но это не соответствует моим ограничениям. Это происходит, если я вызываю это вне контекста цикла for. Это ошибка? Или я что-то не понимаю в geom_rect? outPlot_free и outPlot1 должны быть идентичны (так как .2 = .2 / 1), но прямоугольники в outPlot1 усекаются, и, что интересно, они идентичны outPlot2, outPlot3 и outPlot4.

library('ggplot2')
library('ggrepel')

sum_df <- data.frame(matrix(NA, nrow=10, ncol=3))
colnames(sum_df) <- c("Variable", "Male", "Female")
sum_df$Variable <- c("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")

covar = .7*.1*.1
Sigma = matrix(ncol=2,nrow=2,c(.2^2,covar,covar,.2^2))
temp = eigen(Sigma)
SqrtSigma = temp$vectors%*%diag(sqrt(temp$values))%*%t(temp$vectors)
XYvec = c(0,0) + SqrtSigma%*%rnorm(2)

for(i in 1:10){
  XYvec = c(0,0) + SqrtSigma%*%rnorm(2)
  sum_df$Female[i] = XYvec[1]
  sum_df$Male[i] = XYvec[2]
}

outPlot_free <- ggplot(sum_df, aes(x=Male, y=Female)) + theme_minimal() + 
  geom_rect(aes(xmin=-.2, xmax=.2, ymin=-Inf, ymax=Inf), fill="grey97", color=NA, alpha=.5, size=0) +
  geom_rect(aes(ymin=-.2, ymax=.2, xmin=-Inf, xmax=Inf), fill="grey97", color=NA, alpha=.5, size=0) +
  geom_point() + geom_text_repel(aes(label=Variable)) +
  scale_x_continuous(limits=c(-1, 1), breaks=round(seq(-1, 1, .1), digits=2)) + 
  scale_y_continuous(limits=c(-1, 1), breaks=round(seq(-1, 1, .1), digits=2)) +
  geom_abline(intercept=0, slope=1, linetype="dotdash", alpha=.5) +
  scale_color_manual(values=c("grey60", "black")) + xlab("Female") + ylab("Male") +
  geom_hline(yintercept=.2, linetype="dashed", color="slateblue") + geom_vline(xintercept=.2, linetype="dashed", color="slateblue") +
  geom_hline(yintercept=-.2, linetype="dashed", color="slateblue") + geom_vline(xintercept=-.2, linetype="dashed", color="slateblue") 

for (q in 1:4) {
  covar = .7*.1*.1
  Sigma = matrix(ncol=2,nrow=2,c(.2^2,covar,covar,.2^2))
  temp = eigen(Sigma)
  SqrtSigma = temp$vectors%*%diag(sqrt(temp$values))%*%t(temp$vectors)
  XYvec = c(0,0) + SqrtSigma%*%rnorm(2)

  for(i in 1:10){
    XYvec = c(0,0) + SqrtSigma%*%rnorm(2)
    sum_df$Female[i] = XYvec[1]
    sum_df$Male[i] = XYvec[2]
  }


  outPlot <- ggplot(sum_df, aes(x=Male, y=Female)) + theme_minimal() + 
    geom_rect(aes(xmin=-.2/q, xmax=.2/q, ymin=-Inf, ymax=Inf), fill="grey97", color=NA, alpha=.5, size=0) +
    geom_rect(aes(ymin=-.2/q, ymax=.2/q, xmin=-Inf, xmax=Inf), fill="grey97", color=NA, alpha=.5, size=0) +
    geom_point() + geom_text_repel(aes(label=Variable)) +
    scale_x_continuous(limits=c(-1, 1), breaks=round(seq(-1, 1, .1), digits=2)) + 
    scale_y_continuous(limits=c(-1, 1), breaks=round(seq(-1, 1, .1), digits=2)) +
    geom_abline(intercept=0, slope=1, linetype="dotdash", alpha=.5) +
    scale_color_manual(values=c("grey60", "black")) + xlab("Female") + ylab("Male") +
    geom_hline(yintercept=.2, linetype="dashed", color="slateblue") + geom_vline(xintercept=.2, linetype="dashed", color="slateblue") +
    geom_hline(yintercept=-.2, linetype="dashed", color="slateblue") + geom_vline(xintercept=-.2, linetype="dashed", color="slateblue") 

  assign(paste0("outPlot", q), outPlot)
}

outPlot_free

outPlot1

outPlot2

outPlot3

outPlot4

Создано в 2019-11-09 представительным пакетом (v0.3.0)

outPlot_free и outPlot1 должны быть идентичны, за исключением точек на графике, поскольку они были смоделированы независимо.

1 Ответ

0 голосов
/ 09 ноября 2019

Проблема, с которой вы сталкиваетесь - это ленивое вычисление в R. Это распространенная проблема при написании кода, содержащего циклы, особенно если вы подходите к языку с процедурного мышления. Подробнее см., Например, здесь: http://adv -r.had.co.nz / Functions.html

В следующем примере первым является то, что вы делаете (вэффект), и второе - это то, что вы должны делать.

# doesn't work as expected, as the variable i in the function call
# is evaluated only after the loop is run
x <- list()
for (i in 1:3) {
  x[[i]] <- function() {i}
}
x[[1]]()
#> [1] 3
x[[2]]()
#> [1] 3
x[[3]]()
#> [1] 3

# by writing a function generator, we can bind the variable i
# to the specific function we're generating in each iteration
# of the loop
x <- list()
f_generator <- function(i) {
  force(i)
  function() {i}
}
for (i in 1:3) {
  x[[i]] <- f_generator(i)
}
x[[1]]()
#> [1] 1
x[[2]]()
#> [1] 2
x[[3]]()
#> [1] 3

Создано в 2019-11-09 пакетом prex (v0.3.0)

В контексте вашего кода напишите функцию, которая генерирует график, вызовите force() для всех аргументов этой функции, а затем внутри цикла for() вызовите эту функцию для создания определенных объектов графика. тебе нужно. См. Следующий пример.

library(ggplot2)
library(cowplot)

# this doesn't work, the line in the first plot should be placed
# at y = 1 but is placed at y = 2
plots <- list()
for (i in 1:2) {
  data <- data.frame(x = c(0, 1))
  plots[[i]] <- ggplot(data, aes(x, y = i)) + geom_line() + ylim(0, 3)
}

plot_grid(plotlist = plots, labels = c(1:2))

# this does work
plots <- list()
plot_fun <- function(i) {
  force(i)
  data <- data.frame(x = c(0, 1))
  ggplot(data, aes(x, y = i)) + geom_line() + ylim(0, 3)
}
for (i in 1:2) {
  plots[[i]] <- plot_fun(i)
}

plot_grid(plotlist = plots, labels = c(1:2))

И, наконец, когда вы написали функцию, которая генерирует ваши графики,идиоматический подход в R состоит в том, чтобы не писать цикл for, а вместо этого использовать lapply() или map(). Оказывается, что если вы привыкнете использовать эти функции вместо циклов for, вы с гораздо меньшей вероятностью столкнетесь с проблемой, с которой сталкиваетесь, поскольку R не является процедурным языком.

# this replaces the for loop
plots <- lapply(1:2, plot_fun)

plot_grid(plotlist = plots, labels = c(1:2))

Создано в 2019-11-09 пакетом Представить (v0.3.0)

...