Search code examples
juliaplots.jl

How to make specific portion of lines in a plot thicker without changing colors?


I'm dealing with a dataframe of size (579x31), and I want to plot all columns except the :date column and modify the plot to change the line width of the last 30 data points in the plot.

using DataFrames
using Plots
using StatsPlots

# `selection` is a subset of a dataframe object.
p = @df selection plot(
  :date, cols(2:31),
  legend=:outerright,
  xlabel="Date",
  ylabel="Close",
  title="Stock Prices",
  size=(1000, 600),
  dpi=300,
  left_margin=5Plots.mm,
  bottom_margin=5Plots.mm,
  legend_title="DJI Stocks",
)

This gives me the following plot:
enter image description here

Now I modify the line width of the last 30 data points:

# Grab the default colors to prevent color changes in the modified plot
colors = [p.series_list[i].plotattributes[:linecolor] for i in eachindex(p.series_list)];

# Modify the plot
@df selection[end-30:end, :] plot!(
  :date, cols(2:31),
  linewidth=2,
  label=nothing,
  linecolors=colors
)

The problem is where it sets the color of each data point rather than each line (plot!):
enter image description here (I used a magnifier to better see the problem)
How can I fix this?

The expected output is a plot similar to the first figure (p) with thicker lines in the last 30 data points.

Minimal reproducible example:

using DataFrames
using Plots
using StatsPlots
using Dates

start_date = Date("2021-01-03")
end_date = start_date + Day(4)
df = DataFrame(
  date = start_date:Day(1):end_date,
  col1 = rand(5),
  col2 = rand(5),
  col3 = rand(5),
  col4 = rand(5)
)

p = @df df plot(
  :date, cols(2:5),
  legend=:outerright,
  xlabel="Date",
  ylabel="Close",
  title="Stock Prices",
  size=(1000, 600),
  dpi=300,
  left_margin=5Plots.mm,
  bottom_margin=5Plots.mm,
  legend_title="DJI Stocks",
)

colors = [p.series_list[i].plotattributes[:linecolor] for i in eachindex(p.series_list)];

@df df[end-2:end, :] plot!(
  :date, cols(2:5),
  linewidth=2,
  label=nothing,
  linecolors=colors
)

Gives the following:
enter image description here


Solution

  • I can tackle this problem by specifying a matrix of colors with the shape of 1xn (which n is the number of columns to be plotted) and pass it to the linecolors keyword argument to plot and plot! similarly:

    # Suggest 30 colors
    colors = permutedims(distinguishable_colors(30))
    # Or specify them manually
    # colors = [:red :blue :green :yellow :orange :purple :pink :brown :black :gray :lightblue :lightgreen :lightyellow :lightgray :lime :lightpink :Magenta :orchid :lightgray :darkblue :darkgreen :darkviolet :darkorange :gold :hotpink :midnightblue :yellowgreen :darkgray :steelblue :darkred]
    
    @df selection plot(
      :date, cols(2:31),
      legend=:outerright,
      xlabel="Date",
      ylabel="Close",
      title="Stock Prices",
      size=(1000, 600),
      dpi=300,
      left_margin=5Plots.mm,
      bottom_margin=5Plots.mm,
      legend_title="DJI Stocks",
      linecolors=colors,
    )
    
    @df selection[end-30:end, :] plot!(
      :date, cols(2:31),
      linewidth=2,
      label=nothing,
      linecolors=colors
    )
    

    The outcome is:
    enter image description here

    Note that either linecolors or linecolor can be used interchangeably as of now in Plots v1.38.8 and GR backend v0.71.8.

    Similarly, the minimal reproducible example can be written as following:

    colors = permutedims(distinguishable_colors(4))
    
    @df df plot(
      :date, cols(2:5),
      # same options as in the question
      linecolors=colors
    )
    
    @df df[end-2:end, :] plot!(
      :date, cols(2:5),
      # same options as in the question
      linecolors=colors
    )
    

    enter image description here