@@ -2701,27 +2701,7 @@ def _wrap_applied_output(self, keys, values, not_indexed_same=False):
2701
2701
return self ._concat_objects (keys , values ,
2702
2702
not_indexed_same = not_indexed_same )
2703
2703
2704
- def transform (self , func , * args , ** kwargs ):
2705
- """
2706
- Call function producing a like-indexed DataFrame on each group and
2707
- return a DataFrame having the same indexes as the original object
2708
- filled with the transformed values
2709
-
2710
- Parameters
2711
- ----------
2712
- f : function
2713
- Function to apply to each subframe
2714
-
2715
- Notes
2716
- -----
2717
- Each subframe is endowed the attribute 'name' in case you need to know
2718
- which group you are working on.
2719
-
2720
- Examples
2721
- --------
2722
- >>> grouped = df.groupby(lambda x: mapping[x])
2723
- >>> grouped.transform(lambda x: (x - x.mean()) / x.std())
2724
- """
2704
+ def _transform_general (self , func , * args , ** kwargs ):
2725
2705
from pandas .tools .merge import concat
2726
2706
2727
2707
applied = []
@@ -2763,6 +2743,66 @@ def transform(self, func, *args, **kwargs):
2763
2743
concatenated .sort_index (inplace = True )
2764
2744
return concatenated
2765
2745
2746
+ def transform (self , func , * args , ** kwargs ):
2747
+ """
2748
+ Call function producing a like-indexed DataFrame on each group and
2749
+ return a DataFrame having the same indexes as the original object
2750
+ filled with the transformed values
2751
+
2752
+ Parameters
2753
+ ----------
2754
+ f : function
2755
+ Function to apply to each subframe
2756
+
2757
+ Notes
2758
+ -----
2759
+ Each subframe is endowed the attribute 'name' in case you need to know
2760
+ which group you are working on.
2761
+
2762
+ Examples
2763
+ --------
2764
+ >>> grouped = df.groupby(lambda x: mapping[x])
2765
+ >>> grouped.transform(lambda x: (x - x.mean()) / x.std())
2766
+ """
2767
+
2768
+ # try to do a fast transform via merge if possible
2769
+ try :
2770
+ obj = self ._obj_with_exclusions
2771
+ if isinstance (func , compat .string_types ):
2772
+ result = getattr (self , func )(* args , ** kwargs )
2773
+ else :
2774
+ cyfunc = _intercept_cython (func )
2775
+ if cyfunc and not args and not kwargs :
2776
+ result = getattr (self , cyfunc )()
2777
+ else :
2778
+ return self ._transform_general (func , * args , ** kwargs )
2779
+ except :
2780
+ return self ._transform_general (func , * args , ** kwargs )
2781
+
2782
+ # a reduction transform
2783
+ if not isinstance (result , DataFrame ):
2784
+ return self ._transform_general (func , * args , ** kwargs )
2785
+
2786
+ # nuiscance columns
2787
+ if not result .columns .equals (obj .columns ):
2788
+ return self ._transform_general (func , * args , ** kwargs )
2789
+
2790
+ # a grouped that doesn't preserve the index, remap index based on the grouper
2791
+ # and broadcast it
2792
+ if not isinstance (obj .index ,MultiIndex ) and type (result .index ) != type (obj .index ):
2793
+ results = obj .values .copy ()
2794
+ for (name , group ), (i , row ) in zip (self , result .iterrows ()):
2795
+ indexer = self ._get_index (name )
2796
+ results [indexer ] = np .tile (row .values ,len (indexer )).reshape (len (indexer ),- 1 )
2797
+ return DataFrame (results ,columns = result .columns ,index = obj .index ).convert_objects ()
2798
+
2799
+ # we can merge the result in
2800
+ # GH 7383
2801
+ names = result .columns
2802
+ result = obj .merge (result , how = 'outer' , left_index = True , right_index = True ).ix [:,- result .shape [1 ]:]
2803
+ result .columns = names
2804
+ return result
2805
+
2766
2806
def _define_paths (self , func , * args , ** kwargs ):
2767
2807
if isinstance (func , compat .string_types ):
2768
2808
fast_path = lambda group : getattr (group , func )(* args , ** kwargs )
0 commit comments