From: Ted D. <ted...@jp...> - 2005-03-11 21:21:42
|
We have some data that we'd like to plot and I'd like to know if matplotlib (MPL) supports this directly (I don't think it does) or where we should start looking to implement this capability. We want to plot time intervals in a similar way to a horizontal bar graph. In this case, the data is when a spacecraft is in view of a ground station. So for a list of ground stations (the y axis), we have lists of start and stop times that represent the view periods. So we need to: - show the list of ground stations (i.e. arbitrary labels) along the Y axis like you would on a bar chart. - draw sets of rectangles at the right y location with the length determined by the x data (in user controllable line styles, colors, and fills). - optionally label the start and stop points of each interval with the X value for that point (in this case a date/time). Ideally, the user would have the option of specifying a location relative to the bar for each end point like this (none, high, mid, low): High High |-----------| Mid | BAR | Mid |-----------| Low Low I don't think I can currently do this in MPL so I'd like to here ideas from John and anyone else on which classes I should start looking at any suggestions on how this should work. Thanks, Ted Ted Drain Jet Propulsion Laboratory ted...@jp... |
From: John H. <jdh...@ac...> - 2005-03-11 22:44:37
|
>>>>> "Ted" == Ted Drain <ted...@jp...> writes: Ted> We have some data that we'd like to plot and I'd like to know Ted> if matplotlib (MPL) supports this directly (I don't think it Ted> does) or where we should start looking to implement this Ted> capability. All the core functionality is there already -- you just have to plug the pieces together. Ted> We want to plot time intervals in a similar way to a Ted> horizontal bar graph. In this case, the data is when a Ted> spacecraft is in view of a ground station. So for a list of Ted> ground stations (the y axis), we have lists of start and stop Ted> times that represent the view periods. So we need to: Ted> - show the list of ground stations (i.e. arbitrary labels) Ted> along the Y axis like you would on a bar chart. Ted> - draw sets of rectangles at the right y location with the Ted> length determined by the x data (in user controllable line Ted> styles, colors, and fills). This is mostly available in barh, which by default has the left side of the bar at 0 but optionally allows you to specify the left as an array. So you can use barh to plot intervals with the left side specfied by the 'left' arg and the bar width specified by the 'x' arg. The return value is a list of Rectangles, which you can customize (set the facecolor, edgecolor, edgewidth, transparency, etc). See track1.py, attached below for an example. Note that you can use any mpl function with dates, as long as you set the tick formatters and locators correctly; this is also illustrated in the example code. Ted> - optionally label the start and stop points of each interval Ted> with the X value for that point (in this case a date/time). Ted> Ideally, the user would have the option of specifying a Ted> location relative to the bar for each end point like this Ted> (none, high, mid, low): Ted> High High |-----------| Mid | BAR | Mid |-----------| Low Low This requires you to compute the bar locations for the x, y, width, height coords of the rectangles. You can then add text with text(x,y,s) if you have computed y to be the bottom, center or top of the bar. track1.py also illustrates this; but see track2.py for examples of setting the horizontal and vertical alignment of the text, which should vary with High, Low or Mid for the best looking graphs. Ted> I don't think I can currently do this in MPL so I'd like to Ted> here ideas from John and anyone else on which classes I Ted> should start looking at any suggestions on how this should Ted> work. For real work, it helps to create custom classes to manage some of these layout details. I did this in track2.py, which creates a custom class derived from Rectangle that contains two Text instances labelstart and labelstop. This design makes more sense, since the View object knows it's left, bottom, width, height etc so it makes it easier to do layout. The example shows how to use mpl connections to toggle labels on and off. In any case, the two mpl classes you want to study are matplotlib.patches.Rectangle and matplotlib.text.Text, and you may want to take a close look at matplotlib.axes.Axes.barh method. Sometimes it's easier to write an example than it is to explain how to use everything together. If you can think of a nice way to wrap some of the functionality into reusable pieces, that would be great. JDH #################### # Begin track1.py # #################### from matplotlib.dates import date2num from matplotlib.dates import HourLocator, DateFormatter from matplotlib.mlab import rand from matplotlib.numerix import arange from datetime import datetime from pylab import figure, show N = 7 labels = ['S%d'%i for i in range(N)] colors = ('red', 'green', 'purple', 'brown', 'yellow', 'black', 'blue') t0 = date2num(datetime.now()) start = t0 + rand(N) # add random days in seconds locator = HourLocator(arange(0,24,2)) # ticks every 2 hours formatter = DateFormatter('%H') # hour ylocs = arange(N) # ylocs of the bar, arbitrary duration = rand(N)*0.2 # random durations in fraction of day fig = figure() ax = fig.add_subplot(111) ax.xaxis.set_major_formatter(formatter) ax.xaxis.set_major_locator(locator) height = 0.5 # the height of the bar, arbitrary bars = ax.barh(duration, ylocs, height=height, left=start) ax.set_yticks(ylocs) ax.set_yticklabels(labels) for bar, color in zip(bars, colors): bar.set_facecolor(color) # define some location arrays for labeling left = start right = left + duration top = ylocs + height/2. bottom = ylocs - height/2. center = ylocs # label the 4th bar on top right ind = 3 note = 'hi mom' x = right[ind] y = top[ind] ax.text(x,y,note) #ax.set_xlim((min(left), max(right)+2)) ax.grid(True) ax.set_title('Time windows when craft visible by station') ax.set_xlabel('Time (h)') show() #################### # End track1.py # #################### #################### # Begin track2.py # #################### from matplotlib.artist import Artist from matplotlib.dates import date2num from matplotlib.dates import HourLocator, DateFormatter from matplotlib.patches import Rectangle from matplotlib.mlab import rand from matplotlib.numerix import arange from datetime import datetime import pylab class View(Rectangle): """ A view of when a craft is visible defined by a start and stop time (days as float) with labeling capability. A rectangle will be drawn """ def __init__(self, ax, ind, station, start, stop, timefmt='%H:%M', timeloc='top', height=0.5, **kwargs): """ ax is an axes instance -- the class will add required Artists to to axes ind is the station number for yaxis positions -- the rectangles will be drawn with vertical centers at ind with a height height. station is a string label start and stop will be the left and right side of the rectangles """ # for rects, x,y is lower left but we want ind to be the # center Rectangle.__init__(self, (start, ind-height/2), stop-start, height, **kwargs) ax.add_patch(self) self.ind = ind self.station = station self.timefmt = timefmt self.formatter = DateFormatter(timefmt) self.labelstart = ax.text(start, 0, self.formatter(start), horizontalalignment='right') self.labelstop = ax.text(stop, 0, self.formatter(stop), horizontalalignment='left') self.labelstart.set_visible(False) self.labelstop.set_visible(False) if timeloc is not None: if timeloc == 'top': y = ind + height/2. valign = 'bottom' elif timeloc == 'bottom': y = ind - height/2. valign = 'top' elif timeloc == 'center': y = ind valign = 'center' self.labelstart.set_visible(True) self.labelstop.set_visible(True) self.labelstart.set_y(y) self.labelstop.set_y(y) self.labelstart.set_verticalalignment(valign) self.labelstop.set_verticalalignment(valign) N = 8 t0 = date2num(datetime.now()) start = t0 + rand(N) # add random days in seconds locator = HourLocator(arange(0,24,2)) # ticks every 2 hours formatter = DateFormatter('%H') # hour duration = rand(N)*0.2 # random durations in fraction of day stop = start + duration fig = pylab.figure() ax = fig.add_subplot(111) ax.xaxis.set_major_formatter(formatter) ax.xaxis.set_major_locator(locator) views = [] for ind, x1, x2 in zip(range(1,N+1), start, stop): view = View(ax, ind, 'S%d'%ind, x1, x2, timeloc='center') # Now call any Rectangle function on view instance to customize rect, # and any Text prop on view.labelstart and view.labelstop views.append(view) yticks = [view.ind for view in views] ylabels = [view.station for view in views] ax.set_yticks(yticks) ax.set_yticklabels(ylabels) def toggle_labels(event): if event.key != 't': return toggle_labels.on = not toggle_labels.on for view in views: view.labelstart.set_visible(toggle_labels.on) view.labelstop.set_visible(toggle_labels.on) pylab.draw() toggle_labels.on = True # use canvas.mpl_connect in API pylab.connect('key_press_event', toggle_labels) ax.autoscale_view() # this is normally called by a plot command ax.set_xlim((min(start)-.1, max(stop)+.1)) ax.set_ylim((0,N+1)) ax.set_title("Press 't' to toggle labels") ax.set_xlabel('Time (hours)') ax.grid(True) pylab.show() #################### # End track2.py # #################### |
From: Ted D. <ted...@jp...> - 2005-03-11 23:33:04
|
Jeez John if you're going to do this much work for a simple information request I may start sending you "questions" about my normal job. (thank you very much!) Thanks for the code - it's going to save us a ton of time. We're going to take the track2 demo and extend/refine/document it to be a function similar to barh. We'll have to think about the x data input for this function a little bit since it really needs to be something like x= [ [ (start1,stop1), (start2,stop2), ... ], [ (start1,stop1), (start2,stop2), ... ], ... ] which might be a little complicated for someone to set up correctly. Thanks, Ted At 02:33 PM 3/11/2005, John Hunter wrote: > >>>>> "Ted" == Ted Drain <ted...@jp...> writes: > > Ted> We have some data that we'd like to plot and I'd like to know > Ted> if matplotlib (MPL) supports this directly (I don't think it > Ted> does) or where we should start looking to implement this > Ted> capability. > >All the core functionality is there already -- you just have to plug >the pieces together. > > Ted> We want to plot time intervals in a similar way to a > Ted> horizontal bar graph. In this case, the data is when a > Ted> spacecraft is in view of a ground station. So for a list of > Ted> ground stations (the y axis), we have lists of start and stop > Ted> times that represent the view periods. So we need to: > > Ted> - show the list of ground stations (i.e. arbitrary labels) > Ted> along the Y axis like you would on a bar chart. > > Ted> - draw sets of rectangles at the right y location with the > Ted> length determined by the x data (in user controllable line > Ted> styles, colors, and fills). > >This is mostly available in barh, which by default has the left side >of the bar at 0 but optionally allows you to specify the left as an >array. So you can use barh to plot intervals with the left side >specfied by the 'left' arg and the bar width specified by the 'x' arg. >The return value is a list of Rectangles, which you can customize (set >the facecolor, edgecolor, edgewidth, transparency, etc). See >track1.py, attached below for an example. > >Note that you can use any mpl function with dates, as long as you set >the tick formatters and locators correctly; this is also illustrated >in the example code. > > Ted> - optionally label the start and stop points of each interval > Ted> with the X value for that point (in this case a date/time). > Ted> Ideally, the user would have the option of specifying a > Ted> location relative to the bar for each end point like this > Ted> (none, high, mid, low): > > Ted> High High |-----------| Mid | BAR | Mid |-----------| Low Low > >This requires you to compute the bar locations for the x, y, width, >height coords of the rectangles. You can then add text with >text(x,y,s) if you have computed y to be the bottom, center or top of >the bar. track1.py also illustrates this; but see track2.py for >examples of setting the horizontal and vertical alignment of the text, >which should vary with High, Low or Mid for the best looking graphs. > > Ted> I don't think I can currently do this in MPL so I'd like to > Ted> here ideas from John and anyone else on which classes I > Ted> should start looking at any suggestions on how this should > Ted> work. > >For real work, it helps to create custom classes to manage some of >these layout details. I did this in track2.py, which creates a custom >class derived from Rectangle that contains two Text instances >labelstart and labelstop. This design makes more sense, since the >View object knows it's left, bottom, width, height etc so it makes it >easier to do layout. The example shows how to use mpl connections to >toggle labels on and off. > >In any case, the two mpl classes you want to study are >matplotlib.patches.Rectangle and matplotlib.text.Text, and you may >want to take a close look at matplotlib.axes.Axes.barh method. > >Sometimes it's easier to write an example than it is to explain how to >use everything together. If you can think of a nice way to wrap some >of the functionality into reusable pieces, that would be great. > >JDH > > >#################### ># Begin track1.py # >#################### > >from matplotlib.dates import date2num >from matplotlib.dates import HourLocator, DateFormatter >from matplotlib.mlab import rand >from matplotlib.numerix import arange >from datetime import datetime >from pylab import figure, show > >N = 7 >labels = ['S%d'%i for i in range(N)] >colors = ('red', 'green', 'purple', 'brown', 'yellow', 'black', 'blue') > >t0 = date2num(datetime.now()) >start = t0 + rand(N) # add random days in seconds >locator = HourLocator(arange(0,24,2)) # ticks every 2 hours >formatter = DateFormatter('%H') # hour > >ylocs = arange(N) # ylocs of the bar, arbitrary > >duration = rand(N)*0.2 # random durations in fraction of day > >fig = figure() >ax = fig.add_subplot(111) >ax.xaxis.set_major_formatter(formatter) >ax.xaxis.set_major_locator(locator) > >height = 0.5 # the height of the bar, arbitrary >bars = ax.barh(duration, ylocs, height=height, left=start) >ax.set_yticks(ylocs) >ax.set_yticklabels(labels) > >for bar, color in zip(bars, colors): > bar.set_facecolor(color) > ># define some location arrays for labeling >left = start >right = left + duration >top = ylocs + height/2. >bottom = ylocs - height/2. >center = ylocs > > > ># label the 4th bar on top right >ind = 3 >note = 'hi mom' >x = right[ind] >y = top[ind] >ax.text(x,y,note) >#ax.set_xlim((min(left), max(right)+2)) >ax.grid(True) >ax.set_title('Time windows when craft visible by station') >ax.set_xlabel('Time (h)') > >show() > >#################### ># End track1.py # >#################### > > >#################### ># Begin track2.py # >#################### > >from matplotlib.artist import Artist >from matplotlib.dates import date2num >from matplotlib.dates import HourLocator, DateFormatter >from matplotlib.patches import Rectangle >from matplotlib.mlab import rand >from matplotlib.numerix import arange >from datetime import datetime > >import pylab > >class View(Rectangle): > """ > A view of when a craft is visible defined by a start and stop time > (days as float) with labeling capability. > > A rectangle will be drawn > """ > def __init__(self, ax, ind, station, start, stop, timefmt='%H:%M', > timeloc='top', height=0.5, **kwargs): > """ > ax is an axes instance -- the class will add required Artists > to to axes > > ind is the station number for yaxis positions -- the rectangles > will be > drawn with vertical centers at ind with a height height. > > station is a string label > > start and stop will be the left and right side of the > rectangles > """ > # for rects, x,y is lower left but we want ind to be the > # center > Rectangle.__init__(self, (start, ind-height/2), stop-start, > height, **kwargs) > ax.add_patch(self) > self.ind = ind > self.station = station > > self.timefmt = timefmt > self.formatter = DateFormatter(timefmt) > > self.labelstart = ax.text(start, 0, self.formatter(start), > horizontalalignment='right') > self.labelstop = ax.text(stop, 0, self.formatter(stop), > horizontalalignment='left') > > self.labelstart.set_visible(False) > self.labelstop.set_visible(False) > if timeloc is not None: > if timeloc == 'top': > y = ind + height/2. > valign = 'bottom' > elif timeloc == 'bottom': > y = ind - height/2. > valign = 'top' > elif timeloc == 'center': > y = ind > valign = 'center' > > self.labelstart.set_visible(True) > self.labelstop.set_visible(True) > > self.labelstart.set_y(y) > self.labelstop.set_y(y) > > self.labelstart.set_verticalalignment(valign) > self.labelstop.set_verticalalignment(valign) > > > > >N = 8 >t0 = date2num(datetime.now()) >start = t0 + rand(N) # add random days in seconds >locator = HourLocator(arange(0,24,2)) # ticks every 2 hours >formatter = DateFormatter('%H') # hour > >duration = rand(N)*0.2 # random durations in fraction of day >stop = start + duration >fig = pylab.figure() >ax = fig.add_subplot(111) >ax.xaxis.set_major_formatter(formatter) >ax.xaxis.set_major_locator(locator) > >views = [] >for ind, x1, x2 in zip(range(1,N+1), start, stop): > view = View(ax, ind, 'S%d'%ind, x1, x2, timeloc='center') > # Now call any Rectangle function on view instance to customize rect, > # and any Text prop on view.labelstart and view.labelstop > views.append(view) > > >yticks = [view.ind for view in views] >ylabels = [view.station for view in views] > >ax.set_yticks(yticks) >ax.set_yticklabels(ylabels) > >def toggle_labels(event): > if event.key != 't': return > toggle_labels.on = not toggle_labels.on > for view in views: > view.labelstart.set_visible(toggle_labels.on) > view.labelstop.set_visible(toggle_labels.on) > pylab.draw() >toggle_labels.on = True > ># use canvas.mpl_connect in API >pylab.connect('key_press_event', toggle_labels) > >ax.autoscale_view() # this is normally called by a plot command > >ax.set_xlim((min(start)-.1, max(stop)+.1)) >ax.set_ylim((0,N+1)) >ax.set_title("Press 't' to toggle labels") >ax.set_xlabel('Time (hours)') >ax.grid(True) >pylab.show() > > >#################### ># End track2.py # >#################### > > > > >------------------------------------------------------- >SF email is sponsored by - The IT Product Guide >Read honest & candid reviews on hundreds of IT Products from real users. >Discover which products truly live up to the hype. Start reading now. >http://ads.osdn.com/?ad_id=6595&alloc_id=14396&op=click >_______________________________________________ >Matplotlib-users mailing list >Mat...@li... >https://lists.sourceforge.net/lists/listinfo/matplotlib-users Ted Drain Jet Propulsion Laboratory ted...@jp... |