@@ -445,6 +445,23 @@ def _selection_list(self):
445
445
return [self ._selection ]
446
446
return self ._selection
447
447
448
+ @cache_readonly
449
+ def _selected_obj (self ):
450
+
451
+ if self ._selection is None or isinstance (self .obj , Series ):
452
+ return self .obj
453
+ else :
454
+ return self .obj [self ._selection ]
455
+
456
+ def _set_selection_from_grouper (self ):
457
+ """ we may need create a selection if we have non-level groupers """
458
+ grp = self .grouper
459
+ if self ._selection is None and getattr (grp ,'groupings' ,None ) is not None :
460
+ ax = self .obj ._info_axis
461
+ groupers = [ g .name for g in grp .groupings if g .level is None and g .name is not None and g .name in ax ]
462
+ if len (groupers ):
463
+ self ._selection = (ax - Index (groupers )).tolist ()
464
+
448
465
def _local_dir (self ):
449
466
return sorted (set (self .obj ._local_dir () + list (self ._apply_whitelist )))
450
467
@@ -453,7 +470,6 @@ def __getattr__(self, attr):
453
470
return object .__getattribute__ (self , attr )
454
471
if attr in self .obj :
455
472
return self [attr ]
456
-
457
473
if hasattr (self .obj , attr ):
458
474
return self ._make_wrapper (attr )
459
475
@@ -472,6 +488,10 @@ def _make_wrapper(self, name):
472
488
type (self ).__name__ ))
473
489
raise AttributeError (msg )
474
490
491
+ # need to setup the selection
492
+ # as are not passed directly but in the grouper
493
+ self ._set_selection_from_grouper ()
494
+
475
495
f = getattr (self ._selected_obj , name )
476
496
if not isinstance (f , types .MethodType ):
477
497
return self .apply (lambda self : getattr (self , name ))
@@ -503,7 +523,19 @@ def curried(x):
503
523
try :
504
524
return self .apply (curried_with_axis )
505
525
except Exception :
506
- return self .apply (curried )
526
+ try :
527
+ return self .apply (curried )
528
+ except Exception :
529
+
530
+ # related to : GH3688
531
+ # try item-by-item
532
+ # this can be called recursively, so need to raise ValueError if
533
+ # we don't have this method to indicated to aggregate to
534
+ # mark this column as an error
535
+ try :
536
+ return self ._aggregate_item_by_item (name , * args , ** kwargs )
537
+ except (AttributeError ):
538
+ raise ValueError
507
539
508
540
return wrapper
509
541
@@ -624,6 +656,7 @@ def mean(self):
624
656
except GroupByError :
625
657
raise
626
658
except Exception : # pragma: no cover
659
+ self ._set_selection_from_grouper ()
627
660
f = lambda x : x .mean (axis = self .axis )
628
661
return self ._python_agg_general (f )
629
662
@@ -639,6 +672,7 @@ def median(self):
639
672
raise
640
673
except Exception : # pragma: no cover
641
674
675
+ self ._set_selection_from_grouper ()
642
676
def f (x ):
643
677
if isinstance (x , np .ndarray ):
644
678
x = Series (x )
@@ -655,6 +689,7 @@ def std(self, ddof=1):
655
689
if ddof == 1 :
656
690
return self ._cython_agg_general ('std' )
657
691
else :
692
+ self ._set_selection_from_grouper ()
658
693
f = lambda x : x .std (ddof = ddof )
659
694
return self ._python_agg_general (f )
660
695
@@ -667,6 +702,7 @@ def var(self, ddof=1):
667
702
if ddof == 1 :
668
703
return self ._cython_agg_general ('var' )
669
704
else :
705
+ self ._set_selection_from_grouper ()
670
706
f = lambda x : x .var (ddof = ddof )
671
707
return self ._python_agg_general (f )
672
708
@@ -677,12 +713,14 @@ def size(self):
677
713
"""
678
714
return self .grouper .size ()
679
715
680
- def count (self ):
716
+ def count (self , axis = 0 ):
681
717
"""
682
718
Number of non-null items in each group.
683
-
719
+ axis : axis number, default 0
720
+ the grouping axis
684
721
"""
685
- return self ._python_agg_general (lambda x : notnull (x ).sum ())
722
+ self ._set_selection_from_grouper ()
723
+ return self ._python_agg_general (lambda x : notnull (x ).sum (axis = axis )).astype ('int64' )
686
724
687
725
sum = _groupby_function ('sum' , 'add' , np .sum )
688
726
prod = _groupby_function ('prod' , 'prod' , np .prod )
@@ -693,12 +731,14 @@ def count(self):
693
731
last = _groupby_function ('last' , 'last' , _last_compat , numeric_only = False ,
694
732
_convert = True )
695
733
734
+
696
735
def ohlc (self ):
697
736
"""
698
- Deprecated, use .resample(how="ohlc") instead.
699
-
737
+ Compute sum of values, excluding missing values
738
+ For multiple groupings, the result index will be a MultiIndex
700
739
"""
701
- raise AttributeError ('ohlc is deprecated, use resample(how="ohlc").' )
740
+ return self ._apply_to_column_groupbys (
741
+ lambda x : x ._cython_agg_general ('ohlc' ))
702
742
703
743
def nth (self , n , dropna = None ):
704
744
"""
@@ -894,13 +934,6 @@ def _cumcount_array(self, arr=None, **kwargs):
894
934
cumcounts [v ] = arr [len (v )- 1 ::- 1 ]
895
935
return cumcounts
896
936
897
- @cache_readonly
898
- def _selected_obj (self ):
899
- if self ._selection is None or isinstance (self .obj , Series ):
900
- return self .obj
901
- else :
902
- return self .obj [self ._selection ]
903
-
904
937
def _index_with_as_index (self , b ):
905
938
"""
906
939
Take boolean mask of index to be returned from apply, if as_index=True
@@ -945,7 +978,6 @@ def _cython_agg_general(self, how, numeric_only=True):
945
978
result , names = self .grouper .aggregate (obj .values , how )
946
979
except AssertionError as e :
947
980
raise GroupByError (str (e ))
948
- # infer old dytpe
949
981
output [name ] = self ._try_cast (result , obj )
950
982
951
983
if len (output ) == 0 :
@@ -954,8 +986,6 @@ def _cython_agg_general(self, how, numeric_only=True):
954
986
return self ._wrap_aggregated_output (output , names )
955
987
956
988
def _python_agg_general (self , func , * args , ** kwargs ):
957
- _dtype = kwargs .pop ("_dtype" , None )
958
-
959
989
func = _intercept_function (func )
960
990
f = lambda x : func (x , * args , ** kwargs )
961
991
@@ -964,14 +994,7 @@ def _python_agg_general(self, func, *args, **kwargs):
964
994
for name , obj in self ._iterate_slices ():
965
995
try :
966
996
result , counts = self .grouper .agg_series (obj , f )
967
-
968
- if _dtype is None : # infer old dytpe
969
- output [name ] = self ._try_cast (result , obj )
970
- elif _dtype is False :
971
- output [name ] = result
972
- else :
973
- output [name ] = _possibly_downcast_to_dtype (result , _dtype )
974
-
997
+ output [name ] = self ._try_cast (result , obj )
975
998
except TypeError :
976
999
continue
977
1000
@@ -2203,6 +2226,9 @@ def true_and_notnull(x, *args, **kwargs):
2203
2226
filtered = self ._apply_filter (indices , dropna )
2204
2227
return filtered
2205
2228
2229
+ def _apply_to_column_groupbys (self , func ):
2230
+ """ return a pass thru """
2231
+ return func (self )
2206
2232
2207
2233
class NDFrameGroupBy (GroupBy ):
2208
2234
0 commit comments