[Scipy-svn] r2769 - trunk/Lib/sandbox/timeseries/plotlib

scipy-svn at scipy.org scipy-svn at scipy.org
Tue Feb 27 09:05:05 EST 2007


Author: mattknox_ca
Date: 2007-02-27 08:05:02 -0600 (Tue, 27 Feb 2007)
New Revision: 2769

Modified:
   trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py
Log:
major overhaul

Modified: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py
===================================================================
--- trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py	2007-02-27 14:02:11 UTC (rev 2768)
+++ trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py	2007-02-27 14:05:02 UTC (rev 2769)
@@ -1,12 +1,12 @@
 """
 Classes to plot TimeSeries w/ matplotlib.
 
-:author: Pierre GF Gerard-Marchant
-:contact: pierregm_at_uga_edu
+:author: Pierre GF Gerard-Marchant & Matt Knox
+:contact: pierregm_at_uga_dot_edu - mattknow_ca_at_hotmail_dot_com
 :date: $Date$
 :version: $Id$
 """
-__author__ = "Pierre GF Gerard-Marchant ($Author$)"
+__author__ = "Pierre GF Gerard-Marchant & Matt Knox ($Author$)"
 __version__ = '1.0'
 __revision__ = "$Revision$"
 __date__     = '$Date$'
@@ -26,17 +26,14 @@
 from matplotlib.mlab import meshgrid
 from matplotlib.ticker import Formatter, ScalarFormatter, FuncFormatter, \
                               Locator, FixedLocator
-
 #from matplotlib.transforms import nonsingular
 
 import numpy as N
 import maskedarray as MA
 
 import timeseries
+import timeseries as TS
 from timeseries import date_array, Date, DateArray, TimeSeries
-#from tdates import date_array, Date
-#import tseries
-#from tseries import TimeSeries
 
 import warnings
 
@@ -133,207 +130,366 @@
 
 def _get_default_annual_spacing(nyears):
     """Returns a default spacing between consecutive ticks for annual data."""
-    if nyears < 20: 
+    if nyears < 11: 
+        (min_spacing, maj_spacing) = (1, 1)
+    elif nyears < 20: 
         (min_spacing, maj_spacing) = (1, 2)
     elif nyears < 50: 
         (min_spacing, maj_spacing) = (1, 5)
     elif nyears < 100: 
         (min_spacing, maj_spacing) = (5, 10)
     elif nyears < 200: 
-        (min_spacing, maj_spacing) = (5, 20)
-    elif nyears < 400: 
         (min_spacing, maj_spacing) = (5, 25)
-    elif nyears < 1000: 
+    elif nyears < 600: 
         (min_spacing, maj_spacing) = (10, 50)
     else:
-        (min_spacing, maj_spacing) = (20, 100)
+        factor = nyears // 1000 + 1
+        (min_spacing, maj_spacing) = (factor*20, factor*100)
     return (min_spacing, maj_spacing)
 
-def _get_default_quarterly_spacing(nquarters):
-    """Returns a default spacing between consecutive ticks for quarterly data."""
-    if nquarters <= 3*4:
-        (min_spacing, maj_spacing) = (1,4)
-    elif nquarters <= 11*4:
-        (min_spacing, maj_spacing) = (1,4)
+
+def period_break(dates, period):
+    """Returns the indices where the given period changes.
+
+:Parameters:
+    dates : DateArray
+        Array of dates to monitor.
+    period : string
+        Name of the period to monitor.
+    """
+    current = getattr(dates, period)
+    previous = getattr(dates-1, period)
+    return (current - previous).nonzero()[0]
+
+def has_level_label(label_flags):
+    """returns true if the label_flags indicate there is at least
+one label for this level"""
+    if label_flags.size == 0 or \
+       (label_flags.size == 1 and label_flags[0] == 0):
+        return False
     else:
