Элегантный способ выбрать цвет для определенного сегмента линейного графика?

Для списка из n пар координат x,y есть ли способ построить линию между разными точками определенного цвета?

Решение, которое я реализовал до сих пор, состоит в том, чтобы использовать не функцию plot, а lines, выбирая диапазон, для которого мне нужен цвет. Вот пример:

x <- 1:100
y <- rnorm(100,1,100)
plot(x,y ,type='n')
lines(x[1:50],y[1:50], col='red')
lines(x[50:60],y[50:60], col='black')
lines(x[60:100],y[60:100], col='red')

Есть ли более простой способ сделать это?


person pedrosaurio    schedule 12.10.2011    source источник


Ответы (5)


Да, один из способов сделать это — использовать ggplot.

ggplot требует, чтобы ваши данные были в формате data.frame. В этом data.frame я добавляю столбец col, который указывает желаемый цвет. Затем график строится с ggplot, geom_line и scale_colour_identity, поскольку переменная col уже является цветом:

library(ggplot2)

df <- data.frame(
  x = 1:100,
  y = rnorm(100,1,100),
  col = c(rep("red", 50), rep("black", 10), rep("red", 40))
)

ggplot(df, aes(x=x, y=y)) + 
  geom_line(aes(colour=col, group=1)) + 
  scale_colour_identity()

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

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

df <- data.frame(
  x = 1:100,
  y = rnorm(100,1,100)
)

ggplot(df, aes(x=x, y=y)) + geom_line(aes(colour=x))

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


И если вы настаиваете на использовании базовой графики, то используйте segments следующим образом:

df <- data.frame(
  x = 1:100,
  y = rnorm(100,1,100),
  col = c(rep("red", 50), rep("black", 10), rep("red", 40))
)

plot(df$x, df$y, type="n")
for(i in 1:(length(df$x)-1)){
  segments(df$x[i], df$y[i], df$x[i+1], df$y[i+1], col=df$col[i])
}

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

person Andrie    schedule 12.10.2011
comment
Что, нет lattice решения? ;) - person joran; 13.10.2011
comment
Что касается решения для сегментов, оно использует цикл for, что аналогично многократному вызову lines, как это делал OP. Вы могли бы использовать более элегантный способ: with(df, segments(head(x, -1), head(y, -1), x[-1], y[-1], rep(c("red", "blue", "green"), c(50, 10, 40)))). - person Tomas; 13.10.2011
comment
@joran: Я знаю, что вы пошутили, но с решеткой здесь, похоже, не расправляются. Смотрите мой ответ. - person Aaron left Stack Overflow; 13.10.2011
comment
Спасибо @Andrie, пример градиента также будет очень полезен для моей работы. - person pedrosaurio; 14.10.2011

Для @joran и других фанатов решетки...

xyplot(y~x, data=df, panel=function(x,y,subscripts, groups, ...) {
  for(k in seq_len(length(subscripts)-1)) {
    i <- subscripts[k]
    j <- subscripts[k+1]
    panel.segments(df$x[i], df$y[i], df$x[j], df$y[j], col=df$col[i])
  }
})

К сожалению, я не знаю красивого способа сделать это, поэтому он в основном заключает базовое решение в функцию панели. Вышеприведенное работает правильно при использовании | для разделения на группы, например, y~x|a с переменной a, как здесь:

df <- data.frame(
  x = 1:100,
  y = rnorm(100,1,100),
  col = c(rep("red", 50), rep("black", 10), rep("red", 40)),
  a = 1:2
)

Чтобы использовать group=, вам также потребуется следующее:

xyplot(y~x, group=a, data=df, panel=function(x,y,subscripts, groups, ...) {
  if(missing(groups)) { groups <- rep(1, length(subscripts)) }
  grps <- split(subscripts, groups)
  for(grp in grps) {
    for(k in seq_len(length(grp)-1)) {
      i <- grp[k]
      j <- grp[k+1]
      panel.segments(df$x[i], df$y[i], df$x[j], df$y[j], col=df$col[i])
    }
  }
})
person Aaron left Stack Overflow    schedule 13.10.2011

Однострочник с использованием только базовых библиотек:

segments(head(x, -1), head(y, -1), x[-1], y[-1], rep(c("red", "black", "red"), c(49, 10, 40)))

(вдохновленный использование Андри сегментов, см. его пост и обсуждение там)

Интересно, что его можно было бы сократить до этого:

segments(head(x, -1), head(y, -1), x[-1], y[-1], rep(c("red", "black"), c(49, 10)))
person Tomas    schedule 13.10.2011

Если вы хотите установить цвет на основе значений y, а не значений x, используйте plotrix::clplot . Это фантастическая, замечательная, суперпупер функция. Отказ от ответственности: я написал это :-). Таким образом, clplot() выделяет области ваших данных, где y принимает указанные диапазоны значений. В качестве примечания: вы можете расширить комментарий Чейза следующим образом:

plot(x,y,t='p', col=colorlist[some_function_of_x]) 

где список цветов — это вектор цветов, названий цветов или чего-то еще, и вы выбираете алгоритм, который соответствует вашим потребностям. Первый из сюжетов Андри можно было построить с помощью
colorlist=c('red','black')
и
plot(x,y,t='p', col=colorlist[1+(abs(x-55)<=5)])

person Carl Witthoft    schedule 12.10.2011

В базовой библиотеке я так не думаю (однако я не могу говорить о ggplot и т. д.). Глядя на функцию lines и пытаясь указать col как вектор...: это не работает. Я бы сделал так же, как ты.

EDIT после обсуждения с Andrie и вдохновленный его сообщения: вы можете использовать segments(), чтобы сделать это за один вызов, смотрите обсуждение там.

person Tomas    schedule 12.10.2011
comment
Интересно, что графики типа p действительно работают таким образом. Рассмотрим: plot(x,y, type = "p", col = ifelse(x < 10, "pink", "green")). - person Chase; 12.10.2011
comment
Базовая графика: help(plotrix::color.scale.lines) - person IRTFM; 12.10.2011
comment
@DWin: это я не вызываю базу (для этого вам нужно загрузить пакет plotrix ...) - person Tomas; 12.10.2011
comment
Я добавил к своему ответу базовое решение R: просто используйте segments вместо строк - person Andrie; 12.10.2011
comment
@Andrie, да, но вы используете цикл for - это было бы возможно и с линиями. - person Tomas; 13.10.2011