简体   繁体   中英

Plotly: How to create a line plot with different style and color for each variable?

I am trying to create a graph with 10 different lines with different colours and markers using Plotly express. Something similar to this:

在此处输入图片说明

I can create a nice looking graph with different colours using the px.line function as the documentation suggests. My code looks like this:

import plotly.express as px
import numpy as np
import pandas as pd

rand_elems = []
for i in range(10):
    rand_elems.append(np.random.randn(25))

data = pd.DataFrame(rand_elems)

px.line(data_frame=data.T)

and my line graph looks like this:

在此处输入图片说明

where each variable is a (25,) numpy array with random values from the standard normal distribution (created with np.random.randn(25) ).

Is there a way I can add different styles to each line? Other plotting libraries are also welcome as I couldn't find a solution for this in Plotly's documentation.

I understand there is a limit of line styles I could use. Maybe I could cycle through them and the colours? What would be a good solution for this?

EDIT: The graph purpose is solely to show that the signals are random and within the standard normal distribution limits.

px.line is perfect for datasets of high diversity, like different categories for an array of countries accross several continents because you can distinguish the catgories using arguments like color = 'country , line_dash = 'continent to assign colors and shapes to them. Here's an example using a subset the built-in dataset px.data.gapminder()

plot 1

在此处输入图片说明

code 1

import plotly.express as px
from plotly.validators.scatter.marker import SymbolValidator

# data
df = px.data.gapminder()
df = df[df['country'].isin(['Canada', 'USA', 'Norway', 'Sweden', 'Germany'])]

# plotly
fig = px.line(df, x='year', y='lifeExp',
              color='country',
              line_dash = 'continent')
fig.show()

But you seem to be interested in different shapes of the markers as well, and there does not seem to be a built-in functionality for those that are as easy to use as color and line_shape . So what follows is a way to cycle through available marker shapes and apply those to, for example, the different countries. You could of course define your own sequence by picking shapes from marker styles , like:

['arrow-bar-left', 'asterisk', 'arrow-right', 'line-ne', 'circle-cross', 'y-left']

But you can also grab a bunch of styles based on raw_symbols = SymbolValidator().values , refine those findings a bit and add those to, for example, country names.

Here's the result

在此处输入图片说明

And here's how you do it:

import plotly.express as px
from itertools import cycle

# data
df = px.data.gapminder()
df = df[df['country'].isin(['Canada', 'USA', 'Norway', 'Sweden', 'Germany'])]

# plotly
fig = px.line(df, x='year', y='lifeExp',
              color='country',
              line_dash = 'continent')

# retrieve a bunch of markers
raw_symbols = SymbolValidator().values
namestems = []
namevariants = []
symbols = []
for i in range(0,len(raw_symbols),3):
    name = raw_symbols[i+2]
    symbols.append(raw_symbols[i])
    namestems.append(name.replace("-open", "").replace("-dot", ""))
    namevariants.append(name[len(namestems[-1]):])
markers = cycle(list(set(namestems)))

# set unique marker style for different countries
fig.update_traces(mode='lines+markers')
for d in fig.data:
    d.marker.symbol = next(markers)
    d.marker.size = 10
fig.show()

I have taken @vestland 's code and adapted and fixed to a simpler version that achieves exactly what I needed. The idea is to use SymbolValidator() from plotly.validators.scatter.marker as noted in @vestland 's answer. One could also add a random factor to this list for more distinct results.

sample running code:

import plotly.express as px
from itertools import cycle
from plotly.validators.scatter.marker import SymbolValidator

# data
df = px.data.gapminder()
df = df[df['country'].isin(['Canada', 'USA', 'Norway', 'Sweden', 'Germany'])]

# plotly
fig = px.line(df, x='year', y='lifeExp',
              color='country',
              line_dash = 'continent')

raw_symbols = SymbolValidator().values
# Take only the string values which are in this order.
symbols_names = raw_symbols[::-2]
markers = cycle(symbols_names)

# set unique marker style for different countries
fig.update_traces(mode='lines+markers')
for d in fig.data:
    d.marker.symbol = next(markers)
    d.marker.size = 10
fig.show()

This code produces the following graph: 在此处输入图片说明 Thanks a lot for the insights @vestland I wonder if Plotly could make this a parameter option in the next versions.

Update:

If your DataFrame does not have an aggregation column to use for the line_dash parameter like mine, you can also cycle through a list with line styles and overwrite them using `fig.data.line["dash"] as presented below:

  • Code to generate data and import packages.
import numpy as np
import random
import plotly.express as px
import numpy as np
import pandas as pd
from itertools import cycle
from random import shuffle
from plotly.validators.scatter.marker import SymbolValidator
from plotly.validators.scatter.line import DashValidator

rand_elems = []
for i in range(10):
    rand_elems.append(np.random.randn(25))

data = pd.DataFrame(rand_elems)
data.index = [f"Signal {i}" for i in range(10)]
  • Code to generate different marker and line styles for each column in the DataFrame.
line_styles_names = ['solid', 'dot', 'dash', 'longdash', 'dashdot', 'longdashdot']
line_styles = cycle(line_styles_names)

fig = (px.line(data_frame=data.T, 
              color_discrete_sequence=px.colors.qualitative.Bold_r,
              title="First 10 Signals Visualization",
             ))

symbols_names = list(set([i.replace("-open", "").replace("-dot", "") for i in SymbolValidator().values[::-2]]))
shuffle(symbols_names)
markers = cycle(symbols_names)

_ = fig.update_traces(mode='lines+markers')
for d in fig.data:
    d.line["dash"] = next(line_styles)
    d.marker.symbol = next(markers)
    d.marker.size = 10
fig.show()

with this, the result should look similar to this, which is exactly what I was looking for. 在此处输入图片说明

The way I know is to create individual graph objects for each line you want to plot (each having it's own style). Then create a list of all the graph objects and pass it to the data argument of the go.Figure() function.

See this blog for an example.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM