Elegant Way to Select the Color for a Particular Segment of a Line Plot

Elegant way to select the color for a particular segment of a line plot?

Yes, one way of doing this is to use ggplot.

ggplot requires your data to be in data.frame format. In this data.frame I add a column col that indicates your desired colour. The plot is then constructed with ggplot, geom_line, and scale_colour_identity since the col variable is already a colour:

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()

Sample Image

More generally, each line segment can be a different colour. In the next example I map colour to the x value, giving a plot that smoothly changes colour from blue to red:

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

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

Sample Image


And if you insist on using base graphics, then use segments as follows:

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])
}

Sample Image

Is there a way to color segments of a line in base R?

This is possible, with the segments function.

for(i in 1:(length(templog$time)-1)){

segments(templog$time[i],
templog$temp[i],
templog$time[i+1],
templog$temp[i+1],

col=templog$heaterstatus[i])

}

Basically, you're iterating through each pair of points & draws a straight line in the specified color. Also, you can simplify your plot() call-

plot(temp~time, data=templog, type='n')

would suffice.

Hope this helps :-)

Colour a line by a given value in a plot in R

Try this

x = 1:11
y = abs(6 - x)
y = c(y,y)
plot(1:22,y, col = ifelse(c(y,y) < 2.5, 2, 3), pch = 16)

for(i in 1:21){
if(y[i]>1.9&& y[i+1]>1.9){
linecolour="green"
} else {
linecolour="red"
}
lines(c((1:22)[i],(1:22)[i+1]),c(y[i],y[i+1]),col=linecolour)
}

Sample Image

matplotlib Line plot segment color based on flag column

I would get a single column per season to plot, which you can do with pivot or with unstack:

>>> sales = df.set_index('Season', append=True)['Sale']
>>> data = sales.unstack('Season')
>>> data
Season Fall Spring Summer Winter
2020-01-01 NaN NaN NaN 10.0
2020-02-01 NaN NaN NaN 20.0
2020-03-01 NaN NaN NaN 30.0
2020-04-01 NaN 40.0 NaN NaN
2020-05-01 NaN 50.0 NaN NaN
2020-06-01 NaN 60.0 NaN NaN
2020-07-01 NaN NaN 70.0 NaN
2020-08-01 NaN NaN 80.0 NaN
2020-09-01 NaN NaN 90.0 NaN
2020-10-01 100.0 NaN NaN NaN
2020-11-01 110.0 NaN NaN NaN
2020-12-01 120.0 NaN NaN NaN

Call this new dataframe data, you can then simply plot it with:

data.plot(color=colors_map)

Here’s the result:

plot results

This gives gaps between seasons but is much much simpler than the other question you linked too.

Some options may reduce the impact of your gaps as well as really show that each “point” is in fact a whole month:

data.plot(color=colors_map, drawstyle='steps-pre')

steps plot

If that doesn’t satisfy you you’ll need to duplicate points at the boundary on 2 different columns:

First let’s select the values we’ll want to fill in, make sure the columns are in a sensible order:

>>> fillin = data.mask(data.isna() == data.isna().shift())
>>> fillin = fillin.reindex(['Winter', 'Spring', 'Summer', 'Fall'], axis='columns')
>>> fillin
Season Winter Spring Summer Fall
index
2020-01-01 10.0 NaN NaN NaN
2020-02-01 NaN NaN NaN NaN
2020-03-01 NaN NaN NaN NaN
2020-04-01 NaN 40.0 NaN NaN
2020-05-01 NaN NaN NaN NaN
2020-06-01 NaN NaN NaN NaN
2020-07-01 NaN NaN 70.0 NaN
2020-08-01 NaN NaN NaN NaN
2020-09-01 NaN NaN NaN NaN
2020-10-01 NaN NaN NaN 100.0
2020-11-01 NaN NaN NaN NaN
2020-12-01 NaN NaN NaN NaN

Now fill these values into data by rotating the columns:

>>> fillin.shift(-1, axis='columns').assign(Fall=fillin['Winter'])
Season Winter Spring Summer Fall
index
2020-01-01 NaN NaN NaN 10.0
2020-02-01 NaN NaN NaN NaN
2020-03-01 NaN NaN NaN NaN
2020-04-01 40.0 NaN NaN NaN
2020-05-01 NaN NaN NaN NaN
2020-06-01 NaN NaN NaN NaN
2020-07-01 NaN 70.0 NaN NaN
2020-08-01 NaN NaN NaN NaN
2020-09-01 NaN NaN NaN NaN
2020-10-01 NaN NaN 100.0 NaN
2020-11-01 NaN NaN NaN NaN
2020-12-01 NaN NaN NaN NaN
>>> data.fillna(fillin.shift(-1, axis='columns').assign(Fall=fillin['Winter'])).plot(color=colors_map)

Sample Image

And here’s what this final result looks like with the new data in your post − my code is left unchanged:

Sample Image

Elegant way to select the color for a particular segment of a line plot?

Yes, one way of doing this is to use ggplot.

ggplot requires your data to be in data.frame format. In this data.frame I add a column col that indicates your desired colour. The plot is then constructed with ggplot, geom_line, and scale_colour_identity since the col variable is already a colour:

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()

Sample Image

More generally, each line segment can be a different colour. In the next example I map colour to the x value, giving a plot that smoothly changes colour from blue to red:

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

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

Sample Image


And if you insist on using base graphics, then use segments as follows:

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])
}

Sample Image

reorder legend of multiple line plot with segment using ggplot2

This might be what you are looking for

 ggplot(df, aes(x=x, y=y1)) + 
