@@ -6,9 +6,12 @@ cimport cython
6
6
7
7
import numpy as np
8
8
cimport numpy as np
9
+ from numpy cimport int64_t
9
10
np.import_array()
10
11
11
- from util cimport is_integer_object
12
+ from util cimport is_integer_object, is_string_object
13
+
14
+ from ccalendar import MONTH_NUMBERS
12
15
13
16
# ----------------------------------------------------------------------
14
17
# Constants
@@ -23,6 +26,22 @@ _INVALID_FREQ_ERROR = "Invalid frequency: {0}"
23
26
# ---------------------------------------------------------------------
24
27
# Period codes
25
28
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
+
26
45
# period frequency constants corresponding to scikits timeseries
27
46
# originals
28
47
_period_code_map = {
@@ -125,8 +144,8 @@ cpdef get_freq_code(freqstr):
125
144
-------
126
145
return : tuple of base frequency code and stride (mult)
127
146
128
- Example
129
- -------
147
+ Examples
148
+ --------
130
149
>>> get_freq_code('3D')
131
150
(6000, 3)
132
151
@@ -203,3 +222,292 @@ cpdef _period_str_to_code(freqstr):
203
222
return _period_code_map[freqstr]
204
223
except KeyError :
205
224
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