Seeking help with line types by group in spaghetti plot

1,411 views
Skip to first unread message

canoe.moore

unread,
Aug 2, 2010, 1:18:57 PM8/2/10
to ggplot2
Hello,

I'm trying to create a spaghetti plot with line types that vary by
group, but I keep getting an error. The following examples show that
it's possible.

#http://r.789695.n4.nabble.com/color-lines-by-group-membership-in-
spaghetti-plot-td2216888.html
library(nlme)
library(ggplot2)
p <- ggplot(data=Orthodont, aes(x=age, y=distance, group=Subject,
linetype=Sex))
p + geom_line()

#http://had.co.nz/ggplot2/geom_path.html
x <- seq(0.01, .99, length=100)
df <- data.frame(x = rep(x, 2), y = c(qlogis(x), 2 * qlogis(x)),
group= rep(c("a","b"), each=100))
p <- ggplot(df, aes(x=x, y=y, group=group))
p + geom_line(aes(linetype=group))

When I try to produce similar plots with my own data (sorry I can't
provide the data), I get the following error message:
Error: geom_path: If you are using dotted or dashed lines, colour,
size and linetype must be constant over the line

However, as you can see from my code, I'm not varying color or size
over the line:
g <- ggplot(data=data.plot, aes(x=Grade, y=Score.1, groups=StudentID))
g + geom_line(aes(linetype=FRP.Flag))

Thanks in advance for your help.

Regards,
Chris

P.S. The following code, which varies color by group, works fine:
g <- ggplot(data=data.plot, aes(x=Grade, y=Score.1, groups=StudentID))
g + geom_line(aes(color=FRP.Flag))

Brandon Hurr

unread,
Aug 2, 2010, 1:59:03 PM8/2/10
to canoe.moore, ggplot2
Chris, 

I'm not sure what the problem is exactly yet, but you should start by fixing your syntax. "group= x" != "groups= x". Also, I think linetype goes outside the aes() call. 

Brandon


--
You received this message because you are subscribed to the ggplot2 mailing list.
Please provide a reproducible example: http://gist.github.com/270442

To post: email ggp...@googlegroups.com
To unsubscribe: email ggplot2+u...@googlegroups.com
More options: http://groups.google.com/group/ggplot2

Brandon Hurr

unread,
Aug 2, 2010, 2:00:50 PM8/2/10
to canoe.moore, ggplot2
My bad, not syntax, spelling. 

Brandon Hurr

unread,
Aug 2, 2010, 2:17:24 PM8/2/10
to canoe.moore, ggplot2
Please ignore me, groups is the same as group and linetype goes in the aes as you have it if you want it to be dependent on a factor.

You sure you can't change the names and numbers in your data and share, it's hard to play with something that's broken when you only have 1/2 the picture? 

Christopher Moore

unread,
Aug 2, 2010, 2:17:10 PM8/2/10
to Brandon Hurr, ggplot2
Hi Brandon,

Using "group=" and "groups=" doesn't appear to make a difference.  When the "linetype=" argument is not wrapped inside aes(), it results in an error:

#http://had.co.nz/ggplot2/geom_path.html
x <- seq(0.01, .99, length=100)
df <- data.frame(x = rep(x, 2), y = c(qlogis(x), 2 * qlogis(x)), group = rep(c("a","b"), each=100))
p <- ggplot(df, aes(x=x, y=y, groups=group))
p + geom_line(aes(linetype=group)) #produces desired plot
p <- ggplot(df, aes(x=x, y=y, groups=group))
p + geom_line(linetype=group) # produces "Error in do.call("layer", list(mapping = mapping, data = data, stat = stat, : object 'group' not found"

Thanks,
Chris

Brandon Hurr

unread,
Aug 2, 2010, 2:36:25 PM8/2/10
to canoe.moore, ggplot2
Here, I made some up... Seems to work though. 

library(ggplot2)

