1 """
2 Line-based and matrix-based plotting commands using MatPlotLib.
3
4 Before importing this file, you will probably want to do something
5 like:
6
7 from matplotlib import rcParams
8 rcParams['backend']='TkAgg'
9
10 to select a backend, or else select an appropriate one in your
11 matplotlib.rc file (if any). There are many backends available for
12 different GUI or non-GUI uses.
13
14 $Id: pylabplots.py 10921 2010-02-16 18:46:32Z antolikjan $
15 """
16 __version__='$Revision: 10921 $'
17
18 import re
19 import os
20 import copy
21 import errno
22
23 try:
24 import matplotlib
25 import pylab
26 except ImportError:
27 print "Warning: Could not import matplotlib; pylab plots will not work."
28
29 import numpy
30 from math import pi
31
32 from numpy.oldnumeric import arange, sqrt, array, floor, transpose, argmax, argmin, cos, sin, log10, Float
33 from numpy import outer,arange,ones,zeros
34
35 from numpy.fft.fftpack import fft2
36 from numpy.fft.helper import fftshift
37 from numpy import abs
38
39 import topo
40 from topo.base.sheetview import SheetView
41 from topo.base.arrayutil import octave_output, centroid, wrap
42 from topo.base.sheet import Sheet
43 from topo.base.arrayutil import wrap
44 from topo.misc.filepath import normalize_path
45 from topo.misc.util import frange
46 import topo.analysis.vision
47 from topo.plotting.plot import make_template_plot, Plot
48 import param
49 from param import ParameterizedFunction
50 from param.parameterized import ParamOverrides
51 from topo.pattern.basic import SineGrating, RawRectangle, OrientationContrast
52 from topo.plotting.plotgroup import create_plotgroup, plotgroups
53 from topo.base.cf import CFSheet
54
55 from topo.analysis.featureresponses import Feature, PatternPresenter, FeatureCurves
56 from topo.analysis.featureresponses import SinusoidalMeasureResponseCommand, PositionMeasurementCommand, SingleInputResponseCommand, FeatureCurveCommand, UnitCurveCommand
57
58
59
61 """Parameterized command for plotting using Matplotlib/Pylab."""
62
63 file_dpi = param.Number(
64 default=100.0,bounds=(0,None),softbounds=(0,1000),doc="""
65 Default DPI when rendering to a bitmap.
66 The nominal size * the dpi gives the final image size in pixels.
67 E.g.: 4"x4" image * 80 dpi ==> 320x320 pixel image.""")
68
69 file_format = param.String(default="png",doc="""
70 Which image format to use when saving images.
71 The output can be png, ps, pdf, svg, or any other format
72 supported by Matplotlib.""")
73
74
75
76
77
78
79 filename = param.String(default=None,doc="""
80 Optional base of the filename to use when saving images;
81 if None the plot will be displayed interactively.
82
83 The actual name is constructed from the filename base plus the
84 suffix plus the current simulator time plus the file_format.""")
85
86 filename_suffix = param.String(default="",doc="""
87 Optional suffix to be used for disambiguation of the filename.""")
88
89 title = param.String(default=None,doc="""
90 Optional title to be used when displaying the plot interactively.""")
91
92 __abstract = True
93
94
96 """
97 Helper function to set the title (if not None) of this PyLab plot window.
98 """
99
100
101
102
103
104
105
106
107 if title is not None:
108 try:
109 manager = pylab.get_current_fig_manager()
110 manager.window.title(title)
111 except:
112 pass
113
114
131
132
133
135 """
136 Simple line plotting for any vector or list of numbers.
137
138 Intended for interactive debugging or analyzing from the command
139 prompt. See MatPlotLib's pylab functions to create more elaborate
140 or customized plots; this is just a simple example.
141
142 An optional string can be supplied as a title for the figure, if
143 desired. At present, this is only used for the window, not the
144 actual body of the figure (and will thus not appear when the
145 figure is saved).
146
147 The style argument allows different line/linespoints style for
148 the plot: 'r-' for red solid line, 'bx' for blue x-marks, etc.
149 See http://matplotlib.sourceforge.net/matplotlib.pylab.html#-plot
150 for more possibilities.
151
152 The label argument can be used to identify the line in a figure legend.
153
154 Ordinarily, the x value for each point on the line is the index of
155 that point in the vec array, but a explicit list of xvalues can be
156 supplied; it should be the same length as vec.
157
158 Execution of multiple vectorplot() commands with different styles
159 will result in all those styles overlaid on a single plot window.
160 """
161
162
163 - def __call__(self,vec,xvalues=None,style='-',label=None,**params):
173
174
175
177 """
178 Simple plotting for any matrix as a bitmap with axes.
179
180 Like MatLab's imagesc, scales the values to fit in the range 0 to 1.0.
181 Intended for interactive debugging or analyzing from the command
182 prompt. See MatPlotLib's pylab functions to create more elaborate
183 or customized plots; this is just a simple example.
184 """
185
186 plot_type = param.Callable(default=pylab.gray,doc="""
187 Matplotlib command to generate the plot, e.g. pylab.gray or pylab.hsv.""")
188
189
190 - def __call__(self,mat,aspect=None,colorbar=True,**params):
191 p=ParamOverrides(self,params)
192
193 p.plot_type()
194 pylab.figure(figsize=(5,5))
195 pylab.imshow(mat,interpolation='nearest',aspect=aspect)
196 if colorbar and (mat.min()!= mat.max()): pylab.colorbar()
197 self._generate_figure(p)
198
199
201 """
202 Simple plotting for any matrix as a 3D wireframe with axes.
203
204 Uses Matplotlib's beta-quality features for 3D plotting. These
205 usually work fine for wireframe plots, although they don't always
206 format the axis labels properly, and do not support removal of
207 hidden lines. Note that often the plot can be rotated within the
208 window to make such problems go away, and then the best result can
209 be saved if needed.
210
211 Other than the default "wireframe", the type can be "contour" to
212 get a contour plot, or "surface" to get a solid surface plot, but
213 surface plots currently fail in many cases, e.g. for small
214 matrices.
215
216 If you have trouble, you can try matrixplot3d_gnuplot instead.
217 """
218
219
220 - def __call__(self,mat,type="wireframe",**params):
221 p=ParamOverrides(self,params)
222
223 from mpl_toolkits.mplot3d import axes3d
224
225 fig = pylab.figure()
226 ax = axes3d.Axes3D(fig)
227
228
229 rn,cn = mat.shape
230 c = outer(ones(rn),arange(cn*1.0))
231 r = outer(arange(rn*1.0),ones(cn))
232
233 if type=="wireframe":
234 ax.plot_wireframe(r,c,mat)
235 elif type=="surface":
236
237 ax.plot_surface(r,c,mat)
238 elif type=="contour":
239
240 ax.contour3D(r,c,mat)
241 else:
242 raise ValueError("Unknown plot type "+str(type))
243
244 ax.set_xlabel('R')
245 ax.set_ylabel('C')
246 ax.set_zlabel('Value')
247
248 self._generate_figure(p)
249
250
251
253 """
254 Simple plotting for any matrix as a 3D surface with axes.
255
256 Currently requires the gnuplot-py package to be installed, plus
257 the external gnuplot program; likely to be removed once Matplotlib
258 supports 3D plots better.
259
260 Unlikely to work on non-UNIX systems.
261
262 Should return when it completes, but for some reason the Topographica
263 prompt is not available until this command finishes.
264 """
265 import Gnuplot
266 import Numeric
267 from os import system
268
269 psviewer="gv"
270 g = Gnuplot.Gnuplot(debug=0)
271 r,c = mat.shape
272 x = arange(r*1.0)
273 y = arange(c*1.0)
274
275
276 m = numpy.asarray(mat,dtype="float32").tolist()
277
278 g("set data style lines")
279 g("set hidden3d")
280 g("set xlabel 'R'")
281 g("set ylabel 'C'")
282 g("set zlabel 'Value'")
283 if title: g.title(title)
284
285 if outputfilename:
286 g("set terminal postscript eps color solid 'Times-Roman' 14")
287 g("set output '"+outputfilename+"'")
288 g.splot(Gnuplot.GridData(m,x,y, binary=1))
289
290 system(psviewer+" "+outputfilename+" &")
291
292 else:
293 g.splot(Gnuplot.GridData(m,x,y, binary=1))
294 raw_input('Please press return to continue...\n')
295
296
297
299 """
300 Compute and plot the histogram of the supplied data.
301
302 See help(pylab.hist) for help on the histogram function itself.
303
304 If given, colors is an iterable collection of matplotlib.colors
305 (see help (matplotlib.colors) ) specifying the bar colors.
306
307 Example use:
308 histogramplot([1,1,1,2,2,3,4,5],title='hist',colors='rgb',bins=3,normed=1)
309 """
310
311
312 - def __call__(self,data,colors=None,**params):
313 p=ParamOverrides(self,params,allow_extra_keywords=True)
314
315 pylab.figure(figsize=(4,2))
316 n,bins,bars = pylab.hist(data,**(p.extra_keywords()))
317
318
319
320 if colors: [bar.set_fc(color) for bar,color in zip(bars,colors)]
321
322 self._generate_figure(p)
323
324
325
327 """
328 Compute and show the gradient plot of the supplied data.
329 Translated from Octave code originally written by Yoonsuck Choe.
330
331 If the data is specified to be cyclic, negative differences will
332 be wrapped into the range specified (1.0 by default).
333 """
334
335
336 - def __call__(self,data,cyclic=True,cyclic_range=1.0,**params):
337 p=ParamOverrides(self,params)
338
339 r,c = data.shape
340 dx = numpy.diff(data,1,axis=1)[0:r-1,0:c-1]
341 dy = numpy.diff(data,1,axis=0)[0:r-1,0:c-1]
342
343 if cyclic:
344
345 dx = wrap(0,cyclic_range,dx)
346 dy = wrap(0,cyclic_range,dy)
347
348
349
350 dx = 0.5*cyclic_range-abs(dx-0.5*cyclic_range)
351 dy = 0.5*cyclic_range-abs(dy-0.5*cyclic_range)
352
353 super(gradientplot,self).__call__(sqrt(dx*dx+dy*dy),**p)
354
355
356
358 """
359 Compute and show the 2D Fast Fourier Transform (FFT) of the supplied data.
360
361 Example:: fftplot(topo.sim["V1"].sheet_views["OrientationPreference"].view()[0],filename="out")
362 """
363
365 p=ParamOverrides(self,params)
366 fft_plot=1-abs(fftshift(fft2(data-0.5, s=None, axes=(-2,-1))))
367 super(fftplot,self).__call__(fft_plot,**p)
368
369
370
372 """
373 Plots the activity in a sheet.
374
375 Gets plot's extent from sheet.bounds.aarect(). Adds a title and
376 allows the selection of a colormap. If activity is not given,
377 the sheet's current activity is used.
378 """
379
380
381
382 - def __call__(self,sheet,activity=None,cmap=None,**params):
383 p=ParamOverrides(self,params)
384
385 l,b,r,t = sheet.bounds.aarect().lbrt()
386 if activity is None:
387 activity = sheet.activity
388 if cmap is None:
389 cmap=pylab.cm.Greys
390 pylab.imshow(activity, extent=(l,r,b,t),cmap=cmap)
391
392 self._generate_figure(p)
393
394
395
397 """
398 By default, plot the XPreference and YPreference preferences for all
399 Sheets for which they are defined, using MatPlotLib.
400
401 If sheet_views other than XPreference and YPreference are desired,
402 the names of these can be passed in as arguments.
403 """
404
405 xsheet_view_name = param.String(default='XPreference',doc="""
406 Name of the SheetView holding the X position locations.""")
407
408 ysheet_view_name = param.String(default='YPreference',doc="""
409 Name of the SheetView holding the Y position locations.""")
410
411 axis = param.Parameter(default=[-0.5,0.5,-0.5,0.5],doc="""
412 Four-element list of the plot bounds, i.e. [xmin, xmax, ymin, ymax].""")
413
415 p=ParamOverrides(self,params)
416
417 for sheet in topo.sim.objects(Sheet).values():
418 if ((p.xsheet_view_name in sheet.sheet_views) and
419 (p.ysheet_view_name in sheet.sheet_views)):
420
421 x = sheet.sheet_views[p.xsheet_view_name].view()[0]
422 y = sheet.sheet_views[p.ysheet_view_name].view()[0]
423
424 pylab.figure(figsize=(5,5))
425
426
427
428
429
430
431
432 isint=pylab.isinteractive()
433 pylab.ioff()
434 for r,c in zip(y,x):
435 pylab.plot(c,r,"k-")
436 for r,c in zip(transpose(y),transpose(x)):
437 pylab.plot(c,r,"k-")
438
439 pylab.xlabel('x')
440 pylab.ylabel('y')
441
442
443
444 pylab.axis(p.axis)
445 p.title='Topographic mapping to '+sheet.name+' at time '+topo.sim.timestr()
446
447 if isint: pylab.ion()
448 p.filename_suffix="_"+sheet.name
449 self._generate_figure(p)
450
451
453 """
454 Use matplotlib to make a plot combining a bitmap and line-based overlays.
455 """
456
457 plot_template = param.List(default=[{'Hue':'OrientationPreference'}],doc="""
458 Template for the underlying bitmap plot.""")
459
460 overlay = param.List(default=[('contours','OcularPreference',0.5,'black'),
461 ('arrows','DirectionPreference','DirectionSelectivity','white')],doc="""
462 List of overlaid plots, where each list item may be a 4-tuple
463 specifying either a contour line or a field of arrows::
464
465 ('contours',map-name,contour-value,line-color)
466
467 ('arrows',arrow-location-map-name,arrow-size-map-name,arrow-color)
468
469 Any number or combination of contours and arrows may be supplied.""")
470
471 normalize = param.Boolean(default='Individually',doc="""
472 Type of normalization, if any, to use. Options include 'None',
473 'Individually', and 'AllTogether'. See
474 topo.plotting.plotgroup.TemplatePlotGroup.normalize for more
475 details.""")
476
477
479 p=ParamOverrides(self,params)
480
481 for template in p.plot_template:
482
483 for sheet in topo.sim.objects(Sheet).values():
484 name=template.keys().pop(0)
485 plot=make_template_plot(template,sheet.sheet_views,sheet.xdensity,sheet.bounds,p.normalize,name=template[name])
486 if plot:
487 bitmap=plot.bitmap
488 pylab.figure(figsize=(5,5))
489 isint=pylab.isinteractive()
490 pylab.ioff()
491
492 pylab.imshow(bitmap.image,origin='lower',interpolation='nearest')
493 pylab.axis('off')
494
495 for (t,pref,sel,c) in p.overlay:
496 v = pylab.flipud(sheet.sheet_views[pref].view()[0])
497
498 if (t=='contours'):
499 pylab.contour(v,[sel,sel],colors=c,linewidths=2)
500
501 if (t=='arrows'):
502 s = pylab.flipud(sheet.sheet_views[sel].view()[0])
503 scale=int(pylab.ceil(log10(len(v))))
504 X=pylab.array([x for x in xrange(len(v)/scale)])
505 v_sc=pylab.zeros((len(v)/scale,len(v)/scale))
506 s_sc=pylab.zeros((len(v)/scale,len(v)/scale))
507 for i in X:
508 for j in X:
509 v_sc[i][j]=v[scale*i][scale*j]
510 s_sc[i][j]=s[scale*i][scale*j]
511 pylab.quiver(scale*X,scale*X,-cos(2*pi*v_sc)*s_sc,-sin(2*pi*v_sc)*s_sc,color=c,edgecolors=c,minshaft=3,linewidths=1)
512
513 p.title='%s overlaid with %s at time %s' %(plot.name,pref,topo.sim.timestr())
514 if isint: pylab.ion()
515 p.filename_suffix="_"+sheet.name
516 self._generate_figure(p)
517
518
519
521 """
522 Plot a tuning curve for a feature, such as orientation, contrast, or size.
523
524 The curve datapoints are collected from the curve_dict for
525 the units at the specified coordinates in the specified sheet
526 (where the units and sheet may be set by a GUI, using
527 topo.analysis.featureresponses.UnitCurveCommand.sheet and
528 topo.analysis.featureresponses.UnitCurveCommand.coords,
529 or by hand).
530 """
531
532 coords = param.List(default=[(0,0)],doc="""
533 List of coordinates of units to measure.""")
534
535 sheet = param.ObjectSelector(
536 default=None,doc="""
537 Name of the sheet to use in measurements.""")
538
539 x_axis = param.String(default="",doc="""
540 Feature to plot on the x axis of the tuning curve""")
541
542
543
544 plot_type = param.Callable(default=pylab.plot,doc="""
545 Matplotlib command to generate the plot.""")
546
547 unit = param.String(default="",doc="""
548 String to use in labels to specify the units in which curves are plotted.""")
549
550 __abstract = True
551
552
555
557 n = n % len(seq)
558 return seq[n:] + seq[:n]
559
561 """Return the x, y, and x ticks values for the specified curve from the curve_dict"""
562 x_values=sorted(curve.keys())
563 y_values=[curve[key].view()[0][i_value,j_value] for key in x_values]
564 return x_values,y_values,x_values
565
567 x = [];
568 y= [];
569 num_ticks = 5;
570 y.append(ticks[0])
571 x.append(0)
572 for i in xrange(0,num_ticks):
573 y.append(y[-1]+numpy.pi/(num_ticks+1));
574 x.append(x[-1]+numpy.pi/(num_ticks+1));
575 y.append(y[-1]+numpy.pi/(num_ticks+1));
576 x.append(3.14)
577 return (x,y)
578
579
581 p=ParamOverrides(self,params)
582 sheet = p.sheet
583 for coordinate in p.coords:
584 i_value,j_value=sheet.sheet2matrixidx(coordinate[0],coordinate[1])
585
586 f = pylab.figure(figsize=(7,7))
587 isint=pylab.isinteractive()
588 pylab.ioff()
589
590 pylab.ylabel('Response',fontsize='large')
591 pylab.xlabel('%s (%s)' % (p.x_axis.capitalize(),p.unit),fontsize='large')
592 pylab.title('Sheet %s, coordinate(x,y)=(%0.3f,%0.3f) at time %s' %
593 (sheet.name,coordinate[0],coordinate[1],topo.sim.timestr()))
594 p.title='%s: %s Tuning Curve' % (topo.sim.name,p.x_axis.capitalize())
595
596 self.first_curve=True
597 for curve_label in sorted(sheet.curve_dict[p.x_axis].keys()):
598 x_values,y_values,ticks=self._curve_values(i_value,j_value,sheet.curve_dict[p.x_axis][curve_label])
599
600 x_tick_values,ticks = self._reduce_ticks(ticks)
601 labels = [self._format_x_tick_label(x) for x in ticks]
602 pylab.xticks(x_tick_values, labels,fontsize='large')
603 pylab.yticks(fontsize='large')
604 p.plot_type(x_values, y_values, label=curve_label,lw=3.0)
605 self.first_curve=False
606
607 if isint: pylab.ion()
608 pylab.legend(loc=2)
609 self._generate_figure(p)
610
611
612
614 """
615 Same as tuning_curve, but rotates the curve so that minimum y
616 values are at the minimum x value to make the plots easier to
617 interpret. Such rotation is valid only for periodic quantities
618 like orientation or direction, and only if the correct period
619 is set.
620
621 At present, the y_values and labels are rotated by an amount
622 determined by the minmum y_value for the first curve plotted
623 (usually the lowest contrast curve).
624 """
625
626 cyclic_range = param.Number(default=pi,bounds=(0,None),softbounds=(0,10),doc="""
627 Range of the cyclic quantity (e.g. pi for the orientation of
628 a symmetric stimulus, or 2*pi for motion direction or the
629 orientation of a non-symmetric stimulus).""")
630
631 unit = param.String(default="degrees",doc="""
632 String to use in labels to specify the units in which curves are plotted.""")
633
634
635
636
637
638
641
642
644 """
645 Return the x, y, and x ticks values for the specified curve from the curve_dict.
646
647 With the current implementation, there may be cases (i.e.,
648 when the lowest contrast curve gives a lot of zero y_values)
649 in which the maximum is not in the center. This may
650 eventually be changed so that the preferred orientation is in
651 the center.
652 """
653 if self.first_curve==True:
654 x_values= sorted(curve.keys())
655 y_values=[curve[key].view()[0][i_value,j_value] for key in x_values]
656
657 min_arg=argmin(y_values)
658 x_min=x_values[min_arg]
659 y_min=y_values[min_arg]
660 y_values=self._rotate(y_values, n=min_arg)
661 self.ticks=self._rotate(x_values, n=min_arg)
662 self.ticks+=[x_min]
663 x_max=min(x_values)+self.cyclic_range
664 x_values.append(x_max)
665 y_values.append(y_min)
666
667 self.x_values=x_values
668 else:
669 y_values=[curve[key].view()[0][i_value,j_value] for key in self.ticks]
670
671 return self.x_values,y_values,self.ticks
672
673
674
676 """
677 Given a CF sheet receiving a CFProjection, plot
678 the mapping of the dests CF centers on the src sheet.
679 """
680 if isinstance(dest,str):
681 from topo import sim
682 dest = sim[dest]
683 plot_coord_mapping(dest.projections()[proj].coord_mapper,
684 dest,style=style)
685
686
687
689 """
690 Plot a coordinate mapping for a sheet.
691
692 Given a CoordinateMapperFn (as for a CFProjection) and a sheet
693 of the projection, plot a grid showing where the sheet's units
694 are mapped.
695 """
696
697 from pylab import plot,hold,ishold
698
699 xs = sheet.sheet_rows()
700 ys = sheet.sheet_cols()
701
702 hold_on = ishold()
703 if not hold_on:
704 plot()
705 hold(True)
706
707 for y in ys:
708 pts = [mapper(x,y) for x in xs]
709 plot([u for u,v in pts],
710 [v for u,v in pts],
711 style)
712
713 for x in xs:
714 pts = [mapper(x,y) for y in ys]
715 plot([u for u,v in pts],
716 [v for u,v in pts],
717 style)
718
719 hold(hold_on)
720
721
722
724 """
725 Plots parameter values associated with an AttributeTrackingTF.
726 Example call:
727 VT=AttributeTrackingTF(function=HE, debug_params=['a', 'b',], units=[(0,0),(1,1)], step=1)
728 plot_tracked_attributes(VT,0,10000,attrib_names=['a'],units=[(0,0)], filename='V1')
729 """
730
731
732 raw = param.Boolean(default=False)
733
734 attrib_names = param.List(default=[])
735
736 ylabel = param.String(default="")
737
738
739 units = param.List(default=[])
740
741 ybounds = param.Parameter(default=(None,None))
742
743
744
745 - def __call__(self,output_fn,init_time=0,final_time=None,**params):
746 p=ParamOverrides(self,params)
747
748 if final_time is None:
749 final_time=topo.sim.time()
750
751 attrs = p.attrib_names if len(p.attrib_names)>0 else output_fn.attrib_names
752 for a in attrs:
753 pylab.figure(figsize=(6,4))
754 isint=pylab.isinteractive()
755 pylab.ioff()
756 pylab.grid(True)
757 ylabel=p.ylabel
758 pylab.ylabel(a+" "+ylabel)
759 pylab.xlabel('Iteration Number')
760
761 coords = p.units if len(p.units)>0 else output_fn.units
762 for coord in coords:
763 y_data=[y for (x,y) in output_fn.values[a][coord]]
764 x_data=[x for (x,y) in output_fn.values[a][coord]]
765 if p.raw==True:
766 plot_data=zip(x_data,y_data)
767 pylab.save(normalize_path(p.filename+a+'(%.2f, %.2f)' %(coord[0], coord[1])),plot_data,fmt='%.6f', delimiter=',')
768
769
770 pylab.plot(x_data,y_data, label='Unit (%.2f, %.2f)' %(coord[0], coord[1]))
771 (ymin,ymax)=p.ybounds
772 pylab.axis(xmin=init_time,xmax=final_time,ymin=ymin,ymax=ymax)
773
774 if isint: pylab.ion()
775 pylab.legend(loc=0)
776 p.title=topo.sim.