Short version: is there a Python method for displaying an image which shows, in real time, the pixel indices and intensities? So that as I move the cursor over the image, I have a continually updated display such as pixel[103,214] = 198 (for grayscale) or pixel[103,214] = (138,24,211) for rgb?
Long version:
Suppose I open a grayscale image saved as an ndarray im and display it with imshow from matplotlib:
im = plt.imread('image.png')
plt.imshow(im,cm.gray)
What I get is the image, and in the bottom right of the window frame, an interactive display of the pixel indices. Except that they're not quite, as the values are not integers: x=134.64 y=129.169 for example.
If I set the display with correct resolution:
plt.axis('equal')
the x and y values are still not integers.
The imshow method from the spectral package does a better job:
import spectral as spc
spc.imshow(im)
Then in the bottom right I now have pixel=[103,152] for example.
However, none of these methods also shows the pixel values. So I have two questions:
Can the imshow from matplotlib (and the imshow from scikit-image) be coerced into showing the correct (integer) pixel indices?
Can any of these methods be extended to show the pixel values as well?
There a couple of different ways to go about this.
You can monkey-patch ax.format_coord, similar to this official example. I'm going to use a slightly more "pythonic" approach here that doesn't rely on global variables. (Note that I'm assuming no extent kwarg was specified, similar to the matplotlib example. To be fully general, you need to do a touch more work.)
import numpy as np
import matplotlib.pyplot as plt
class Formatter(object):
def __init__(self, im):
self.im = im
def __call__(self, x, y):
z = self.im.get_array()[int(y), int(x)]
return 'x={:.01f}, y={:.01f}, z={:.01f}'.format(x, y, z)
data = np.random.random((10,10))
fig, ax = plt.subplots()
im = ax.imshow(data, interpolation='none')
ax.format_coord = Formatter(im)
plt.show()
Alternatively, just to plug one of my own projects, you can use mpldatacursor for this. If you specify hover=True, the box will pop up whenever you hover over an enabled artist. (By default it only pops up when clicked.) Note that mpldatacursor does handle the extent and origin kwargs to imshow correctly.
import numpy as np
import matplotlib.pyplot as plt
import mpldatacursor
data = np.random.random((10,10))
fig, ax = plt.subplots()
ax.imshow(data, interpolation='none')
mpldatacursor.datacursor(hover=True, bbox=dict(alpha=1, fc='w'))
plt.show()
Also, I forgot to mention how to show the pixel indices. In the first example, it's just assuming that i, j = int(y), int(x). You can add those in place of x and y, if you'd prefer.
With mpldatacursor, you can specify them with a custom formatter. The i and j arguments are the correct pixel indices, regardless of the extent and origin of the image plotted.
For example (note the extent of the image vs. the i,j coordinates displayed):
import numpy as np
import matplotlib.pyplot as plt
import mpldatacursor
data = np.random.random((10,10))
fig, ax = plt.subplots()
ax.imshow(data, interpolation='none', extent=[0, 1.5*np.pi, 0, np.pi])
mpldatacursor.datacursor(hover=True, bbox=dict(alpha=1, fc='w'),
formatter='i, j = {i}, {j}\nz = {z:.02g}'.format)
plt.show()
An absolute bare-bones "one-liner" to do this: (without relying on datacursor)
def val_shower(im):
return lambda x,y: '%dx%d = %d' % (x,y,im[int(y+.5),int(x+.5)])
plt.imshow(image)
plt.gca().format_coord = val_shower(ims)
It puts the image in closure so makes sure if you have multiple images each will display its own values.
All of the examples that I have seen only work if your x and y extents start from 0. Here is code that uses your image extents to find the z value.
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
d = np.array([[i+j for i in range(-5, 6)] for j in range(-5, 6)])
im = ax.imshow(d)
im.set_extent((-5, 5, -5, 5))
def format_coord(x, y):
"""Format the x and y string display."""
imgs = ax.get_images()
if len(imgs) > 0:
for img in imgs:
try:
array = img.get_array()
extent = img.get_extent()
# Get the x and y index spacing
x_space = np.linspace(extent[0], extent[1], array.shape[1])
y_space = np.linspace(extent[3], extent[2], array.shape[0])
# Find the closest index
x_idx= (np.abs(x_space - x)).argmin()
y_idx= (np.abs(y_space - y)).argmin()
# Grab z
z = array[y_idx, x_idx]
return 'x={:1.4f}, y={:1.4f}, z={:1.4f}'.format(x, y, z)
except (TypeError, ValueError):
pass
return 'x={:1.4f}, y={:1.4f}, z={:1.4f}'.format(x, y, 0)
return 'x={:1.4f}, y={:1.4f}'.format(x, y)
# end format_coord
ax.format_coord = format_coord
If you are using PySide/PyQT here is an example to have a mouse hover tooltip for the data
import matplotlib
matplotlib.use("Qt4Agg")
matplotlib.rcParams["backend.qt4"] = "PySide"
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
# Mouse tooltip
from PySide import QtGui, QtCore
mouse_tooltip = QtGui.QLabel()
mouse_tooltip.setFrameShape(QtGui.QFrame.StyledPanel)
mouse_tooltip.setWindowFlags(QtCore.Qt.ToolTip)
mouse_tooltip.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents)
mouse_tooltip.show()
def show_tooltip(msg):
msg = msg.replace(', ', '\n')
mouse_tooltip.setText(msg)
pos = QtGui.QCursor.pos()
mouse_tooltip.move(pos.x()+20, pos.y()+15)
mouse_tooltip.adjustSize()
fig.canvas.toolbar.message.connect(show_tooltip)
# Show the plot
plt.show()
with Jupyter you can do so either with datacursor(myax)or by ax.format_coord.
Sample code:
%matplotlib nbagg
import numpy as np
import matplotlib.pyplot as plt
X = 10*np.random.rand(5,3)
fig,ax = plt.subplots()
myax = ax.imshow(X, cmap=cm.jet,interpolation='nearest')
ax.set_title('hover over the image')
datacursor(myax)
plt.show()
the datacursor(myax) can also be replaced with ax.format_coord = lambda x,y : "x=%g y=%g" % (x, y)
In case you, like me, work on Google Colab, this solutions do not work as Colab disabled interactive feature of images for matplotlib.
Then you might simply use Plotly:
https://plotly.com/python/imshow/
import plotly.express as px
import numpy as np
img_rgb = np.array([[[255, 0, 0], [0, 255, 0], [0, 0, 255]],
[[0, 255, 0], [0, 0, 255], [255, 0, 0]]
], dtype=np.uint8)
fig = px.imshow(img_rgb)
fig.show()
Matplotlib has built-in interactive plot which logs pixel values at the corner of the screen.
To setup first install pip install ipympl
Then use either %matplotlib notebook or %matplotlib widget instead of %matplotlib inline
The drawback with plotly or Bokeh is that they don't work on Pycharm.
For more information take a look at the doc
To get interactive pixel information of an image use the module imagetoolbox
To download the module open the command prompt and write
pip install imagetoolbox
Write the given code to get interactive pixel information of an image
enter image description here
Output:enter image description here
Related
Below is the plot I generated using axes.text option,
ax[0].text(row.TIMESTAMP, row.HIGH+(0.1*width),row['candlestick_pattern'], fontsize=5, rotation='vertical')
I'm trying to achieve the same output using TextPath and PathPatch, in order to increase/decrease the font size when I zoom in/out of the plot, and below is the code I have (taken from here and here )
textPath = TextPath((data_coord[1], -data_coord[0]), row['candlestick_pattern'], size=2)
pathPatch = PathPatch(textPath, color="black")
transform = mpl.transforms.Affine2D().rotate_deg(90) + ax[0].transData
pathPatch.set_transform(transform)
ax[0].add_patch(pathPatch)
Output with this is
You could see that the text is cramped into a very small region and its not what I want. I would want to set the font size to a smaller value and increase the width (in vertical mode - height) of the TextPath. Is that possible?
Below is the complete code with which we can reproduce the problem for the dataset here
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.textpath import TextPath
from matplotlib.patches import PathPatch
from mplfinance.original_flavor import candlestick_ohlc
from matplotlib import transforms as tf
import pandas as pd
plotDf = pd.read_csv("data.csv")
plotDf.reset_index(inplace=True)
del plotDf['TIMESTAMP']
del plotDf['TOTTRDQTY']
fig, ax = plt.subplots(1)
candlestick_ohlc(ax,plotDf.values,width=0.6, \
colorup='green', colordown='red', alpha=0.8)
maxHigh = plotDf['HIGH'].max()
minLow = plotDf['LOW'].min()
width = maxHigh - minLow
threshold = (width)*0.6
for idx, row in plotDf.iterrows():
if (row['candlestick_pattern'] != 'NO_PATTERN'):
if (row.HIGH < (threshold+minLow)):
data_coord = (idx, row.HIGH+(0.1*width))
#ax.text(idx, row.HIGH+(0.1*width), row['candlestick_pattern'], fontsize=5, rotation='vertical')
else:
data_coord = (idx, row.LOW-(0.4*width))
#ax.text(idx, row.LOW-(0.4*width), row['candlestick_pattern'], fontsize=5, rotation='vertical')
textPath = TextPath((data_coord[1], -data_coord[0]), row['candlestick_pattern'], size=2)
pathPatch = PathPatch(textPath, color="black")
transform = mpl.transforms.Affine2D().rotate_deg(90) + ax.transData
pathPatch.set_transform(transform)
ax.add_patch(pathPatch)
fig.autofmt_xdate()
fig.tight_layout()
fig.suptitle("test", fontsize=16)
fig.set_size_inches(10.5, 10.5)
plt.subplots_adjust(top=0.95)
plt.show()
Apparently, your problem is a scaling problem. Messing around with .scale(x,y), ax.set_xlim and ax.set_ylim might allow you to "unsqueeze" the text. You can also try to set an anchor for your plot like done here:
ts = ax.transData
coords = ts.transform([0,0]) #<-anchor
tr = mpl.transforms.Affine2D().rotate_deg_around(coords[0],coords[1],90).scale(1,3) #<- scale
t = ts + tr
#<extra code>
pathPatch = PathPatch(textPath, color="black", transform = t)
EDIT
I tried many things, but I couldn't find a good way of doing it. I'll leave below what I tried and some resources that might help.
The way to properly use .rotate_deg_around would be like such:
ts = ax.transData
# ts = fig.dpi_scale_trans #this guy uses the fig scale, if you're interested
coords = ts.transform([data_coord[0],data_coord[1]])
converter = (coords[0]/data_coord[0], coords[1]/data_coord[1])
#plot the anchor points for visualization:
plt.plot(coords[0]/converter[0], coords[1]/converter[1],'xk')
tr = mpl.transforms.Affine2D().rotate_deg_around(coords[0]/converter[0],coords[1]/converter[1],90).scale(converter[0],converter[1])
pathPatch = PathPatch(textPath, color="black", transform = tr)
ax.add_patch(pathPatch)
Nonetheless, the results are still similar to what you had at the beginning:
It appears that TextPath does not behave like it should when using transform. Here .get_offset_transform is used, and it apparently fixes this sort of issue, but I was unable to use it since the plt has a Line type.
Also, you will see that if you increase the y axis in .scale, you can start to see the text, but it spreads the coordinates as well. One idea you can try is setting a good readable y scale (use ax.set_ylim to see your text) and then use that value as a divisor when setting the coordinates for your plot.
There are also some ideas here that might serve you.
I would like to plot an animated vibrating string using python but to be able to play it and to control the parameters used during the vibration (much like this Desmos calculation).
So far, this is my code:
from __future__ import print_function
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets
import matplotlib as mpl
%matplotlib inline
def f(n=1, v=0.2, L=2, t=0):
x = np.linspace(0, L, 2001)
func = np.sin((n*np.pi*x)/L)*np.cos((n*np.pi*v*t)/L)
plt.figure(figsize=(6,6))
ax1 = plt.plot(x, func)
plt.show()
interactive_plot = interactive(f, n=(0, 10, 1), v=(0.2, 5, 0.1), L=(0.2, 2, 0.1), t=(0, 10, 1))
output = interactive_plot.children[-1]
interactive_plot
I can control the wavefunction and all parameters, but I am not sure about what is the easiest way to animate it.
So far, I know that matplotlib can do it, but I am wondering if we have a more straightforward way to do animated interactive plots (using another package, maybe?).
Thanks in advance for any help.
Here my version of your script useing opencv and numpy.
You can set all params in realtime using keyboard.
More info in the code
import numpy as np
import cv2
def f(im,n=1, v=0.2, L=2, t=0):
x = np.linspace(0, L, 2001)
func = np.sin( (n*np.pi*x) / L ) * np.cos( (n*np.pi*v*t) / L )
ww2 = int(win_w/2)
wh2 = int(win_h/2)
scale = 100
for i in range(len(x)):
ix=x[i]
iy=func[i]
p = (int(ww2 + ix*scale) , int(wh2 + iy*scale))
cv2.circle( im, p ,1, (255,255,255) )
win_w=640
win_h=480
#params={
# "n":(0, 10, 1),
# "v":(0.2, 5, 0.1),
# "L":(0.2, 2, 0.1),
# "t":(0, 10, 1)
#}
params={
"n":[10],
"v":[5],
"L":[2],
"t":[0]
}
while True:
im = np.zeros( (win_h,win_w,3), dtype="uint8")
for i in range(len(params["n"])):
n=params["n"][i]
v=params["v"][i]
L=params["L"][i]
t=params["t"][i]
f(im,n,v,L,t)
cv2.imshow("f",im)
k = cv2.waitKey(33) & 0xFF
if k==ord('q'):break
# Parameter setting with the keyboard
# comment / uncomment this for animation:
params["t"][0]+=.001
# n param setting +,- use n,b
if k==ord('n'): params["n"][0]+=.001
if k==ord('b'): params["n"][0]-=.001
# v param setting +,- use v,c
if k==ord('v'): params["v"][0]+=.001
if k==ord('c'): params["v"][0]-=.001
# L param setting +,- use l,k
if k==ord('l'): params["L"][0]+=.001
if k==ord('k'): params["L"][0]-=.001
# t param setting +,- use t,r
if k==ord('t'): params["t"][0]+=.001
if k==ord('r'): params["t"][0]-=.001
print(params)
cv2.destroyAllWindows()
Take a look at the gif python package. I have only used it for simple gif saves. But it can be integrated with plotly or Altair, which may give better interactions.
Here is a simple plot I just made based on this YouTube video.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button
x = np.arange(0,np.pi,0.01)
y_base = 0.5*np.sin(x)
y = y_base+1
fig, ax = plt.subplots()
plt.subplots_adjust(left=0.1, bottom=0.35)
p, = plt.plot(x,y, linewidth=2, color='blue')
plt.axis([0,np.pi,0,2])
axSlider = plt.axes([0.1,0.2,0.8,0.05])
slider1 = Slider(axSlider, "Slider", valmin=-100, valmax=100)
def val_update(val):
yval = slider1.val/50
p.set_ydata(yval*y_base+1)
plt.draw()
slider1.on_changed(val_update)
plt.show()
I am trying to create a plotting object that produces an animated matplotlib pcolor plot with a polar projection. Currently the object can either create a set of polar plots or try to create an animation of those plots.
When creating the set of polar plots (but not the animation) the object works as planned.
The animation portion of the object is based on this example, which works on my system. Unfortunately the animation as implemented in my object is not working. There is a figure and an MP4 file produced for the animation but both the figure and the too-short animation both show just some mis-shaped axes.
Does anyone have a suggestion of how to capture this figure series in an animation when embedded in an object?
I am using python 3.7, matplotlib 3.03 on a windows 10 machine
The code for the object and the code to run its instantiation are given below.
class Polar_smudge(object):
# object for creating polar contour plots
def __init__(self, azimuth_grid, range_grid):
import numpy as np
self.azimuth_grid = np.deg2rad(azimuth_grid)
self.range_grid = range_grid
self.fig = None
self.ax = None
self.images = []
#------------------------------------------------------------------
def add_data(self, value_grid):
import numpy as np
self.value_grid = value_grid
self.value_grid[self.value_grid<=0] = np.nan
#------------------------------------------------------------------
def add_figure(self, value_grid):
import matplotlib.pyplot as plt
# make and set-up figure
fig, ax = plt.subplots(subplot_kw=dict(projection='polar'))
ax.set_theta_zero_location("N")
ax.set_theta_direction(-1)
ax.set_rlim([0,10])
# make plot
cax = ax.pcolor(self.azimuth_grid, self.range_grid, value_grid, cmap=plt.cm.viridis_r)
ax.grid()
plt.show()
#------------------------------------------------------------------
def start_figure(self):
import matplotlib.pyplot as plt
# make and set-up figure
if self.fig is None :
self.fig, self.ax = plt.subplots(111, subplot_kw=dict(projection='polar'))
self.ax[0].set_theta_zero_location("N")
self.ax[0].set_theta_direction(-1)
def update_figure(self, value_grid):
import matplotlib.pyplot as plt
# make figure and add to image list
self.images.append((self.ax[0].pcolor(self.azimuth_grid, self.range_grid, value_grid, cmap=plt.cm.viridis_r),))
def end_figure(self):
import matplotlib.animation as animation
# animate the figure list
im_ani = animation.ArtistAnimation(self.fig, self.images, interval=50, repeat_delay=3000,blit=True)
im_ani.save('smudge.mp4')
#============This runs the object ====================================
import numpy as np
azimuth_bins = np.linspace(0, 360, 360)
range_bins = np.linspace(0, 10, 30)
# make plotting azim range grids
range_grid, azimuth_grid = np.meshgrid(range_bins, azimuth_bins)
# this works but isnt what I want
good_smudge = Polar_smudge(azimuth_grid,range_grid)
for ix in range(3):
val_grid = np.random.randn(360,30)
good_smudge.add_figure(val_grid)
# this doesnt work
bad_smudge = Polar_smudge(azimuth_grid,range_grid)
bad_smudge.start_figure()
for ix in range(3):
val_grid = np.random.randn(360,30)
bad_smudge.update_figure(val_grid)
bad_smudge.end_figure()
In response to the comment from Earnest, I did some further refinement and it appears that the problem is not linked to being embedded in an object, and also that increasing the number of frames (to eg. 30) does not solve the problem. The code snippet below provides a more concise demonstration of the problem (but lacks the correctly produced figure output option).
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
azimuth_bins = np.linspace(0, 360, 60)
range_bins = np.linspace(0, 10, 30)
images = []
# make plotting azim range grids
range_grid, azimuth_grid = np.meshgrid(range_bins, azimuth_bins)
fig,ax = plt.subplots(111, subplot_kw=dict(projection='polar'))
ax[0].set_theta_zero_location("N")
ax[0].set_theta_direction(-1)
for ix in range(30):
val_grid = np.random.randn(60,30)
images.append((ax[0].pcolor(azimuth_grid, range_grid, val_grid, cmap=plt.cm.viridis_r),))
# animate the figure list
im_ani = animation.ArtistAnimation(fig, images, interval=50, repeat_delay=3000,blit=False)
im_ani.save('smudge2.mp4')
I would like to know if someone who dominate more advanced matplotlib could help me in this one. I have a heatmap, which could be simulated with the following code:
import numpy as np
import string
from matplotlib import pylab as plt
def random_letter(chars=string.ascii_uppercase, size=2):
char_arr = np.array(list(chars))
if size > 27:
size = 27
np.random.shuffle(char_arr)
return char_arr[:size]
data = np.random.poisson(1, (174, 40))
y_labels = [', '.join(x for x in random_letter()) for _ in range(174)]
y_labels = sorted(y_labels)
fig, ax = plt.subplots()
fig.set_size_inches(11.7, 16.5)
heatmap = ax.pcolor(data,
cmap=plt.cm.Blues,
vmin=data.min(),
vmax=data.max(),
edgecolors='white')
ax.set_xticks(np.arange(data.shape[1])+.5, minor=False);
ax.set_yticks(np.arange(data.shape[0])+.5, minor=False);
ax.set_xticklabels(np.arange(40), rotation=90);
ax.set_yticklabels(y_labels, fontsize=5);
cb = fig.colorbar(heatmap, shrink=0.33, aspect=10)
My need is to draw lines over the heatmap, to separate features over the ytickslabels as I show in the following image (in which i draw by hand):
Any one knows how to programmatically code matplotlib to do that?
I'll take the liberty to do write the full solution for #tcaswell, actually it only takes 7 more lines:
xl, xh=ax.get_xlim()
left=xl-(xh-xl)*0.1 #10% extension on each side
right=xh+(xh-xl)*0.1
Lines=ax.hlines([5,10,15,20], left, right, color='r', linewidth=1.2)
Lines.set_clip_on(False)
ax.set_xlim((xl, xh))
How does one set the color of a line in matplotlib with scalar values provided at run time using a colormap (say jet)? I tried a couple of different approaches here and I think I'm stumped. values[] is a storted array of scalars. curves are a set of 1-d arrays, and labels are an array of text strings. Each of the arrays have the same length.
fig = plt.figure()
ax = fig.add_subplot(111)
jet = colors.Colormap('jet')
cNorm = colors.Normalize(vmin=0, vmax=values[-1])
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=jet)
lines = []
for idx in range(len(curves)):
line = curves[idx]
colorVal = scalarMap.to_rgba(values[idx])
retLine, = ax.plot(line, color=colorVal)
#retLine.set_color()
lines.append(retLine)
ax.legend(lines, labels, loc='upper right')
ax.grid()
plt.show()
The error you are receiving is due to how you define jet. You are creating the base class Colormap with the name 'jet', but this is very different from getting the default definition of the 'jet' colormap. This base class should never be created directly, and only the subclasses should be instantiated.
What you've found with your example is a buggy behavior in Matplotlib. There should be a clearer error message generated when this code is run.
This is an updated version of your example:
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import matplotlib.cm as cmx
import numpy as np
# define some random data that emulates your indeded code:
NCURVES = 10
np.random.seed(101)
curves = [np.random.random(20) for i in range(NCURVES)]
values = range(NCURVES)
fig = plt.figure()
ax = fig.add_subplot(111)
# replace the next line
#jet = colors.Colormap('jet')
# with
jet = cm = plt.get_cmap('jet')
cNorm = colors.Normalize(vmin=0, vmax=values[-1])
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=jet)
print scalarMap.get_clim()
lines = []
for idx in range(len(curves)):
line = curves[idx]
colorVal = scalarMap.to_rgba(values[idx])
colorText = (
'color: (%4.2f,%4.2f,%4.2f)'%(colorVal[0],colorVal[1],colorVal[2])
)
retLine, = ax.plot(line,
color=colorVal,
label=colorText)
lines.append(retLine)
#added this to get the legend to work
handles,labels = ax.get_legend_handles_labels()
ax.legend(handles, labels, loc='upper right')
ax.grid()
plt.show()
Resulting in:
Using a ScalarMappable is an improvement over the approach presented in my related answer:
creating over 20 unique legend colors using matplotlib
I thought it would be beneficial to include what I consider to be a more simple method using numpy's linspace coupled with matplotlib's cm-type object. It's possible that the above solution is for an older version. I am using the python 3.4.3, matplotlib 1.4.3, and numpy 1.9.3., and my solution is as follows.
import matplotlib.pyplot as plt
from matplotlib import cm
from numpy import linspace
start = 0.0
stop = 1.0
number_of_lines= 1000
cm_subsection = linspace(start, stop, number_of_lines)
colors = [ cm.jet(x) for x in cm_subsection ]
for i, color in enumerate(colors):
plt.axhline(i, color=color)
plt.ylabel('Line Number')
plt.show()
This results in 1000 uniquely-colored lines that span the entire cm.jet colormap as pictured below. If you run this script you'll find that you can zoom in on the individual lines.
Now say I want my 1000 line colors to just span the greenish portion between lines 400 to 600. I simply change my start and stop values to 0.4 and 0.6 and this results in using only 20% of the cm.jet color map between 0.4 and 0.6.
So in a one line summary you can create a list of rgba colors from a matplotlib.cm colormap accordingly:
colors = [ cm.jet(x) for x in linspace(start, stop, number_of_lines) ]
In this case I use the commonly invoked map named jet but you can find the complete list of colormaps available in your matplotlib version by invoking:
>>> from matplotlib import cm
>>> dir(cm)
A combination of line styles, markers, and qualitative colors from matplotlib:
import itertools
import matplotlib as mpl
import matplotlib.pyplot as plt
N = 8*4+10
l_styles = ['-','--','-.',':']
m_styles = ['','.','o','^','*']
colormap = mpl.cm.Dark2.colors # Qualitative colormap
for i,(marker,linestyle,color) in zip(range(N),itertools.product(m_styles,l_styles, colormap)):
plt.plot([0,1,2],[0,2*i,2*i], color=color, linestyle=linestyle,marker=marker,label=i)
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.,ncol=4);
UPDATE: Supporting not only ListedColormap, but also LinearSegmentedColormap
import itertools
import matplotlib.pyplot as plt
Ncolors = 8
#colormap = plt.cm.Dark2# ListedColormap
colormap = plt.cm.viridis# LinearSegmentedColormap
Ncolors = min(colormap.N,Ncolors)
mapcolors = [colormap(int(x*colormap.N/Ncolors)) for x in range(Ncolors)]
N = Ncolors*4+10
l_styles = ['-','--','-.',':']
m_styles = ['','.','o','^','*']
fig,ax = plt.subplots(gridspec_kw=dict(right=0.6))
for i,(marker,linestyle,color) in zip(range(N),itertools.product(m_styles,l_styles, mapcolors)):
ax.plot([0,1,2],[0,2*i,2*i], color=color, linestyle=linestyle,marker=marker,label=i)
ax.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.,ncol=3,prop={'size': 8})
U may do as I have written from my deleted account (ban for new posts :( there was). Its rather simple and nice looking.
Im using 3-rd one of these 3 ones usually, also I wasny checking 1 and 2 version.
from matplotlib.pyplot import cm
import numpy as np
#variable n should be number of curves to plot (I skipped this earlier thinking that it is obvious when looking at picture - sorry my bad mistake xD): n=len(array_of_curves_to_plot)
#version 1:
color=cm.rainbow(np.linspace(0,1,n))
for i,c in zip(range(n),color):
ax1.plot(x, y,c=c)
#or version 2: - faster and better:
color=iter(cm.rainbow(np.linspace(0,1,n)))
c=next(color)
plt.plot(x,y,c=c)
#or version 3:
color=iter(cm.rainbow(np.linspace(0,1,n)))
for i in range(n):
c=next(color)
ax1.plot(x, y,c=c)
example of 3:
Ship RAO of Roll vs Ikeda damping in function of Roll amplitude A44