Множество графиков с использованием петель в R
Я все еще пытаюсь склонить голову к использованию циклов для построения в R. Я хотел бы построить (любой график для визуализации данных будет делать) столбцы z_1 против z_2 в фрейме данных ниже в соответствии с разными именами в столбце x_1.
x_1 <- c("A1", "A1","A1", "B10", "B10", "B10","B10", "C100", "C100", "C100")
z_1 <- rnorm(10, 70)
z_2 <- rnorm(10, 1.7)
A <- data.frame(x_1, z_1, z_2)
Таким образом, я хотел бы получить три разных сюжета; один для категории A1, один для B10 и другой для C100. Я могу сделать это с использованием трех разных кодов, но я хотел бы иметь возможность использовать цикл или любой другой отдельный код для выполнения всех трех графиков на одной странице. На самом деле, у меня есть большой набор данных (4000 строк) и хотел бы построить пару идентификаторов на странице (например, 5 на странице).
Я надеюсь в этом есть смысл. Спасибо за вашу помощь.
Здесь моя попытка построить их индивидуально:
для A1:
data_A1 <- A[which(A$x_1 == "A1"), ]
plot(data_A1$z_2, data_A1$z_1)
Я также пробовал что-то вроде этого, но получал сообщения об ошибках
for ( i in A$x_1[[i]]){
plot(A[which(A$x_1==A$x_1[[i]]), ], aspect = 1)
}
Ответы
Ответ 1
Простой подход с петлями будет
for (cat in unique(x_1)){
d <- subset(A, x_1 == cat)
plot(d$z_1, d$z_2)
}
unique(x_1)
получает все уникальные значения x_1
. Затем для каждого из этих значений получаем соответствующее подмножество и используйте это подмножество для построения графика.
Ответ 2
Просто чтобы понять, почему ваш исходный код не работал:
Настройка данных прекрасно работает
x_1 <- c("A1", "A1", "A1", "B10", "B10", "B10","B10", "C100", "C100", "C100")
z_1 <- rnorm(10, 70)
z_2 <- rnorm(10, 1.7)
A <- data.frame(x_1, z_1, z_2)
Индивидуальный участок работает отлично, но как я уже сказал в комментариях, то, which
не является необходимым
data_A1 <- A[which(A$x_1 == "A1"), ] # your way
plot(data_A1$z_2, data_A1$z_1)
data_A1 <- A[A$x_1 == "A1", ] # deleting which() makes it cleaner
with(data_A1, plot(z_2, z_1)) # you can also use with() to save typing
Теперь цикл for. Давайте рассмотрим простой цикл цикла в R (довольно близко к примеру в ?"for"
):
for (i in 1:5) {
print(1:i)
}
Довольно просто, 1:5
- c(1, 2, 3, 4, 5)
, поэтому сначала i
равно 1
, затем 2
и т.д. В вашей первой строке есть проблема в этой первой строке:
for (i in A$x_1[[i]]) { ## already a problem
Сначала i
является A$x_1[[i]]
? Это не сработает, i
еще не определился. Кроме того, A$x_1
- это вектор, а не список, поэтому вы не должны использовать [[
для его подмножества. Но мы еще не хотим подмножества, нам нужен вектор значений, которые i
должен принять. В этом случае мы хотим for (i in c("A1", "B10", "C100"))
, но мы также хотим сделать это программно, вместо того, чтобы печатать все разные возможности. Есть пара общих способов получить это:
unique(A$x_1) # as in Mark solution
levels(A$x_1) # works because A$x_1 is a factor
Мы можем либо из этих выражений после in
. Я изменил ваш [[
на [
в разговоре сюжета. [[
только для списков. Я также вынул ненужное, which()
for (i in unique(A$x_1)) { # this line is good
plot(A[A$x_1==A$x_1[i], ], aspect = 1) # still a problem
}
Напомним, какие значения i
принимаю: "A1"
, "B10"
, "C100"
. Что A$x_1 == A$x_1["A1"]
собирается дать? Ничего полезного.
for (i in unique(A$x_1)) {
plot(A[A$x_1 == i, ], aspect = 1) # getting there
}
Вышеприведенный код что-то делает, и он опрятен, но это не то, что вы хотите. Там есть куча предупреждений, и все они говорят нам, что aspect
не является допустимым аргументом, поэтому мы его удалим. Глядя на сюжет, вы увидите, что он рисует 3 переменные, потому что мы не сказали, что поставить на оси x и y.
for (i in unique(A$x_1)) {
plot(A[A$x_1==i, "z_2"], A[A$x_1==i, "z_1"]) # z_2 on x, z_1 on y
} # Works!!!
Обратите внимание, что это почти идентично ответу Марка. Вам не нужно использовать i
и j
для циклов, он использовал cat
. Хорошей практикой является использование более описательного имени. Теперь позвольте немного подумать:
for (i in unique(A$x_1)) {
plot(A[A$x_1==i, "z_2"], A[A$x_1==i, "z_1"],
xlim = range(A$z_2), ylim = range(A$z_1), # base the axes on full data range
main = paste("Plot of", i)) # Give each a title
}
В следующий раз: не забывайте, что вы можете запускать крошечные фрагменты кода, чтобы узнать, что они собой представляют. Если у вас есть строка, подобная for (i in A$x_1[[i]])
что вы не уверены, правильно ли это, введите A$x_1[[i]]
на консоли, надеюсь, это поможет вам понять, что вы не определили i
, поэтому вы измените его на
for (i in A$x_1)
то вы запустите A$x_1
и поймете, что длина равна 10. Вам нужны 3 графика, а не 10, поэтому вам нужно, чтобы i
принимал 3 значения, все они разные и т.д.
Ответ 3
Возможно, вам не нужна петля. Попробуйте использовать ggplots facet_grid(). Вот документация, полная примеров.
library(ggplot2)
library(reshape2)
melted_a <- melt(A)
ggplot(melted_a, aes(variable, value)) +
geom_jitter() +
facet_grid(. ~ x_1)
ggplot(melted_a, aes(variable, value)) +
geom_jitter() +
facet_grid(variable ~ x_1)
Редактировать Возможно, это решает эту проблему. Но если вам нужно сделать много графиков с аналогичной структурой, вы можете сделать функцию и использовать aes_string()
вместо aes()
. Примечание. Я не специалист по написанию функций, поэтому, возможно, кто-то может редактировать и улучшать его. (не испытано)
ggplot_fun <- function(data, x, y, rowfacet, colfacet, ...){
p <- ggplot(data, aes_string(x, y))
p <- p + geom_jitter()
p <- p + facet_grid(as.formula(sprintf("%s ~ %s", rowfacet, colfacet))
}
ggplot_fun(melted_a, variable, value, variable, x_1)
Идея взята из этого вопроса.
Ответ 4
Вы также настраиваете данные, например, как я здесь...
Если мне нужны датчатые графики и xlab, ylab и заголовок сюжета, чтобы иметь конкретные детали...
for ( i in 1:length(unique(wheeldata$Date)) ){
d <- subset( wheeldata, Date == unique ( wheeldata$Date )[i] )
plot(d$X, d$Y, xlab = "X", ylab = "Y", main = paste0("Date: ", unique(d$Date)) )
}