-        (min_anndef, maj_anndef) = _get_default_annual_spacing(nquarters//4)
-        min_spacing = min_anndef * 4
-        maj_spacing = maj_anndef * 4
-    return (min_spacing, maj_spacing)
+        return True
 
-def _get_default_monthly_spacing(nmonths):
-    """Returns a default spacing between consecutive ticks for monthly data."""
-    if nmonths <= 10:
-        (min_spacing, maj_spacing) = (1,3)
-    elif nmonths <= 2*12:
-        (min_spacing, maj_spacing) = (1,6)
-    elif nmonths <= 3*12:
-        (min_spacing, maj_spacing) = (1,12)
-    elif nmonths <= 11*12:
-        (min_spacing, maj_spacing) = (3,12)  
+def _daily_finder(vmin, vmax, freq, aslocator):
+
+    if freq == TS.FR_BUS: 
+        periodsperyear = 261
+    elif freq == TS.FR_DAY: 
+        periodsperyear = 365
+    else: 
+        raise ValueError("unexpected frequency")
+
+    (vmin, vmax) = (int(vmin), int(vmax))
+    span = vmax - vmin + 1
+    dates = date_array(start_date=Date(freq,vmin), 
+                       end_date=Date(freq, vmax))
+    default = N.arange(vmin, vmax+1) 
+    # Initialize the output
+    if not aslocator:
+        format = N.empty(default.shape, dtype="|S10")
+        format.flat = ''
+
+    def first_label(label_flags):
+        if label_flags[0] == 0: return label_flags[1]
+        else: return label_flags[0]
+
+    # Case 1. Less than a month
+    if span <= (periodsperyear//12 - 2):
+        month_start = period_break(dates,'month')
+        if aslocator:
+            major = default[month_start]
+            minor = default
+        else:
+            year_start = period_break(dates,'year')
+            format[:] = '%d'
+            format[month_start] = '%d\n%b'
+            format[year_start] = '%d\n%b\n%Y'
+            if not has_level_label(year_start):
+                if not has_level_label(month_start):
+                    if dates.size > 1: 
+                        idx = 1
+                    else: 
+                        idx = 0
+                    format[idx] = '%d\n%b\n%Y'
+                else:
+                    format[first_label(month_start)] = '%d\n%b\n%Y'
+    # Case 2. Less than three months        
+    elif span <= periodsperyear//4:
+        month_start = period_break(dates,'month')
+        if aslocator:
+            major = default[month_start]
+            minor = default
+        else:
+            week_start = period_break(dates,'week')
+            year_start = period_break(dates,'year')
+
+            format[week_start] = '%d'
+            format[month_start] = '\n\n%b'
+            format[year_start] = '\n\n%b\n%Y'
+            if not has_level_label(year_start):
+                if not has_level_label(month_start):
+                    format[first_label(week_start)] = '\n\n%b\n%Y'
+                else:
+                    format[first_label(month_start)] = '\n\n%b\n%Y'
+    # Case 3. Less than 14 months ...............
+    elif span <= 1.15 * periodsperyear:
+        
+        if aslocator:
+            d_minus_1 = dates-1
+
+            month_diff = N.abs(dates.month - d_minus_1.month)
+            week_diff = N.abs(dates.week - d_minus_1.week)
+            minor_idx = (month_diff + week_diff).nonzero()[0]
+
+            major = default[month_diff != 0]
+            minor = default[minor_idx]
+        else:
+            year_start = period_break(dates,'year')
+            month_start = period_break(dates,'month')
+
+            format[month_start] = '%b'
+            format[year_start] = '%b\n%Y'
+            if not has_level_label(year_start):
+                format[first_label(month_start)] = '%b\n%Y'
+    # Case 4. Less than 2.5 years ...............
+    elif span <= 2.5 * periodsperyear:
+        year_start = period_break(dates,'year')
+        if aslocator:
+            month_start = period_break(dates, 'quarter')
+            major = default[year_start]
+            minor = default[month_start]
+        else:
+            quarter_start = period_break(dates, 'quarter')
+            format[quarter_start] = '%b'
+            format[year_start] = '%b\n%Y'
+    # Case 4. Less than 4 years .................
+    elif span <= 4 * periodsperyear:
+        year_start = period_break(dates,'year')
+        month_start = period_break(dates, 'month')
+        if aslocator:
+            major = default[year_start]
+            minor = default[month_start]
+        else:
+            month_break = dates[month_start].month
+            jan_or_jul = month_start[(month_break == 1) | (month_break == 7)]
+            format[jan_or_jul] = '%b'
+            format[year_start] = '%b\n%Y'
+    # Case 5. Less than 11 years ................
+    elif span <= 11 * periodsperyear:
+        year_start = period_break(dates,'year')
+        if aslocator:
+            quarter_start = period_break(dates, 'quarter')
+            major = default[year_start]
+            minor = default[quarter_start]
+        else:
+            format[year_start] = '%Y'
+    # Case 6. More than 12 years ................
     else:
-        (min_anndef, maj_anndef) = _get_default_annual_spacing(nmonths//12)
-        min_spacing = min_anndef * 12
-        maj_spacing = maj_anndef * 12
-    return (min_spacing, maj_spacing)
+        year_start = period_break(dates,'year')
+        year_break = dates[year_start].years
+        nyears = span/periodsperyear
+        (min_anndef, maj_anndef) = _get_default_annual_spacing(nyears)
+        major_idx = year_start[(year_break % maj_anndef == 0)]
+        if aslocator:
+            major = default[major_idx]
+            minor_idx = year_start[(year_break % min_anndef == 0)]
+            minor = default[minor_idx]
+        else:
+            format[major_idx] = '%Y'
+    #............................................
+    if aslocator:
+        return minor, major
+    else:
+        formatted = (format != '')
+        return dict([(d,f) for (d,f) in zip(default[formatted],format[formatted])])
+#...............................................................................
+def _monthly_finder(vmin, vmax, freq, aslocator):
+    if freq != TS.FR_MTH: 
+        raise ValueError("unexpected frequency")
+    periodsperyear = 12
 
+    (vmin, vmax) = (int(vmin), int(vmax))
+    span = vmax - vmin + 1
+    #............................................
+    dates = N.arange(vmin, vmax+1) 
+    format = N.empty(span, dtype="|S8")
+    format.flat = ''
+    year_start = (dates % 12 == 1).nonzero()[0]
+    #............................................
+    if span <= 1.15 * periodsperyear:
+        if aslocator:
+            major = dates[year_start]
+            minor = dates
+        else:
+            
+            format[:] = '%b'
+            format[year_start] = '%b\n%Y'
+
+            if not has_level_label(year_start):
+                if dates.size > 1: 
+                    idx = 1
+                else: 
+                    idx = 0
+                format[idx] = '%b\n%Y'
+    #........................
+    elif span <= 2.5 * periodsperyear:
+        if aslocator:
+            major = dates[year_start]
+            minor = dates
+        else:
+            quarter_start = (dates % 3 == 1).nonzero()
+            format[quarter_start] = '%b'
+            format[year_start] = '%b\n%Y'
+    #.......................
+    elif span <= 4 * periodsperyear:
+        if aslocator:
+            major = dates[year_start]
+            minor = dates
+        else:
+            jan_or_jul = (dates % 12 == 1) | (dates % 12 == 7)
+            format[jan_or_jul] = '%b'
+            format[year_start] = '%b\n%Y'
+    #........................
+    elif span <= 11 * periodsperyear:
+        if aslocator:
+            quarter_start = (dates % 3 == 1).nonzero()
+            major = dates[year_start]
+            minor = dates[quarter_start]
+        else:
+            format[year_start] = '%Y'
+   #......................... 
+    else:
+        nyears = span/periodsperyear
+        (min_anndef, maj_anndef) = _get_default_annual_spacing(nyears)
+        years = dates[year_start]//12 + 1
+        major_idx = year_start[(years % maj_anndef == 0)]
+        if aslocator:
+            major = dates[major_idx]
+            minor = dates[year_start[(years % min_anndef == 0)]]
+        else:
+            format[major_idx] = '%Y'
+    #........................
+    if aslocator:
+        return minor, major
+    else:
+        formatted = (format != '')
+        return dict([(d,f) for (d,f) in zip(dates[formatted],format[formatted])])
 #...............................................................................
+def _quarterly_finder(vmin, vmax, freq, aslocator):
+    if freq != TS.FR_QTR: 
+        raise ValueError("unexpected frequency")
+    periodsperyear = 4  
+    (vmin, vmax) = (int(vmin), int(vmax))
+    span = vmax - vmin + 1
+    #............................................
+    dates = N.arange(vmin, vmax+1) 
+    format = N.empty(span, dtype="|S8")
+    format.flat = ''
+    year_start = (dates % 4 == 1).nonzero()[0]
+    #............................................
+    if span <= 3.5 * periodsperyear:
+        if aslocator:
+            major = dates[year_start]
+            minor = dates
+        else:
+            format[:] = 'Q%q'
+            format[year_start] = 'Q%q\n%Y'
+            if not has_level_label(year_start):
+                if dates.size > 1: 
+                    idx = 1
+                else: 
+                    idx = 0
+                format[idx] = 'Q%q\n%Y'
+    #............................................
+    elif span <= 11 * periodsperyear:
+        if aslocator:
+            major = dates[year_start]
+            minor = dates
+        else:
+            format[year_start] = '%Y'
+    #............................................
+    else:
+        years = dates[year_start]//4 + 1
+        nyears = span/periodsperyear
+        (min_anndef, maj_anndef) = _get_default_annual_spacing(nyears)
+        major_idx = year_start[(years % maj_anndef == 0)]
+        if aslocator:
+            major = dates[major_idx]
+            minor = dates[year_start[(years % min_anndef == 0)]]
+        else:
+            format[major_idx] = '%Y'
+    #............................................
+    if aslocator:
+        return minor, major
+    else:
+        formatted = (format != '')
+        return dict([(d,f) for (d,f) in zip(dates[formatted],format[formatted])])
+#...............................................................................
+def _annual_finder(vmin, vmax, freq, aslocator):
+    if freq != TS.FR_ANN: 
+        raise ValueError("unexpected frequency")   
+    (vmin, vmax) = (int(vmin), int(vmax+1))
+    span = vmax - vmin + 1
+    #............................................
+    dates = N.arange(vmin, vmax+1) 
+    format = N.empty(span, dtype="|S8")
+    format.flat = ''
+    #............................................
+    (min_anndef, maj_anndef) = _get_default_annual_spacing(span)
+    major_idx = dates % maj_anndef == 0
+    if aslocator:
+        major = dates[major_idx]
+        minor = dates[(dates % min_anndef == 0)]
+    else:
+        format[major_idx] = '%Y'
+    #............................................
+    if aslocator:
+        return minor, major
+    else:
+        formatted = (format != '')
+        return dict([(d,f) for (d,f) in zip(dates[formatted],format[formatted])])
+    
+#...............................................................................
 class TimeSeries_DateLocator(Locator):
     "Locates the ticks along an axis controlled by a DateArray."
 
     def __init__(self, freq, minor_locator=False, dynamic_mode=True,
                  base=1, quarter=1, month=1, day=1):
-        self.freqstr = freq
+        self.freq = freq
         self.base = base
         (self.quarter, self.month, self.day) = (quarter, month, day)
         self.isminor = minor_locator
         self.isdynamic = dynamic_mode
         self.offset = 0
+        #.....
+        if freq == TS.FR_ANN:
+            self.finder = _annual_finder
+        elif freq == TS.FR_QTR:
+            self.finder = _quarterly_finder
+        elif freq == TS.FR_MTH:
+            self.finder = _monthly_finder
+        elif freq in (TS.FR_BUS, TS.FR_DAY):
+            self.finder = _daily_finder
             
-    def _initialize_dates(self, start_val, end_val):
-        "Returns a DateArray for the current frequency."
-        freq = self.freqstr
-        dates = date_array(start_date=Date(freq, value=int(start_val)),
-                           end_date=Date(freq, value=int(end_val)), 
-                           freq=freq)
-        return dates
-
-    def _get_default_spacing(self, span):
-        "Returns the default ticks spacing."
-        raise NotImplementedError('Derived must override')
+    def asminor(self):
+        "Returns the locator set to minor mode."
+        self.isminor = True
+        return self
     
-    def __call__(self):
-        'Return the locations of the ticks.'
-        self.verify_intervals()
-        vmin, vmax = self.viewInterval.get_bounds()
-        if vmax < vmin:
-            vmin, vmax = vmax, vmin
-        if self.isdynamic:
-            base = self._get_default_spacing(vmax-vmin+1)
-        else:
-            base = self.base
-        d = vmin // base
-        vmin = (d+1) * base + self.offset
-        locs = range(vmin, vmax+1, base)
-        return locs
+    def asmajor(self):
+        "Returns the locator set to major mode."
+        self.isminor = False
+        return self
     
-    def autoscale(self):
-        """Sets the view limits to the nearest multiples of base that contain 
-    the data.
-        """
-        self.verify_intervals()
-        dmin, dmax = self.dataInterval.get_bounds()
-        if self.isdynamic:
-            base = self._get_default_spacing(dmax-dmin+1)
-        else:
-            base = self.base
-        (d,m) = divmod(dmin, base)
-        if m < base/2:
-            vmin = d * base
-        else:
-            vmin = (d+1) * base
-        (d,m) = divmod(dmax, base)
-        vmax = (d+1) * base
-        if vmin == vmax:
-            vmin -= 1
-            vmax += 1
-        return nonsingular(vmin, vmax)        
-    
-#...............................................................................
-class TimeSeries_AnnualLocator(TimeSeries_DateLocator):
-    "Locates the ticks along an axis controlled by an annual DateArray."
-
-    def __init__(self, minor_locator=False, dynamic_mode=True,
-                 base=1, quarter=1, month=1, day=1):
-        TimeSeries_DateLocator.__init__(self,'A', minor_locator, dynamic_mode,
-                                        base, quarter, month, day)
-    
-    def _get_default_spacing(self, span):
-        "Returns the default tick spacing for annual data."
-        (minor, major) = _get_default_annual_spacing(span)
+    def _get_default_locs(self, vmin, vmax):
+        "Returns the default locations of ticks."
+        (minor, major) = self.finder(vmin, vmax, self.freq, True)
         if self.isminor:
             return minor
         return major
-#...............................................................................
-class TimeSeries_QuarterlyLocator(TimeSeries_DateLocator):
-    "Locates the ticks along an axis controlled by a quarterly DateArray."
-
-    def __init__(self, minor_locator=False, dynamic_mode=True,
-                 base=1, quarter=1, month=1, day=1):
-        TimeSeries_DateLocator.__init__(self,'Q', minor_locator, dynamic_mode,
-                                        base, quarter, month, day)
-        self.offset=1
     
-    def _get_default_spacing(self, span):
-        "Returns the default tick spacing for quarterly data."
-        (minor, major) = _get_default_quarterly_spacing(span)
-        if self.isminor:
-            return minor
-        return major       
-#...............................................................................
-class TimeSeries_MonthlyLocator(TimeSeries_DateLocator):
-    "Locates the ticks along an axis controlled by a monthly DateArray."
-
-    def __init__(self, minor_locator=False, dynamic_mode=True,
-                 base=1, quarter=1, month=1, day=1):
-        TimeSeries_DateLocator.__init__(self,'M', minor_locator, dynamic_mode,
-                                        base, quarter, month, day)
-        self.offset = 1
-    
-    def _get_default_spacing(self, span):
-        "Returns the default tick spacing for monthly data."
-        (minor, major) = _get_default_monthly_spacing(span)
-        if self.isminor:
-            return minor
-        return major
-    
-#...............................................................................
-class TimeSeries_DailyLocator(TimeSeries_DateLocator):
-    "Locates the ticks along an axis controlled by a daily DateArray."
-
-    def __init__(self, freq, minor_locator=False, dynamic_mode=True,
-                 base=1, quarter=1, month=1, day=1):
-        TimeSeries_DateLocator.__init__(self, freq, minor_locator, dynamic_mode,
-                                        base, quarter, month, day)
-        if self.freqstr == 'B':
-            self.daysinyear = 261
-        else:
-            self.daysinyear = 365
-        self._cacheddates = None
-        
-    def _get_default_locs(self, vmin, vmax):
-        "Returns the default tick locations for daily data."
-        daysperyear = self.daysinyear
-        span = vmax - vmin + 1
-        dates = self._initialize_dates(vmin, vmax)
-        default = N.arange(vmin, vmax+1) 
-        #
-        if span <= daysperyear//12:
-            minor = default
-            major = default[(dates.day_of_week == 1)]
-        elif span <= daysperyear//3:
-            minor = default[(dates.day_of_week == 1)]
-            major = default[(dates.day == 1)]
-        elif span <= 1.5 * daysperyear:
-            minor = default[(dates.day_of_week == 1)]
-            major = default[(dates.day == 1)]
-        elif span <= 3 * daysperyear:
-            minor = default[(dates.day == 1)]
-            major = default[(dates.day_of_year == 1)]
-        elif span <= 11 * daysperyear:
-            minor = default[(dates.quarter != (dates-1).quarter)]
-            major = default[(dates.day_of_year == 1)]
-        else:
-            (min_anndef, maj_anndef) = _get_default_annual_spacing(span/daysperyear)
-            annual = (dates.day_of_year == 1)
-            minor = default[annual & (dates.years % min_anndef == 0)]
-            major = default[annual & (dates.years % maj_anndef == 0)]
-        if self.isminor:
-            return minor
-        return major
-
     def __call__(self):
-        'Return the locations of the ticks'
+        'Return the locations of the ticks.'
         self.verify_intervals()
         vmin, vmax = self.viewInterval.get_bounds()
         if vmax < vmin:
@@ -342,11 +498,11 @@
             locs = self._get_default_locs(vmin, vmax)
         else:
             base = self.base
-            (d,m) = divmod(vmin, base)
+            (d, m) = divmod(vmin, base)
             vmin = (d+1) * base
             locs = range(vmin, vmax+1, base)
         return locs
-
+    
     def autoscale(self):
         """Sets the view limits to the nearest multiples of base that contain 
     the data.
@@ -354,137 +510,72 @@
         self.verify_intervals()
         dmin, dmax = self.dataInterval.get_bounds()
         locs = self._get_default_locs(dmin, dmax)
-        (vmin, vmax) = locs[[0,-1]]
+        (vmin, vmax) = locs[[0, -1]]
         if vmin == vmax:
             vmin -= 1
             vmax += 1
-        return nonsingular(vmin, vmax)        
+        return nonsingular(vmin, vmax)      
 
-#...............................................................................
-class TimeSeries_YearLocator(TimeSeries_DateLocator):
-    """Locates ticks along a Date axis, for each (multiple of) year.
-    
-:Ivariables:
-    - `base` : Integer
-      Gives the spacing between two consecutive annual ticks.
-    - `quarter` : Integer *[1]*
-      Tells on which quarter the ticks should be.
-    - `month` : Integer *[1]*
-      Tells on which month the ticks should be.
-    - `day` : Integer *[1]*
-      Tells on which day the ticks should be.    
-    """
-    def __init__(self, freq, minor_locator=False,
-                 base=1, quarter=1, month=1, day=1):
-        TimeSeries_DateLocator.__init__(self, freq, minor_locator, False,
-                                        base, quarter, month, day)
-    
-    def __call__(self):
-        self.verify_intervals()
-        vmin, vmax = self.viewInterval.get_bounds()
-        freq = self.freqstr
-        if freq == 'A':
-            return range(vmin, vmax+1, self.base)
-        else:
-            dates = self._initialize_dates()
-            if freq == 'Q':
-                locs = (dates.quarters == self.quarter)
-            elif freq == 'M':
-                locs = (dates.months == self.month)
-            elif freq in 'BDU':
-                locs = (dates.months == self.month) & (dates.day == self.day)
-            if self.base > 1:
-                locs &= (locs.cumsum() % self.base == 1)
-            return dates.tovalue()[locs]
-#...............................................
-class TimeSeries_QuarterLocator(TimeSeries_DateLocator):
-    """Locates ticks along a Date axis, for each (multiple of) quarter.
-    
-:Ivariables:
-    - `base` : Integer
-      Gives the spacing between two consecutive quarter ticks.
-    - `month` : Integer *[1]*
-      Tells on which month the ticks should be.
-    - `day` : Integer *[1]*
-      Tells on which day the ticks should be.    
-    """
-    
-    def __init__(self, freq, minor_locator=False,
-                 base=1, quarter=1, month=1, day=1):
-        TimeSeries_DateLocator.__init__(self, freq, minor_locator, False,
-                                        base, quarter, month, day)
-    
-    def __call__(self):
-        self.verify_intervals()
-        vmin, vmax = self.viewInterval.get_bounds()
-        freq = self.freqstr
-        if freq == 'A':
-            msg = "The current frequency ('%s') is too coarse!" % freq
-            raise ValueError, msg
-        elif freq == 'Q':
-            return range(vmin, vmax+1, self.base)
-        else:
-            dates = self._initialize_dates()
-            values = dates.tovalue()
-            if freq == 'M':
-                locs = (dates.months % 4 == self.month)
-            elif freq in 'BDU':
-                locs = (dates.months % 4 == self.month) & (dates.day == self.day)
-            if self.base > 1:
-                locs &= (locs.cumsum() % self.base == 1)
-            return values[locs]
-#...............................................................................
-class TimeSeries_MonthLocator(TimeSeries_DateLocator):
-    """Locates ticks along a Date axis, for each (multiple of) month.
-    
-:Ivariables:
-    - `base` : Integer
-      Gives the spacing between two consecutive quarter ticks.
-    - `month` : Integer *[1]*
-      Tells on which month the ticks should be.
-    - `day` : Integer *[1]*
-      Tells on which day the ticks should be.    
-    """
-    
-    def __init__(self, freq, minor_locator=False,
-                 base=1, quarter=1, month=1, day=1):
-        TimeSeries_DateLocator.__init__(self, freq, minor_locator, False,
-                                        base, quarter, month, day)
-    
-    def __call__(self):
-        self.verify_intervals()
-        vmin, vmax = self.viewInterval.get_bounds()
-        freq = self.freqstr
-        if freq == 'AQ':
-            msg = "The current frequency ('%s') is too coarse!" % freq
-            raise ValueError, msg
-        elif freq == 'M':
-            return range(vmin, vmax+1, self.base)
-        else:
-            dates = self._initialize_dates()
-            values = dates.tovalue()
-            if freq in 'BDU':
-                locs = (dates.months == self.month) & (dates.day == self.day)
-            if self.base > 1:
-                locs &= (locs.cumsum() % self.base == 1)
-            return values[locs]         
-
 #####---------------------------------------------------------------------------
 #---- --- Formatter ---
 #####---------------------------------------------------------------------------            
 class TimeSeries_DateFormatter(Formatter):
     """Formats the ticks along a DateArray axis."""
     
-    def __init__(self, freq, fmt=None):
-        if fmt is None:
-            fmt = Date.default_fmtstr[freq]
-        self.fmt = fmt
-        self.freqstr = freq
+    def __init__(self, freq, minor_locator=False, dynamic_mode=True,):
+        self.format = None
+        self.freq = freq
+        self.locs = []
+        self.formatdict = {}
+        self.isminor = minor_locator
+        self.isdynamic = dynamic_mode
+        self.offset = 0
+        #.....
+        if freq == TS.FR_ANN:
+            self.finder = _annual_finder
+        elif freq == TS.FR_QTR:
+            self.finder = _quarterly_finder
+        elif freq == TS.FR_MTH:
+            self.finder = _monthly_finder
+        elif freq in (TS.FR_BUS, TS.FR_DAY):
+            self.finder = _daily_finder
+            
+    def asminor(self):
+        "Returns the formatter set to minor mode."
+        self.isminor = True
+        return self
     
+    def asmajor(self):
+        "Returns the fromatter set to major mode."
+        self.isminor = False
+        return self
+    
+    def _set_default_format(self, vmin, vmax):
+        "Returns the default ticks spacing."
+        self.formatdict = self.finder(vmin, vmax, self.freq, False)
+        return self.formatdict
+    
+    def set_locs(self, locs):
+        'Sets the locations of the ticks'
+        self.locs = locs
+        if len(self.locs) > 0:
+            self.verify_intervals()
+            self._set_default_format(locs[0], locs[-1])
+    #
     def __call__(self, x, pos=0):
-        return Date(self.freqstr, value=int(x)).strfmt(self.fmt)
+        if self.isminor:
+            fmt = self.formatdict.pop(x, '')
+            if fmt is not '':
+                retval = Date(self.freq, value=int(x)).strfmt(fmt)
+            else:
+                retval = ''
+        else:
+            retval = ''
+        return retval
+    
 
 
+
 #####--------------------------------------------------------------------------
 #---- --- TimeSeries plots ---
 #####--------------------------------------------------------------------------
@@ -514,13 +605,13 @@
             assert hasattr(_series, "dates")
             self._series = _series.ravel()
             self.xdata = _series.dates
-            self.freqstr = _series.dates.freqstr
+            self.freq = _series.dates.freq
             self.xaxis.set_major_locator
             
         else:
             self._series = None
             self.xdata = None
-            self.freqstr = None
+            self.freq = None
         self._austoscale = False
         # Get the data to plot 
         self.legendsymbols = []
@@ -567,9 +658,9 @@
             # The argument is a DateArray............
             elif isinstance(a, (Date, DateArray)):
                 # Force to current freq
-                if self.freqstr is not None:
-                    if a.freqstr != self.freqstr:
-                        a = a.asfreq(self.freqstr)
+                if self.freq is not None:
+                    if a.freq != self.freq:
+                        a = a.asfreq(self.freq)
                 # There's an argument after
                 if len(remaining) > 0:
                     #...and it's a format string
@@ -594,12 +685,9 @@
                                 output.extend([a,b,c])
                             else:
                                 output.extend([a,b])
-                     #   continue
                 else:
                     if self.ydata is None:
                         raise ValueError, "No data information available!"
-                    #else:
-                    #    break
             # Otherwise..............................
             elif len(remaining) > 0:
                 if isinstance(remaining[0], str):
@@ -608,20 +696,18 @@
                         raise ValueError, "No date information available!"
                     else:
                         output.extend([self.xdata, a, b])
-                    #continue
                 elif self.xdata is None:
                     raise ValueError, "No date information available!"
                 else:
                     output.extend([self.xdata, a])
-                    #continue
         # Reinitialize the plot if needed ...........
         if self.xdata is None:
             self.xdata = output[0]
-            self.freqstr = self.xdata.freqstr
+            self.freq = self.xdata.freq
         # Force the xdata to the current frequency
-        elif output[0].freqstr != self.freqstr:
+        elif output[0].freq != self.freq:
             output = list(output)
-            output[0] = output[0].asfreq(self.freqstr)
+            output[0] = output[0].asfreq(self.freq)
         return output
     #............................................
     def tsplot(self,*parms,**kwargs):
@@ -631,11 +717,6 @@
         parms = self._check_plot_params(*parms)
         self.legendlabels.append(kwargs.get('label',None))
         Subplot.plot(self, *parms,**kwargs)
-        pylab.draw_if_interactive()
-#    #............................................
-#    def ybaseline(self,ybase,**kwargs):
-#        """Plots an horizontal baseline on each subplot."""
-#        self.axhline(ybase,**kwargs)
     #............................................       
     def format_dateaxis(self,maj_spacing=None, min_spacing=None, 
                         strformat="%Y", rotate=True):
@@ -649,49 +730,28 @@
     `strformat` : String *['%Y']*
         String format for major ticks ("%Y").
         """
-        # Get the locator class .................
-        if self.freqstr in 'BDU':
-            locator = TimeSeries_DailyLocator
-            self.xaxis.set_major_locator(locator(self.freqstr,
-                                                 minor_locator=False,
-                                                 dynamic_mode=True))
-            self.xaxis.set_minor_locator(locator(self.freqstr,
-                                                 minor_locator=True,
-                                                 dynamic_mode=True))
-        else:
-            if self.freqstr == 'A':
-                locator = TimeSeries_AnnualLocator
-            elif self.freqstr == 'Q':
-                locator = TimeSeries_QuarterlyLocator
-            elif self.freqstr == 'M':
-                locator = TimeSeries_MonthlyLocator
-            self.xaxis.set_major_locator(locator(minor_locator=False,
-                                                 dynamic_mode=True))
-            self.xaxis.set_minor_locator(locator(minor_locator=True,
-                                                 dynamic_mode=True))
+        # Get the locator class ................. 
+        majlocator = TimeSeries_DateLocator(self.freq, dynamic_mode=True,
+                                            minor_locator=False)
+        minlocator = TimeSeries_DateLocator(self.freq, dynamic_mode=True,
+                                            minor_locator=True)
+        self.xaxis.set_major_locator(majlocator)
+        self.xaxis.set_minor_locator(minlocator)
+        # Get the formatter .....................
+        majformatter = TimeSeries_DateFormatter(self.freq, dynamic_mode=True,
+                                                minor_locator=False)
+        minformatter = TimeSeries_DateFormatter(self.freq, dynamic_mode=True,
+                                                minor_locator=True)
+        self.xaxis.set_major_formatter(majformatter)
+        self.xaxis.set_minor_formatter(minformatter)
         #........................................
-        self.xaxis.set_major_formatter(TimeSeries_DateFormatter(self.freqstr))
-        if rcParams['backend'] == 'PS':
-            rotate = False
-            warnings.warn("dateplot: PS backend detected, rotate disabled")
-        if self.is_last_row():
-            if rotate:
-                setp(self.get_xticklabels(),rotation=45)
-#        self.xaxis.set_major_formatter(FuncFormatter(self.dateticks_formatter))
-#        self.xaxis.set_minor_formatter(FuncFormatter(self.dateticks_formatter))
-#        else:
-#            self.set_xticklabels([])
-#            self.set_xlabel('')          
-#    #............................................
-#    def plot_shifts(self,shifts,**kwargs):
-#        """Plots regime shifts.
-#:param shifts: Shifts/trends to plot.
-#:type shifts: `RegimeShift`
-#        """
-#        self.tsplot(self.xdata,shifts.regimes,**kwargs)
-#        for x in shifts.xshifts[0]:
-#            self.axvline(self.xdata[x],ls=':',c='#999999',lw=0.5)    
-    #............................................
+#        if rcParams['backend'] == 'PS':
+#            rotate = False
+#            warnings.warn("dateplot: PS backend detected, rotate disabled")
+#        if self.is_last_row():
+#            if rotate:
+#                setp(self.get_xticklabels(),rotation=45)
+
 TSPlot = TimeSeriesPlot
 
 
@@ -715,7 +775,6 @@
         return add_generic_subplot(self, *args, **kwargs)
     add_plot = add_tsplot
 TSFigure = TimeSeriesFigure
-#Figure.add_tsplot = 
 #................................................
 def tsfigure(series, **figargs):    
     """Creates a new `TimeSeriesFigure` object.
@@ -728,8 +787,6 @@
     """
     figargs.update(FigureClass=TSFigure)
     figargs.update(series=series)
-#    print "figargs:",figargs
-#    num = figargs.pop('num',None)
     fig = pylab.figure(**figargs)
     return fig
 
@@ -760,16 +817,16 @@
 ################################################################################
 if __name__ == '__main__':
 
-    da = date_array(start_date=Date(freq='D', year=2003, quarter=3, month=1, day=17), 
-                    length=51)
+    da = date_array(start_date=Date(freq='B', year=2003, quarter=3, month=1, day=17), 
+                    length=10)
     ser = timeseries.time_series(MA.arange(len(da)), dates=da)
-    ser[4] = MA.masked
-    ser_2 = timeseries.time_series(MA.arange(len(da)), dates=da.asfreq('M'))
+#    ser[4] = MA.masked
+#    ser_2 = timeseries.time_series(MA.arange(len(da)), dates=da.asfreq('Q'))
     
     pylab.figure()
     pylab.gcf().add_tsplot(111)
     pylab.gca().tsplot(ser, 'ko-')
     pylab.gca().format_dateaxis()
-    pylab.gca().tsplot(ser_2, 'rs')
+#    pylab.gca().tsplot(ser_2, 'rs')
     pylab.show()
     
\ No newline at end of file




More information about the Scipy-svn mailing list