@@ -6,9 +6,12 @@ cimport cython
66
77import numpy as np
88cimport numpy as np
9+ from numpy cimport int64_t
910np.import_array()
1011
11- from util cimport is_integer_object
12+ from util cimport is_integer_object, is_string_object
13+
14+ from ccalendar import MONTH_NUMBERS
1215
1316# ----------------------------------------------------------------------
1417# Constants
@@ -23,6 +26,22 @@ _INVALID_FREQ_ERROR = "Invalid frequency: {0}"
2326# ---------------------------------------------------------------------
2427# Period codes
2528
29+
30+ class FreqGroup (object ):
31+ FR_ANN = 1000
32+ FR_QTR = 2000
33+ FR_MTH = 3000
34+ FR_WK = 4000
35+ FR_BUS = 5000
36+ FR_DAY = 6000
37+ FR_HR = 7000
38+ FR_MIN = 8000
39+ FR_SEC = 9000
40+ FR_MS = 10000
41+ FR_US = 11000
42+ FR_NS = 12000
43+
44+
2645# period frequency constants corresponding to scikits timeseries
2746# originals
2847_period_code_map = {
@@ -125,8 +144,8 @@ cpdef get_freq_code(freqstr):
125144 -------
126145 return : tuple of base frequency code and stride (mult)
127146
128- Example
129- -------
147+ Examples
148+ --------
130149 >>> get_freq_code('3D')
131150 (6000, 3)
132151
@@ -203,3 +222,292 @@ cpdef _period_str_to_code(freqstr):
203222 return _period_code_map[freqstr]
204223 except KeyError :
205224 raise ValueError (_INVALID_FREQ_ERROR.format(freqstr))
225+
226+
227+ cpdef str get_freq_str(base, mult = 1 ):
228+ """
229+ Return the summary string associated with this offset code, possibly
230+ adjusted by a multiplier.
231+
232+ Parameters
233+ ----------
234+ base : int (member of FreqGroup)
235+
236+ Returns
237+ -------
238+ freq_str : str
239+
240+ Examples
241+ --------
242+ >>> get_freq_str(1000)
243+ 'A-DEC'
244+
245+ >>> get_freq_str(2000, 2)
246+ '2Q-DEC'
247+
248+ >>> get_freq_str("foo")
249+ """
250+ code = _reverse_period_code_map.get(base)
251+ if mult == 1 :
252+ return code
253+ return str (mult) + code
254+
255+
256+ cpdef str get_base_alias(freqstr):
257+ """
258+ Returns the base frequency alias, e.g., '5D' -> 'D'
259+
260+ Parameters
261+ ----------
262+ freqstr : str
263+
264+ Returns
265+ -------
266+ base_alias : str
267+ """
268+ return _base_and_stride(freqstr)[0 ]
269+
270+
271+ cpdef int get_to_timestamp_base(int base):
272+ """
273+ Return frequency code group used for base of to_timestamp against
274+ frequency code.
275+
276+ Parameters
277+ ----------
278+ base : int (member of FreqGroup)
279+
280+ Returns
281+ -------
282+ base : int
283+
284+ Examples
285+ --------
286+ # Return day freq code against longer freq than day
287+ >>> get_to_timestamp_base(get_freq_code('D')[0])
288+ 6000
289+ >>> get_to_timestamp_base(get_freq_code('W')[0])
290+ 6000
291+ >>> get_to_timestamp_base(get_freq_code('M')[0])
292+ 6000
293+
294+ # Return second freq code against hour between second
295+ >>> get_to_timestamp_base(get_freq_code('H')[0])
296+ 9000
297+ >>> get_to_timestamp_base(get_freq_code('S')[0])
298+ 9000
299+ """
300+ if base < FreqGroup.FR_BUS:
301+ return FreqGroup.FR_DAY
302+ elif FreqGroup.FR_HR <= base <= FreqGroup.FR_SEC:
303+ return FreqGroup.FR_SEC
304+ return base
305+
306+
307+ cpdef object get_freq(object freq):
308+ """
309+ Return frequency code of given frequency str.
310+ If input is not string, return input as it is.
311+
312+ Examples
313+ --------
314+ >>> get_freq('A')
315+ 1000
316+
317+ >>> get_freq('3A')
318+ 1000
319+ """
320+ if is_string_object(freq):
321+ base, mult = get_freq_code(freq)
322+ freq = base
323+ return freq
324+
325+
326+ # ----------------------------------------------------------------------
327+ # Frequency comparison
328+
329+ cpdef bint is_subperiod(source, target):
330+ """
331+ Returns True if downsampling is possible between source and target
332+ frequencies
333+
334+ Parameters
335+ ----------
336+ source : string or DateOffset
337+ Frequency converting from
338+ target : string or DateOffset
339+ Frequency converting to
340+
341+ Returns
342+ -------
343+ is_subperiod : boolean
344+ """
345+
346+ if target is None or source is None :
347+ return False
348+ source = _maybe_coerce_freq(source)
349+ target = _maybe_coerce_freq(target)
350+
351+ if _is_annual(target):
352+ if _is_quarterly(source):
353+ return _quarter_months_conform(get_rule_month(source),
354+ get_rule_month(target))
355+ return source in {' D' , ' C' , ' B' , ' M' , ' H' , ' T' , ' S' , ' L' , ' U' , ' N' }
356+ elif _is_quarterly(target):
357+ return source in {' D' , ' C' , ' B' , ' M' , ' H' , ' T' , ' S' , ' L' , ' U' , ' N' }
358+ elif _is_monthly(target):
359+ return source in {' D' , ' C' , ' B' , ' H' , ' T' , ' S' , ' L' , ' U' , ' N' }
360+ elif _is_weekly(target):
361+ return source in {target, ' D' , ' C' , ' B' , ' H' , ' T' , ' S' , ' L' , ' U' , ' N' }
362+ elif target == ' B' :
363+ return source in {' B' , ' H' , ' T' , ' S' , ' L' , ' U' , ' N' }
364+ elif target == ' C' :
365+ return source in {' C' , ' H' , ' T' , ' S' , ' L' , ' U' , ' N' }
366+ elif target == ' D' :
367+ return source in {' D' , ' H' , ' T' , ' S' , ' L' , ' U' , ' N' }
368+ elif target == ' H' :
369+ return source in {' H' , ' T' , ' S' , ' L' , ' U' , ' N' }
370+ elif target == ' T' :
371+ return source in {' T' , ' S' , ' L' , ' U' , ' N' }
372+ elif target == ' S' :
373+ return source in {' S' , ' L' , ' U' , ' N' }
374+ elif target == ' L' :
375+ return source in {' L' , ' U' , ' N' }
376+ elif target == ' U' :
377+ return source in {' U' , ' N' }
378+ elif target == ' N' :
379+ return source in {' N' }
380+
381+
382+ cpdef bint is_superperiod(source, target):
383+ """
384+ Returns True if upsampling is possible between source and target
385+ frequencies
386+
387+ Parameters
388+ ----------
389+ source : string
390+ Frequency converting from
391+ target : string
392+ Frequency converting to
393+
394+ Returns
395+ -------
396+ is_superperiod : boolean
397+ """
398+ if target is None or source is None :
399+ return False
400+ source = _maybe_coerce_freq(source)
401+ target = _maybe_coerce_freq(target)
402+
403+ if _is_annual(source):
404+ if _is_annual(target):
405+ return get_rule_month(source) == get_rule_month(target)
406+
407+ if _is_quarterly(target):
408+ smonth = get_rule_month(source)
409+ tmonth = get_rule_month(target)
410+ return _quarter_months_conform(smonth, tmonth)
411+ return target in {' D' , ' C' , ' B' , ' M' , ' H' , ' T' , ' S' , ' L' , ' U' , ' N' }
412+ elif _is_quarterly(source):
413+ return target in {' D' , ' C' , ' B' , ' M' , ' H' , ' T' , ' S' , ' L' , ' U' , ' N' }
414+ elif _is_monthly(source):
415+ return target in {' D' , ' C' , ' B' , ' H' , ' T' , ' S' , ' L' , ' U' , ' N' }
416+ elif _is_weekly(source):
417+ return target in {source, ' D' , ' C' , ' B' , ' H' , ' T' , ' S' , ' L' , ' U' , ' N' }
418+ elif source == ' B' :
419+ return target in {' D' , ' C' , ' B' , ' H' , ' T' , ' S' , ' L' , ' U' , ' N' }
420+ elif source == ' C' :
421+ return target in {' D' , ' C' , ' B' , ' H' , ' T' , ' S' , ' L' , ' U' , ' N' }
422+ elif source == ' D' :
423+ return target in {' D' , ' C' , ' B' , ' H' , ' T' , ' S' , ' L' , ' U' , ' N' }
424+ elif source == ' H' :
425+ return target in {' H' , ' T' , ' S' , ' L' , ' U' , ' N' }
426+ elif source == ' T' :
427+ return target in {' T' , ' S' , ' L' , ' U' , ' N' }
428+ elif source == ' S' :
429+ return target in {' S' , ' L' , ' U' , ' N' }
430+ elif source == ' L' :
431+ return target in {' L' , ' U' , ' N' }
432+ elif source == ' U' :
433+ return target in {' U' , ' N' }
434+ elif source == ' N' :
435+ return target in {' N' }
436+
437+
438+ cdef str _maybe_coerce_freq(code):
439+ """ we might need to coerce a code to a rule_code
440+ and uppercase it
441+
442+ Parameters
443+ ----------
444+ source : string or DateOffset
445+ Frequency converting from
446+
447+ Returns
448+ -------
449+ code : string
450+ """
451+ assert code is not None
452+ if getattr (code, ' _typ' , None ) == ' dateoffset' :
453+ # i.e. isinstance(code, ABCDateOffset):
454+ code = code.rule_code
455+ return code.upper()
456+
457+
458+ cdef bint _quarter_months_conform(str source, str target):
459+ snum = MONTH_NUMBERS[source]
460+ tnum = MONTH_NUMBERS[target]
461+ return snum % 3 == tnum % 3
462+
463+
464+ cdef bint _is_annual(str rule):
465+ rule = rule.upper()
466+ return rule == ' A' or rule.startswith(' A-' )
467+
468+
469+ cdef bint _is_quarterly(str rule):
470+ rule = rule.upper()
471+ return rule == ' Q' or rule.startswith(' Q-' ) or rule.startswith(' BQ' )
472+
473+
474+ cdef bint _is_monthly(str rule):
475+ rule = rule.upper()
476+ return rule == ' M' or rule == ' BM'
477+
478+
479+ cdef bint _is_weekly(str rule):
480+ rule = rule.upper()
481+ return rule == ' W' or rule.startswith(' W-' )
482+
483+
484+ # ----------------------------------------------------------------------
485+
486+ cpdef object get_rule_month(object source, object default = ' DEC' ):
487+ """
488+ Return starting month of given freq, default is December.
489+
490+ Parameters
491+ ----------
492+ source : object
493+ default : object (default "DEC")
494+
495+ Returns
496+ -------
497+ rule_month: object (usually string)
498+
499+ Examples
500+ --------
501+ >>> get_rule_month('D')
502+ 'DEC'
503+
504+ >>> get_rule_month('A-JAN')
505+ 'JAN'
506+ """
507+ if hasattr (source, ' freqstr' ):
508+ source = source.freqstr
509+ source = source.upper()
510+ if ' -' not in source:
511+ return default
512+ else :
513+ return source.split(' -' )[1 ]
0 commit comments