data.plot<-structure(list(ID = 1:40, Grade = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 
3L, 3L, 3L, 3L, 3L, 3L, 3L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 
4L), Score.1 = c(12L, 15L, 12L, 11L, 16L, 16L, 18L, 39L, 25L, 
23L, 32L, 35L, 32L, 31L, 36L, 36L, 38L, 59L, 45L, 43L, 17L, 20L, 
17L, 16L, 21L, 21L, 23L, 44L, 30L, 28L, 47L, 50L, 47L, 46L, 51L, 
51L, 53L, 74L, 60L, 58L), StudentID = c(1L, 2L, 3L, 4L, 5L, 6L, 
7L, 8L, 9L, 10L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 1L, 
2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 1L, 2L, 3L, 4L, 5L, 6L, 
7L, 8L, 9L, 10L), FRP.Flag = structure(c(2L, 2L, 2L, 1L, 1L, 
1L, 2L, 1L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 2L, 1L, 2L, 2L, 2L, 
2L, 2L, 1L, 1L, 1L, 2L, 1L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 2L, 
1L, 2L, 2L), .Label = c("Female", "Male"), class = "factor")), .Names = c("ID", 
"Grade", "Score.1", "StudentID", "FRP.Flag"), class = "data.frame", row.names = c(NA, 
-40L))

g <- ggplot(data=data.plot, aes(x=Grade, y=Score.1, groups=StudentID))
g + geom_line(aes(linetype=FRP.Flag))



Dennis Murphy

unread,
Aug 2, 2010, 3:57:48 PM8/2/10
to Christopher Moore, ggplot2
Hi:

On Mon, Aug 2, 2010 at 11:17 AM, Christopher Moore <canoe...@gmail.com> wrote:
Hi Brandon,

Using "group=" and "groups=" doesn't appear to make a difference.  When the "linetype=" argument is not wrapped inside aes(), it results in an error:

This behavior is documented. When an aesthetic is set, it can only assume one value; when an aesthetic is mapped (i.e., inside an aes() statement), it can assume more than one value. In
p + geom_line(linetype=group)
linetype is set, but since group is a factor with multiple values, an inconsistency is found and the error is thrown. When you wrapped linetype inside an aes() statement, i.e.,

p + geom_line(aes(linetype=group)) #produces desired plot
linetype was mapped to the levels of group, so multiple linetypes were created and a legend was drawn.

As Hadley noted in the ggplot2 book (p. 47-48, section 4.5.2):
"Instead of mapping an aesthetic property to a variable, you can set it to a single value by specifying it in the layer parameters. Aesthetics can vary for each observation being plotted, while parameters do not. We *map* an aesthetic to a variable (e.g., aes(colour = cut)) or *set* it to a constant (e.g., colour = 'red'). ""

HTH,
Dennis

Brandon Hurr

unread,
Aug 2, 2010, 4:24:09 PM8/2/10
to Dennis Murphy, Christopher Moore, ggplot2
That was my bad Dennis, I had seen it both ways and thought maybe the issue was simple, but couldn't have been more wrong.  Thanks for the clarification. 

Looks like Chris figured it out though. Not sure how you could cheat it and change linetypes in a piecewise fashion. Only thing I can think of is breaking the lines up into pieces and plotting them piecewise by the FLP.Flag factor. I'm not sure how you would do that in an automated fashion though. 

Good luck and apologies if I only added to the confusion,

Brandon

Christopher Moore

unread,
Aug 2, 2010, 4:05:51 PM8/2/10
to Brandon Hurr, ggplot2
I simulated some data that reflect my real data.  It appears that group membership must remain static in order to vary line types.  The simulation below shows that if one stays in the same group (FRP.Flag) over time, then ggplot can handle different line types, but if one moves between groups over time, then only color variation can be used to represent group membership.  The downside of this is that color variation can get obscured when printing in grayscale.  Varying line types can help maintain fidelity when printing.

> data.plot <- data.frame(matrix(nrow=5, ncol=2))
> names(data.plot) <- c("StudentID", "Grade.1")
> data.plot$StudentID <- 1:5
> data.plot$Grade.1 <- trunc(runif(n=5, 3, 8))
> data.plot$Grade.2 <- data.plot$Grade.1 + 1
> data.plot$Grade.3 <- data.plot$Grade.1 + 2
> data.plot$Score.1 <- trunc(data.plot$Grade.1*100 + rnorm(n=nrow(data.plot), mean=50))
> data.plot$Score.2 <- trunc(data.plot$Score.1 + 100 + rnorm(n=nrow(data.plot), sd=50))
> data.plot$Score.3 <- trunc(data.plot$Score.2 + 100 + rnorm(n=nrow(data.plot), sd=50))
> data.plot$FRP.Flag <- as.factor(rbinom(nrow(data.plot), size=1, prob=.35))
> data.plot
  StudentID Grade.1 Grade.2 Grade.3 Score.1 Score.2 Score.3 FRP.Flag