geom_line(aes(colour=col1, group=1)) +
geom_line(aes(x=x, y=y2,col=col2,group=1)) +
geom_line(aes(x=x, y=y3,col=col3,group=1)) +
geom_line(aes(x=x, y=y4,colour=col4, group=1)) +
geom_line(aes(x=x, y=y4,col=col4,group=1)) +
scale_color_manual(breaks = c("blue","orange","cyan","red","black"),
values=c("blue" = "blue", "red" = "red","orange" = "orange","cyan" = "cyan","black" = "black"),
labels=c("s1on","s2_on","s1_off","s2off","new"),name="")

You can use breaks to reorder the legend. The values argument is mapping the right color to the right value.

It is easier for me to see if the values in df are different than the colors you want

df <- data.frame(x = 1:100,
y1 = rnorm(100,1,100),
y2=rnorm(100,5,50),
y3=rnorm(100,10,500),
y4=rnorm(100,1,200),
col1 = c(rep("rrr", 50),
rep("bbb", 10),
rep("rrr", 40)),
col2=c(rep("blbl", 50),
rep("bbb", 10),
rep("blbl", 40)),
col3=c(rep("ooo", 50),
rep("bbb", 10),
rep("ooo", 40)),
col4=c(rep("ccc", 50),
rep("bbb", 10),
rep("ccc", 40)))

ggplot(df, aes(x=x, y=y1)) +
geom_line(aes(colour=col1, group=1)) +
geom_line(aes(x=x, y=y2,col=col2,group=1)) +
geom_line(aes(x=x, y=y3,col=col3,group=1)) +
geom_line(aes(x=x, y=y4,colour=col4, group=1)) +
geom_line(aes(x=x, y=y4,col=col4,group=1)) +
scale_color_manual(breaks = c("blbl","ooo","ccc","rrr","bbb"),
values=c("blbl" = "blue", "rrr" = "red","ooo" = "orange","ccc" = "cyan","bbb" = "black"),
labels=c("s1on","s2_on","s1_off","s2off","new"),name="")

Scatterplot with color groups - base R plot

You can pass a vector of colours to the col parameter, so it is just a matter of defining your z groups in a way that makes sense for your application. There is the cut() function in base, or cut2() in Hmisc which offers a bit more flexibility. To assist in picking reasonable colour palettes, the RColorBrewer package is invaluable. Here's a quick example after defining x,y,z:

z.cols <- cut(z, 3, labels = c("pink", "green", "yellow"))
plot(x,y, col = as.character(z.cols), pch = 16)

Sample Image

You can obviously add a legend manually. Unfortunately, I don't think all types of plots accept vectors for the col argument, but type = "p" obviously works. For instance, plot(x,y, type = "l", col = as.character(z.cols)) comes out as a single colour for me. For these plots, you can add different colours with lines() or segments() or whatever the low level plotting command you need to use is. See the answer by @Andrie for doing this with type = "l" plots in base graphics here.

R - If else statement within for loop

This is my solution. It assumes that the NAs are still present in the original data. These will be omitted in the first plot() command. The function then loops over just the NAs.

You will probably get finer control if you take the plot() command out of the function. As written, "..." gets passed to plot() and a type = "b" graph is mimicked - but it's trivial to change it to whatever you want.

# Function to plot interpolated valules in specified colours.
PlotIntrps <- function(exxes, wyes, int_wyes, int_pt = "red", int_ln = "grey",
goodcol = "darkgreen", ptch = 20, ...) {

plot(exxes, wyes, type = "b", col = goodcol, pch = ptch, ...)

nas <- which(is.na(wyes))
enn <- length(wyes)

for (idx in seq(nas)) {
points(exxes[nas[idx]], int_wyes[idx], col = int_pt, pch = ptch)
lines(
x = c(exxes[max(nas[idx] - 1, 1)], exxes[nas[idx]],
exxes[min(nas[idx] + 1, enn)]),
y = c(wyes[max(nas[idx] - 1, 1)], int_wyes[idx],
wyes[min(nas[idx] + 1, enn)]),
col = int_ln, type = "c")

# Only needed if you have 2 (or more) contiguous NAs (interpolations)
wyes[nas[idx]] <- int_wyes[idx]
}
}

# Dummy data (jitter() for some noise)
x_data <- 1:12
y_data <- jitter(c(12, 11, NA, 9:7, NA, NA, 4:1), factor = 3)
interpolations <- c(10, 6, 5)

PlotIntrps(exxes = x_data, wyes = y_data, int_wyes = interpolations,
main = "Interpolations in pretty colours!",
ylab = "Didn't manage to get all of these")

Cheers.

Matplotlib, plot the column of a dataframe, and the lines changes color based on a condition from another column

I am not sure if this is the most elegant way since I am fairly new to Python, but this seems to accomplish what you are looking for.

First, import relevant libraries:

import pandas as pd
import matplotlib.pyplot as plt

Then, plot all the price data as a black line:

plt.plot(df['time'], df['price'], c = 'k')

Finally, loop over the combinations of consecutive rows of the price column (because this is really what you want to be color coded, not the points themselves). The code here will identify if the second element in each combination of consecutive elements was akin to an increase or decrease. If it was an increase, it will plot a green line over that segment of the black line. Conversely, if it was a decrease, it will plot a red line over that segment of the black line.

for val in range(2, len(df)+1):   
start = val - 2
focal_val = val -1
if df['trend'][focal_val] == 'increasing':
plt.plot(df['time'][start:val], df['price'][start:val], c = 'g')
elif df['trend'][focal_val] == 'decreasing':
plt.plot(df['time'][start:val], df['price'][start:val], c = 'r')

The final graph looks like
this.



Related Topics



Leave a reply



Submit