@@ -339,3 +339,153 @@ def test_value_counts_object_inference_deprecated():
339
339
exp = dti .value_counts ()
340
340
exp .index = exp .index .astype (object )
341
341
tm .assert_series_equal (res , exp )
342
+
343
+
344
+ def _vc_make_index (kind : str , periods = 5 , freq = "D" ):
345
+ if kind == "dt" :
346
+ return pd .date_range ("2016-01-01" , periods = periods , freq = freq )
347
+ if kind == "td" :
348
+ return pd .timedelta_range (Timedelta (0 ), periods = periods , freq = freq )
349
+ raise ValueError ("kind must be 'dt' or 'td'" )
350
+
351
+
352
+ @pytest .mark .parametrize (
353
+ "kind,freq,normalize" ,
354
+ [
355
+ ("dt" , "D" , False ),
356
+ ("dt" , "D" , True ),
357
+ ("td" , "D" , False ),
358
+ ("td" , "D" , True ),
359
+ ("td" , Timedelta (hours = 1 ), False ),
360
+ ("td" , Timedelta (hours = 1 ), True ),
361
+ ],
362
+ )
363
+ def test_value_counts_freq_preserved_datetimelike_no_sort (kind , freq , normalize ):
364
+ idx = _vc_make_index (kind , periods = 5 , freq = freq )
365
+ vc = idx .value_counts (sort = False , normalize = normalize )
366
+ assert vc .index .freq == idx .freq
367
+ if normalize :
368
+ assert np .isclose (vc .values , 1 / len (idx )).all ()
369
+
370
+
371
+ @pytest .mark .parametrize (
372
+ "kind,freq" ,
373
+ [
374
+ ("dt" , "D" ),
375
+ ("td" , "D" ),
376
+ ("td" , Timedelta (hours = 1 )),
377
+ ],
378
+ )
379
+ def test_value_counts_freq_drops_datetimelike_when_sorted (kind , freq ):
380
+ idx = _vc_make_index (kind , periods = 5 , freq = freq )
381
+ vc = idx .value_counts () # default sort=True (reorders)
382
+ assert vc .index .freq is None
383
+
384
+
385
+ @pytest .mark .parametrize (
386
+ "kind,freq" ,
387
+ [
388
+ ("dt" , "D" ),
389
+ ("td" , "D" ),
390
+ ("td" , Timedelta (hours = 1 )),
391
+ ],
392
+ )
393
+ def test_value_counts_freq_drops_datetimelike_with_duplicates (kind , freq ):
394
+ base = _vc_make_index (kind , periods = 5 , freq = freq )
395
+ obj = base .insert (1 , base [1 ]) # duplicate one label
396
+ vc = obj .value_counts (sort = False )
397
+ assert vc .index .freq is None
398
+
399
+
400
+ @pytest .mark .parametrize (
401
+ "kind,freq" ,
402
+ [
403
+ ("dt" , "D" ),
404
+ ("td" , "D" ),
405
+ ("td" , Timedelta (hours = 1 )),
406
+ ],
407
+ )
408
+ def test_value_counts_freq_drops_datetimelike_with_gap (kind , freq ):
409
+ base = _vc_make_index (kind , periods = 5 , freq = freq )
410
+ obj = base .delete (2 ) # remove one step to break contiguity
411
+ vc = obj .value_counts (sort = False )
412
+ assert vc .index .freq is None
413
+
414
+
415
+ @pytest .mark .parametrize (
416
+ "kind,freq,dropna,expect_hasnans" ,
417
+ [
418
+ ("dt" , "D" , False , True ), # keep NaT
419
+ ("dt" , "D" , True , False ), # drop NaT
420
+ ("td" , "D" , False , True ),
421
+ ("td" , "D" , True , False ),
422
+ ("td" , Timedelta (hours = 1 ), False , True ),
423
+ ("td" , Timedelta (hours = 1 ), True , False ),
424
+ ],
425
+ )
426
+ def test_value_counts_freq_drops_datetimelike_with_nat (
427
+ kind , freq , dropna , expect_hasnans
428
+ ):
429
+ base = _vc_make_index (kind , periods = 3 , freq = freq )
430
+ obj = base .insert (1 , pd .NaT )
431
+ vc = obj .value_counts (dropna = dropna , sort = False )
432
+ assert vc .index .freq is None
433
+ assert vc .index .hasnans is expect_hasnans
434
+
435
+
436
+ @pytest .mark .parametrize (
437
+ "freq,start,periods,sort" ,
438
+ [
439
+ ("D" , "2016-01-01" , 5 , False ),
440
+ ("D" , "2016-01-01" , 5 , True ),
441
+ ("M" , "2016-01" , 6 , False ), # MonthEnd
442
+ ("M" , "2016-01" , 6 , True ),
443
+ ("Q-DEC" , "2016Q1" , 4 , False ), # QuarterEnd (Dec anchored)
444
+ ("Q-DEC" , "2016Q1" , 4 , True ),
445
+ ("Y-DEC" , "2014" , 3 , False ), # YearEnd (Dec anchored)
446
+ ("Y-DEC" , "2014" , 3 , True ),
447
+ ],
448
+ )
449
+ def test_value_counts_period_freq_preserved_sort_and_nosort (freq , start , periods , sort ):
450
+ pi = pd .period_range (start = start , periods = periods , freq = freq )
451
+ vc = pi .value_counts (sort = sort )
452
+ assert isinstance (vc .index , pd .PeriodIndex )
453
+ assert vc .index .dtype == pi .dtype
454
+ assert vc .index .freq == pi .freq
455
+
456
+
457
+ def test_value_counts_period_freq_preserved_with_duplicates ():
458
+ pi = pd .period_range ("2016-01" , periods = 5 , freq = "M" )
459
+ obj = pi .insert (1 , pi [1 ]) # duplicate one label
460
+ vc = obj .value_counts (sort = False )
461
+ assert isinstance (vc .index , pd .PeriodIndex )
462
+ assert vc .index .dtype == pi .dtype
463
+ assert vc .index .freq == pi .freq
464
+
465
+
466
+ def test_value_counts_period_freq_preserved_with_gap ():
467
+ pi = pd .period_range ("2016-01" , periods = 5 , freq = "M" )
468
+ obj = pi .delete (2 ) # remove one element
469
+ vc = obj .value_counts (sort = False )
470
+ assert isinstance (vc .index , pd .PeriodIndex )
471
+ assert vc .index .dtype == pi .dtype
472
+ assert vc .index .freq == pi .freq
473
+
474
+
475
+ def test_value_counts_period_freq_preserved_with_normalize ():
476
+ pi = pd .period_range ("2016-01" , periods = 4 , freq = "M" )
477
+ vc = pi .value_counts (normalize = True , sort = False )
478
+ assert isinstance (vc .index , pd .PeriodIndex )
479
+ assert vc .index .dtype == pi .dtype
480
+ assert vc .index .freq == pi .freq
481
+ assert np .isclose (vc .values , 1 / len (pi )).all ()
482
+
483
+
484
+ def test_value_counts_period_freq_preserved_with_nat_dropna_true ():
485
+ pi = pd .period_range ("2016-01" , periods = 5 , freq = "M" )
486
+ obj = pi .insert (1 , pd .NaT )
487
+ vc = obj .value_counts (dropna = True , sort = False )
488
+ assert not vc .index .hasnans
489
+ assert isinstance (vc .index , pd .PeriodIndex )
490
+ assert vc .index .dtype == pi .dtype
491
+ assert vc .index .freq == pi .freq
0 commit comments