Without plt.legend() called, the plot gets displayed. With it, I just get:
<matplotlib.legend.Legend at 0x1189a404c50>
I'm working in JupyterLab, Python 3, Anaconda
I do not understand what is preventing legend from displaying. Without the last for loop iterating through xarray, i.e. if I load just one spectrum to plot, legend works fine. Any ideas? Thanks!
Here is the code:
import colour
from colour.plotting import *
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import matplotlib
import scipy.integrate as integrate
from scipy import interpolate
### Read Spectrum file ###
xarray = []
yarray = []
while True:
# Separator in CSV
separator = input("Separator: [, or tab or semicolon]")
if separator in {',','comma'}:
separator = ','
elif separator in {'tab','TAB'}:
separator = '\t'
elif separator in {';','semicolon'}:
separator = ';'
else:
print("Separator must be one of the listed")
# Header in CSV
headerskip = input("Header [y/n]")
if headerskip in {'y','yes','Y','Yes','YES'}:
headerskip = 1
elif headerskip in {'n','no','N','No','NO'}:
headerskip = 0
else:
print("Header?")
# Choose CSV file
filename = input("Filename")
try:
spectrum = pd.read_csv(filename, sep = separator, header = None, skiprows = headerskip, index_col=0)
except FileNotFoundError:
print("Wrong file or file path")
# Convert to our dictionary
spec = spectrum[1].to_dict() #functional dictionary
sample_sd_data = {int(k):v for k,v in spec.items()} # changes index to integer
# Do color calculations
sd = colour.SpectralDistribution(sample_sd_data)
cmfs = colour.STANDARD_OBSERVERS_CMFS['CIE 1931 2 Degree Standard Observer']
illuminant = colour.ILLUMINANTS_SDS['D65']
XYZ = colour.sd_to_XYZ(sd, cmfs, illuminant) #tristimulus values.
print(XYZ)
xy = colour.XYZ_to_xy(XYZ) # chromaticity coordinates
x, y = xy
print(xy)
xarray.append(x)
yarray.append(y)
# Query to add another file
addfile = input("Add another spectrum [y,n]")
if addfile in {'y','yes','Y','Yes','YES'}:
print("adding another file")
elif addfile in {'n','no','N','No','NO'}:
print("done with adding files")
break
else:
print("Add another file? Breaking loop")
break
# Plotting the *CIE 1931 Chromaticity Diagram*.
# The argument *standalone=False* is passed so that the plot doesn't get
# displayed and can be used as a basis for other plots.
#plot_single_sd(sd)
print(xarray)
print(yarray)
plot_chromaticity_diagram_CIE1931(standalone=False)
# Plotting the *CIE xy* chromaticity coordinates.
for i in range(len(xarray)):
x = xarray[i]
y = yarray[i]
plt.plot(x, y, '-p', color='gray',
markersize=15, linewidth=4,
markerfacecolor='None',
markeredgecolor='gray',
markeredgewidth=2,
label=str(i))
plt.plot(.3,.3, '-o', label='test')
# Customizing plot
plt.grid(True, linestyle=':')
plt.axis('equal') # disable this to go x to zero, however it will hide 500nm label
plt.xlim(0,.8)
plt.ylim(0,.9)
#plt.legend(framealpha=1, frameon=True, handlelength=0) # set handlelength to 0 to destroy line over the symbol
You need to call the magic function %matplotlib notebook or %matplotlib inline after your imports in jupyter.
Related
I am drawing streamplots using matplotlib, and exporting them to a vector format. However, I find the streamlines are exported as a series of separate lines - not joined objects. This has the effect of reducing the quality of the image, and making for an unwieldy file for further manipulation. An example; the following images are of a pdf generated by exportfig and viewed in Acrobat Reader:
This is the entire plot
and this is a zoom of the center.
Interestingly, the length of these short line segments is affected by 'density' - increasing the density decreases the length of the lines. I get the same behavior whether exporting to svg, pdf or eps.
Is there a way to get a streamplot to export streamlines as a single object, preferably as a curved line?
MWE
import matplotlib.pyplot as plt
import numpy as np
square_size = 101
x = np.linspace(-1,1,square_size)
y = np.linspace(-1,1,square_size)
u, v = np.meshgrid(-x,y)
fig, axis = plt.subplots(1, figsize = (4,3))
axis.streamplot(x,y,u,v)
fig.savefig('YourDirHere\\test.pdf')
In the end, it seemed like the best solution was to extract the lines from the streamplot object, and plot them using axis.plot. The lines are stored as individual segments with no clue as to which line they belong, so it is necessary to stitch them together into continuous lines.
Code follows:
import matplotlib.pyplot as plt
import numpy as np
def extract_streamlines(sl):
# empty list for extracted lines, flag
new_lines = []
for line in sl:
#ignore zero length lines
if np.array_equiv(line[0],line[1]):
continue
ap_flag = 1
for new_line in new_lines:
#append the line segment to either start or end of exiting lines, if either the star or end of the segment is close.
if np.allclose(line[0],new_line[-1]):
new_line.append(list(line[1]))
ap_flag = 0
break
elif np.allclose(line[1],new_line[-1]):
new_line.append(list(line[0]))
ap_flag = 0
break
elif np.allclose(line[0],new_line[0]):
new_line.insert(0,list(line[1]))
ap_flag = 0
break
elif np.allclose(line[1],new_line[0]):
new_line.insert(0,list(line[0]))
ap_flag = 0
break
# otherwise start a new line
if ap_flag:
new_lines.append(line.tolist())
return [np.array(line) for line in new_lines]
square_size = 101
x = np.linspace(-1,1,square_size)
y = np.linspace(-1,1,square_size)
u, v = np.meshgrid(-x,y)
fig_stream, axis_stream = plt.subplots(1, figsize = (4,3))
stream = axis_stream.streamplot(x,y,u,v)
np_new_lines = extract_streamlines(stream.lines.get_segments())
fig, axis = plt.subplots(1, figsize = (4,4))
for line in np_new_lines:
axis.plot(line[:,0], line[:,1])
fig.savefig('YourDirHere\\test.pdf')
A quick solution to this issue is to change the default cap styles of those tiny segments drawn by the streamplot function. In order to do this, follow the below steps.
Extract all the segments from the stream plot.
Bundle these segments through LineCollection function.
Set the collection's cap style to round.
Set the collection's zorder value smaller than the stream plot's default 2. If it is higher than the default value, the arrows of the stream plot will be overdrawn by the lines of the new collection.
Add the collection to the figure.
The solution of the example code is presented below.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.collections import LineCollection # Import LineCollection function.
square_size = 101
x = np.linspace(-1,1,square_size)
y = np.linspace(-1,1,square_size)
u, v = np.meshgrid(-x,y)
fig, axis = plt.subplots(1, figsize = (4,3))
strm = axis.streamplot(x,y,u,v)
# Extract all the segments from streamplot.
strm_seg = strm.lines.get_segments()
# Bundle segments with round capstyle. The `zorder` value should be less than 2 to not
# overlap streamplot's arrows.
lc = LineCollection(strm_seg, zorder=1.9, capstyle='round')
# Add the bundled segment to the subplot.
axis.add_collection(lc)
fig.savefig('streamline.pdf')
Additionally, if you want to have streamlines their line widths changing throughout the graph, you have to extract them and append this information to LineCollection.
strm_lw = strm.lines.get_linewidths()
lc = LineCollection(strm_seg, zorder=1.9, capstyle='round', linewidths=strm_lw)
Sadly, the implementation of a color map is not as straight as the above solution. Therefore, using a color map with above approach will not be very pleasing. You can still automate the coloring process, as shown below.
strm_col = strm.lines.get_color()
lc = LineCollection(strm_seg, zorder=1.9, capstyle='round', color=strm_col)
Lastly, I opened a pull request to change the default capstyle option in the matplotlib repository, it can be seen here. You can apply this commit using below code too. If you prefer to do so, you do not need any tricks explained above.
diff --git a/lib/matplotlib/streamplot.py b/lib/matplotlib/streamplot.py
index 95ce56a512..0229ae107c 100644
--- a/lib/matplotlib/streamplot.py
+++ b/lib/matplotlib/streamplot.py
## -222,7 +222,7 ## def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None,
arrows.append(p)
lc = mcollections.LineCollection(
- streamlines, transform=transform, **line_kw)
+ streamlines, transform=transform, **line_kw, capstyle='round')
lc.sticky_edges.x[:] = [grid.x_origin, grid.x_origin + grid.width]
lc.sticky_edges.y[:] = [grid.y_origin, grid.y_origin + grid.height]
if use_multicolor_lines:
I want to draw multiple ternary graphs and thought to do this using matplotlib's subplot.
I'm just getting empty 'regular' plots though, not the ternary graphs I want in there. I found the usage of
figure, ax = plt.subplots()
tax = ternary.TernaryAxesSubplot(ax=ax)
so this seems to be possible, but can't really find out how to get this working. Any ideas?
Code I'm using:
I'm using a for loop as the data has columns named tria1-a, tria2-a, etc for the different triads
import ternary
import matplotlib.pyplot as plt
import pandas as pd
#configure file to import.
filename = 'somecsv.csv'
filelocation = 'location'
dfTriad = pd.read_csv(filelocation+filename)
# plot the data
scale = 33
figure, ax = plt.subplots()
tax = ternary.TernaryAxesSubplot(ax=ax, scale=scale)
figure.set_size_inches(10, 10)
tax.set_title("Scatter Plot", fontsize=20)
tax.boundary(linewidth=2.0)
tax.gridlines(multiple=1, color="blue")
tax.legend()
tax.ticks(axis='lbr', linewidth=1, multiple=5)
tax.clear_matplotlib_ticks()
#extract the xyz columns for the triads from the full dataset
for i in range(1,6) :
key_x = 'tria'+ str(i) + '-a'
key_y = 'tria' + str(i) + '-b'
key_z = 'tria' + str(i) + '-c'
#construct dataframe from the extracted xyz columns
dfTriad_data = pd.DataFrame(dfTriad[key_x], columns=['X'])
dfTriad_data['Y'] = dfTriad[key_y]
dfTriad_data['Z'] = dfTriad[key_z]
#create list of tuples from the constructed dataframe
triad_data = [tuple(x) for x in dfTriad_data.to_records(index=False)]
plt.subplot(2, 3, i)
tax.scatter(triad_data, marker='D', color='green', label="")
tax.show()
I had the same problem and could solve it by first "going" into the subplot, then creating the ternary figure in there by giving plt.gca() as keyword argument ax:
plt.subplot(2,2,4, frameon = False)
scale = 10
plt.gca().get_xaxis().set_visible(False)
plt.gca().get_yaxis().set_visible(False)
figure, tax = ternary.figure(ax = plt.gca(), scale = scale)
#now you can use ternary normally:
tax.line(scale * np.array((0.5,0.5,0.0)), scale*np.array((0.0, 0.5, 0.5)))
tax.boundary(linewidth=1.0)
#...
I have made an animation from a set of images like this (10 snapshots):
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
import time
infile = open ('out.txt')
frame_counter = 0
N_p = 100
N_step = 10
N_line = N_p*N_step
for s in xrange(N_step):
x, y = [], []
for i in xrange(N_p):
data = infile.readline()
raw = data.split()
x.append(float(raw[0]))
y.append(float(raw[1]))
xnp = np.array(x)
ynp = np.array(y)
fig = plt.figure(0)
ax = fig.add_subplot(111, aspect='equal')
for x, y in zip(xnp, ynp):
cir = Circle(xy = (x, y), radius = 1)
cir.set_facecolor('red')
ax.add_artist(cir)
cir.set_clip_box(ax.bbox)
ax.set_xlim(-10, 150)
ax.set_ylim(-10, 150)
fig.savefig("step.%04d.png" % frame_counter)
ax.remove()
frame_counter +=1
Now I want to add a legend to each image showing the time step.
For doing this I must set legends to each of these 10 images. The problem is that I have tested different things like ax.set_label , cir.set_label, ...
and I get errors like this:
UserWarning: No labelled objects found. Use label='...' kwarg on individual plots
According to this error I must add label to my individual plots, but since this is a plot of Artists, I don't know how I can do this.
If for whatever reason you need a legend, you can show your Circle as the handle and use some text as the label.
ax.legend(handles=[cir], labels=["{}".format(frame_counter)])
If you don't really need a legend, you can just use some text to place inside the axes.
ax.text(.8,.8, "{}".format(frame_counter), transform=ax.transAxes)
I want to make a real time plot of temeperature vs. iteration but I will end up having so many points that it would not make sense to have them on the same plot. Does anyone know of any good ways to only show the most recent (lets say 100) data points so that after the first 100 the plot starts to replace the old data points with the new data points?
I thought it would be easier without code but here is the real time ploting that I have now.
from time import sleep
from labjack import ljm
import pylab as pl
import matplotlib.pyplot as plt
# Open T7 over USB
handle = ljm.openS("T7", "USB", "ANY")
# Configure thermocouple line on AIN0
ljm.eWriteName(handle, "AIN0_EF_INDEX", 22) # Feature index for type K thermocouple
ljm.eWriteName(handle, "AIN0_EF_CONFIG_A", 1) # Units. Default = Kelvin. 1 = Celsius. 2 = Fahrenheit.
ljm.eWriteName(handle, "AIN0_EF_CONFIG_B", 60052) # CJC source, address for device temperature sensor
ljm.eWriteName(handle, "AIN0_EF_CONFIG_D", 1.0) # Slope for CJC reading
ljm.eWriteName(handle, "AIN0_EF_CONFIG_E", 0.0) # Offset for CJC reading
temperature = []
x = list()
y = list()
x1 = list()
y1 = list()
dT_tol = .5
plt.ion()
fig=plt.figure()
# Read loop
for i in range(60):
# Get the thermocouple reading on AIN0.
tempC = ljm.eReadName(handle, "AIN0_EF_READ_A")
temperature.append(tempC)
dT = temperature[i]-temperature[i-1]
if -dT_tol<dT<dT_tol:
print "Temperature:","%.3f"% temperature[i]," " "dT:", "%.3f"% dT, " " "Steady State"
sleep(1)
else:
print "Temperature:","%.3f"% temperature[i]," " "dT:", "%.3f"% dT
sleep(1)
#Plotting
plt.figure(1)
plt.subplot(211)
plt.axis([0,60,0,80])
x.append(i)
y.append(temperature[i])
plt.scatter(x,y)
plt.ylabel('Temperature (C)')
plt.subplot(212)
plt.axis([0,60,-4,4])
x1.append(i)
y1.append(dT)
plt.scatter(x1,y1,zorder = 2)
#Set dT steady state boundaries
plt.axhspan(-dT_tol, dT_tol, color='#87CEFA', alpha=1, zorder = 1)
plt.ylabel('dT')
plt.xlabel('Time (s)')
plt.show()
plt.pause(.0001)
# Close handle
ljm.close(handle)
you can use list of array to show the all data given for a while.
for example
tempaturelist=[]
for i in range(50):
enter code here
tempaturelist.append(tempature)
print tempaturelist
There is a overwriting if you use same variable for all values.
Thats why you see only most recent values .
Edit:
You might consider using a deque object to improve performance. It is like a stack/queue hybrid, which may be faster than numpy.roll. I left the old code in for example..
from collections import deque
You can use something like this, just update it to fit your needs ( I am just going to make up random data because im too lazy to use your example)
import numpy as np
import pylab as plt
buffer_size = 100 # how many data points you want to plot at any given time
#data_buffer = np.zeros( buffer_size) # this is where you will keep the latest data points
data_buffer = deque()
for i in range( buffer_size):
data_buffer.append(0)
temperatures = np.random.random( 200 ) # some random temperature data, i dunno
# setup the figure
fig = plt.figure(1)
plt.suptitle('Previous %d temperatures'%buffer_size, fontsize=12)
ax = plt.gca()
for i,Temp in enumerate( temperatures ):
#data_buffer = np.roll( data_buffer, shift=-1)
#data_buffer[ -1] = Temp
data_buffer.popleft()
data_buffer.append( Temp)
ax.plot( data_buffer, 'rs', ms=6) # whatever style you want...
plt.draw()
plt.pause(0.01)
plt.cla() # clears the axis
I won't post the output of this plot because it will always be changing, but try it yourself ;)
I'm not sure if this questions has been asked - couldn't find a good practice one so far.
I have two python files with exactly same package import, and a number of different methods. A couple of variables vary only, and others are the same.
IF I need to make a change to one file, I have to go to the other one to apply the same changes which doesn't seem a robust way.
I really want to keep these files separate (in two files). I never had a good grasp of idea of class. Should I need to make a class in first file having all methods, loops, variables, and call it in the second, I can then overwrite the variables if need be?
This is how my first file looks like, apologies I should have spent some time to make it readable, but it's just to give you an idea about the structure. This code actually plots up a number of matplotlib figures. The second file would have different input files (CSV files) which then plot up different figures.
import csv
import datetime
import pylab
import sys
import time
from inspect import getsourcefile
from os.path import abspath
import matplotlib.dates as mdates
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import style
from matplotlib.backends.backend_pdf import PdfPages
def get_mul_list(*args):
return map(list, zip(*args))
def str2float(s):
if not s == '':
s = (float(s))
else:
s = np.nan
return s
def clean_nans(x, y, num_nan_gap=24):
x_clean, y_clean = [], []
cnt = 0
for _x, _y in zip(x, y):
if np.isnan(_y):
cnt += 1
if cnt == num_nan_gap:
# on the 5th nan, put it in the list to break line
x_clean.append(_x)
y_clean.append(_y)
continue
cnt = 0
x_clean.append(_x)
y_clean.append(_y)
return x_clean, y_clean
def csv_store_in_dict(filepath, mode):
csv_data = open(filepath, mode)
data = list(csv.reader(csv_data))
csv_imported_in_dict = dict(zip(data[0], get_mul_list(*data[1:])))
return csv_imported_in_dict
colors_list = ['deeppink', 'aquamarine', 'yellowgreen', 'orangered', 'darkviolet',
'darkolivegreen', 'lightskyblue', 'teal', 'seagreen', 'olivedrab', 'red', 'indigo', 'goldenrod', 'firebrick',
'slategray', 'cornflowerblue', 'darksalmon', 'blue', 'khaki', 'wheat', 'dodgerblue', 'moccasin', 'sienna',
'darkcyan']
current_py_filepath = abspath(getsourcefile(lambda: 0)) # python source file path for figure footnote
kkk_dict = csv_store_in_dict('CSV/qry_WatLvl_kkk_xlsTS_1c_v4.csv', 'r') # all WL kkk data stored in a dictionary
yyyddd_dict = csv_store_in_dict('CSV/qry_WatLvl_TimeSeries2_v2.csv', 'r') # all WL kkk data stored in a dictionary
XX_info_dict = csv_store_in_dict('CSV/XX_info.csv', 'r') # XX_name, XX_group_name, BB_Main, CC, dddd
XX_groups_chartE = ('XXH_05',
'XXH_16',
'XXH_11',
'DXX_27',
'DXX_22',
'DXX_21',
'DXX_09',
'DXX_07',
'DXX_01',
'DXX_05',)
y_range = [[5,10], # chart 1
[7,12], # chart 2
[3,8], # chart 3
[7,12], # chart 4
[5,10], # chart 5
[20,50], # chart 6
[12,22], # chart 7
[5,25], # chart 8
[10,15], # chart 9
[22,42]] # chart 10
# Date conversion
x_kkk_date = []
x_yyy_date = []
x_kkk = kkk_dict["DateTime"]
x_yyyddd = yyyddd_dict["DateTime"]
for i in x_kkk:
x_kkk_date.append(datetime.datetime.strptime(i, "%d/%m/%Y %H:%M:%S"))
for i in x_yyyddd:
x_yyy_date.append(datetime.datetime.strptime(i, "%d/%m/%Y %H:%M:%S"))
# plotting XX groups
XXs_curr_grp = []
chart_num = 1
for XX_gr_nam in XX_groups_chartE:
for count, elem in enumerate(XX_info_dict['XX_group_name']):
if elem == XX_gr_nam:
XXs_curr_grp.append(XX_info_dict['XX_name'][count])
fig = plt.figure(figsize=(14, 11))
col_ind = 0
for XX_v in XXs_curr_grp:
y_kkk = kkk_dict[XX_v]
y_yyyddd = yyyddd_dict[XX_v]
y_kkk_num = [str2float(i) for i in y_kkk]
y_yyyddd_num = [str2float(i) for i in y_yyyddd]
ind_XX = XX_info_dict["XX_name"].index(XX_v)
BB_Main = XX_info_dict["BB_Main"][ind_XX]
CC = XX_info_dict["CC"][ind_XX]
dddd = XX_info_dict["dddd"][ind_XX]
def label_pl(d_type):
label_dis = "%s (%s, %s / %s)" % (XX_v, BB_Main, CC, d_type)
return label_dis
x_kkk_date_nan_cln, y_kkk_num_nan_cln = clean_nans(x_kkk_date, y_kkk_num, 200)
plt.plot_date(x_kkk_date_nan_cln, y_kkk_num_nan_cln, '-', markeredgewidth=0,
label=label_pl("kkk data"), color=colors_list[col_ind]) # c = col_rand
plt.scatter(x_yyy_date, y_yyyddd_num, label=label_pl("yyy ddds"), marker='x', linewidths=2,
s=50, color=colors_list[col_ind])
col_ind += 1
XX_grp_title = XX_gr_nam.replace("_", "-")
plt.title("kkk Levels \n" + XX_grp_title + " Group", fontsize=20)
plt.ylabel('wwL (mmm)')
plt.legend(loc=9, ncol=2, prop={'size': 8})
plt.figtext(0.05, 0.05, current_py_filepath, horizontalalignment='left', fontsize=8) # footnote for file path
plt.figtext(0.95, 0.05, 'Chart E%s' % (chart_num,), horizontalalignment='right', fontsize=12) # chart number
plt.figtext(0.95, 0.95, datetime.date.today(), horizontalalignment='right', fontsize=8)
# FIGURE FORMATTING
myFmt = mdates.DateFormatter('%d/%m/%Y')
ax = plt.gca()
ax.xaxis.set_major_formatter(myFmt)
plt.gcf().autofmt_xdate()
ax.set_ylim(y_range[chart_num-1])
plt.grid()
fig.tight_layout()
plt.subplots_adjust(left=0.05, right=0.95, top=0.9, bottom=0.15)
fig_pdf_file = "PDF/OXX_grp_page %s.pdf" % (chart_num,)
fig.savefig(fig_pdf_file)
XXs_curr_grp = []
chart_num += 1 # assumed charts numbering is the same as the order of plotting
plt.show()
No, you do not need to define a class. You need to remove the shared functions from one file, and have it import the other. An import statement can import installed python packages, but also python files. Use it like this:
# myfile.py
def f(x):
return x * 2
# main.py
import myfile
myfile.f(2)
Note that for this example, both files must be in the same directory.
However, if you would like to store myfile.py in a different directory, i.e. in this hierarchy:
my_project
----main.py
----my_modules
----myfile.py
----__init__.py
Simply create an empty __init__.py file in the 'my_modules' directory, and change your import statement to reflect import my_modules.myfile.