1         1       4       5       6     450     629     693        0
2         2       4       5       6     450     472     576        0
3         3       7       8       9     749     839     922        1
4         4       3       4       5     351     447     494        0
5         5       7       8       9     751     763     857        0
> data.plot <- reshape(data.plot, direction="long", idvar="StudentID", varying=2:7, sep=".", timevar="Grade")
> data.plot
    StudentID FRP.Flag Grade Score
1.1         1        0     4   450
2.1         2        0     4   450
3.1         3        1     7   749
4.1         4        0     3   351
5.1         5        0     7   751
1.2         1        0     5   629
2.2         2        0     5   472
3.2         3        1     8   839
4.2         4        0     4   447
5.2         5        0     8   763
1.3         1        0     6   693
2.3         2        0     6   576
3.3         3        1     9   922
4.3         4        0     5   494
5.3         5        0     9   857
> g1 <- ggplot(data=data.plot, aes(x=Grade, y=Score, groups=StudentID))
> g1 + geom_line(aes(linetype=FRP.Flag))
> g1 + geom_line(aes(color=FRP.Flag))
> data.plot$FRP.Flag <- as.factor(rbinom(nrow(data.plot), size=1, prob=.35))
> data.plot
    StudentID FRP.Flag Grade Score
1.1         1        0     4   450
2.1         2        1     4   450
3.1         3        0     7   749
4.1         4        0     3   351
5.1         5        0     7   751
1.2         1        0     5   629
2.2         2        1     5   472
3.2         3        1     8   839
4.2         4        0     4   447
5.2         5        1     8   763
1.3         1        0     6   693
2.3         2        1     6   576
3.3         3        1     9   922
4.3         4        0     5   494
5.3         5        1     9   857
> g1 <- ggplot(data=data.plot, aes(x=Grade, y=Score, groups=StudentID))
> g1 + geom_line(aes(linetype=FRP.Flag))

Error: geom_path: If you are using dotted or dashed lines, colour, size and linetype must be constant over the line
> g1 + geom_line(aes(color=FRP.Flag))

Hadley Wickham

unread,
Aug 9, 2010, 10:52:48 PM8/9/10
to Christopher Moore, Brandon Hurr, ggplot2
Hi Chris,

Could you please send a version of that code that can easily be pasted
into R? I think this is a bug - or it maybe that the error message is
misleading, and this reflects an underlying limitation of the R
graphics model.

Thanks,

Hadley

--
Assistant Professor / Dobelman Family Junior Chair
Department of Statistics / Rice University
http://had.co.nz/

Christopher Moore

unread,
Aug 10, 2010, 5:19:07 PM8/10/10
to Hadley Wickham, Brandon Hurr, ggplot2
#Here's the code formatted for pasting.

data.plot <- data.frame(matrix(nrow=5, ncol=2))
names(data.plot) <- c("StudentID", "Grade.1")
data.plot$StudentID <- 1:5
data.plot$Grade.1 <- trunc(runif(n=5, 3, 8))
data.plot$Grade.2 <- data.plot$Grade.1 + 1
data.plot$Grade.3 <- data.plot$Grade.1 + 2
data.plot$Score.1 <- trunc(data.plot$Grade.1*100 + rnorm(n=nrow(data.plot), mean=50))
data.plot$Score.2 <- trunc(data.plot$Score.1 + 100 + rnorm(n=nrow(data.plot), sd=50))
data.plot$Score.3 <- trunc(data.plot$Score.2 + 100 + rnorm(n=nrow(data.plot), sd=50))
data.plot$FRP.Flag <- as.factor(rbinom(nrow(data.plot), size=1, prob=.35))
data.plot
data.plot <- reshape(data.plot, direction="long", idvar="StudentID", varying=2:7, sep=".", timevar="Grade")
data.plot
g1 <- ggplot(data=data.plot, aes(x=Grade, y=Score, groups=StudentID))
g1 + geom_line(aes(linetype=FRP.Flag))
g1 + geom_line(aes(color=FRP.Flag))
data.plot$FRP.Flag <- as.factor(rbinom(nrow(data.plot), size=1, prob=.35))
data.plot
g1 <- ggplot(data=data.plot, aes(x=Grade, y=Score, groups=StudentID))
g1 + geom_line(aes(linetype=FRP.Flag))
g1 + geom_line(aes(color=FRP.Flag))


takahashi kohske

unread,
Aug 10, 2010, 10:54:24 PM8/10/10
to Christopher Moore, Hadley Wickham, Brandon Hurr, ggplot2
hi, the code

if (!solid_lines && !constant) {
stop("geom_path: If you are using dotted or dashed lines",
", colour, size and linetype must be constant over the line",
call.=FALSE)
}

in GeomPath$draw makes the error.
By removing this part, it looks like working well.
I don't know what this code actually doing.

Christopher T. Moore

unread,
Aug 11, 2010, 5:27:20 PM8/11/10
to ggplot2
That works for me, too, after revising GeomPath$draw locally. Thanks,
Takahashi. -Chris

On Aug 10, 9:54 pm, takahashi kohske <takahashi.koh...@gmail.com>
wrote:
> hi, the code
>
>     if (!solid_lines && !constant) {
>       stop("geom_path: If you are using dotted or dashed lines",
>         ", colour, size and linetype must be constant over the line",
>         call.=FALSE)
>     }
>
> in GeomPath$draw makes the error.
> By removing this part, it looks like working well.
> I don't know what this code actually doing.
>
> On Wed, Aug 11, 2010 at 6:19 AM, Christopher Moore
>
> >> On Mon, Aug 2, 2010 at 3:05 PM, Christopher Moore <canoe.mo...@gmail.com>
> >> > On Mon, Aug 2, 2010 at 1:36 PM, Brandon Hurr <bhiv...@gmail.com> wrote:
>
> >> >> Here, I made some up... Seems to work though.
> >> >> library(ggplot2)
> >> >> data.plot<-structure(list(ID = 1:40, Grade = c(1L, 1L, 1L, 1L, 1L, 1L,
> >> >> 1L,
> >> >> 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L,
> >> >> 3L, 3L, 3L, 3L, 3L, 3L, 3L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L,
> >> >> 4L), Score.1 = c(12L, 15L, 12L, 11L, 16L, 16L, 18L, 39L, 25L,
> >> >> 23L, 32L, 35L, 32L, 31L, 36L, 36L, 38L, 59L, 45L, 43L, 17L, 20L,
> >> >> 17L, 16L, 21L, 21L, 23L, 44L, 30L, 28L, 47L, 50L, 47L, 46L, 51L,
> >> >> 51L, 53L, 74L, 60L, 58L), StudentID = c(1L, 2L, 3L, 4L, 5L, 6L,
> >> >> 7L, 8L, 9L, 10L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 1L,
> >> >> 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 1L, 2L, 3L, 4L, 5L, 6L,
> >> >> 7L, 8L, 9L, 10L), FRP.Flag = structure(c(2L, 2L, 2L, 1L, 1L,
> >> >> 1L, 2L, 1L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 2L, 1L, 2L, 2L, 2L,
> >> >> 2L, 2L, 1L, 1L, 1L, 2L, 1L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 2L,
> >> >> 1L, 2L, 2L), .Label = c("Female", "Male"), class = "factor")), .Names =
> >> >> c("ID",
> >> >> "Grade", "Score.1", "StudentID", "FRP.Flag"), class = "data.frame",
> >> >> row.names = c(NA,
> >> >> -40L))
> >> >> g <- ggplot(data=data.plot, aes(x=Grade, y=Score.1, groups=StudentID))
> >> >> g + geom_line(aes(linetype=FRP.Flag))
>
> >> >> On Mon, Aug 2, 2010 at 19:17, Brandon Hurr <bhiv...@gmail.com> wrote:
>
> >> >>> Please ignore me, groups is the same as group and linetype goes in the
> >> >>> aes as you have it if you want it to be dependent on a factor.
> >> >>> You sure you can't change the names and numbers in your data and
> >> >>> share,
> >> >>> it's hard to play with something that's broken when you only have 1/2
> >> >>> the
> >> >>> picture?
> >> >>> On Mon, Aug 2, 2010 at 19:00, Brandon Hurr <bhiv...@gmail.com> wrote:
>
> >> >>>> My bad, not syntax, spelling.
>
> >> >>>> On Mon, Aug 2, 2010 at 18:59, Brandon Hurr <bhiv...@gmail.com> wrote:
>
> >> >>>>> Chris,
> >> >>>>> I'm not sure what the problem is exactly yet, but you should start
> >> >>>>> by
> >> >>>>> fixing your syntax. "group= x" != "groups= x". Also, I think
> >> >>>>> linetype goes
> >> >>>>> outside the aes() call.
> >> >>>>> Brandon
>
> >> >>>>> On Mon, Aug 2, 2010 at 18:18, canoe.moore <canoe.mo...@gmail.com>
> >> >>>>> wrote:
>
> >> >>>>>> Hello,
>
> >> >>>>>> I'm trying to create a spaghetti plot with line types that vary by
> >> >>>>>> group, but I keep getting an error.  The following examples show
> >> >>>>>> that
> >> >>>>>> it's possible.
>
> >> >>>>>> #http://r.789695.n4.nabble.com/color-lines-by-group-membership-in-
> >> >>>>>> spaghetti-plot-td2216888.html
> >> >>>>>> library(nlme)
> >> >>>>>> library(ggplot2)
> >> >>>>>> p <- ggplot(data=Orthodont, aes(x=age, y=distance, group=Subject,
> >> >>>>>> linetype=Sex))
> >> >>>>>> p + geom_line()
>
> >> >>>>>> #http://had.co.nz/ggplot2/geom_path.html
> >> >>>>>> x <- seq(0.01, .99, length=100)
> >> >>>>>> df <- data.frame(x = rep(x, 2), y = c(qlogis(x), 2 * qlogis(x)),
> >> >>>>>> group= rep(c("a","b"), each=100))
> >> >>>>>> p <- ggplot(df, aes(x=x, y=y, group=group))
> >> >>>>>> p + geom_line(aes(linetype=group))
>
> >> >>>>>> When I try to produce similar plots with my own data (sorry I can't
> >> >>>>>> provide the data), I get the following error message:
> >> >>>>>> Error:
>
> ...
>
> read more »

Hadley Wickham

unread,
Aug 12, 2010, 11:17:16 AM8/12/10
to Christopher T. Moore, ggplot2
It works in this case, where the lines are straight, but it is
dangerous in general. See the last example in geom_path:

x <- seq(0.01, .99, length=100)
df <- data.frame(x = rep(x, 2), y = c(qlogis(x), 2 * qlogis(x)),

group = rep(c("a","b"), each=100))


p <- ggplot(df, aes(x=x, y=y, group=group))

# Should work
p + geom_line(linetype = 2)
p + geom_line(aes(colour = group), linetype = 2)
p + geom_line(aes(colour = x))

# Should fail
should_stop(p + geom_line(aes(colour = x), linetype=2))

Hadley

Christopher Moore

unread,
Aug 12, 2010, 5:31:37 PM8/12/10
to Hadley Wickham, ggplot2
I see what you mean about the need for a failure criterion.  Is there a different, less strict failure criterion that would allow one to vary linetype along a trajectory to represent dynamic group membership over time and still fail when appropriate?  A couple ideas... perhaps it should fail if one tries to vary both linetype and color or size over the span of a dotted/dashed line (i.e., allow just linetype to vary so that the error becomes "Error: geom_path: If you are using dotted or dashed lines, colour and size must be constant over the line") or if the number of x values exceeds the number of line types.  Hope that helps.  Thanks for all your hard work on the package.

Regards,
Chris
Reply all
Reply to author
Forward
0 new messages