pydata/pandas — pull request #5850 by jreback (2014-01-04 18:03:12+00:00)
Title: ENH: enhancements to Panel.apply to enable arbitrary functions and multi-dim slicing (GH1148)

closes #1148

A reproduction of the new docs section

Applying with a Panel will pass a Series to the applied function. If the applied function returns a Series, the result of the application will be a Panel. If the applied function reduces to a scalar, the result of the application will be a DataFrame.

Note Prior to 0.13.1 apply on a Panel would only work on ufuncs (e.g. np.sum/np.max).

```
In [120]: import pandas.util.testing as tm

In [121]: panel = tm.makePanel(5)

In [122]: panel

Dimensions: 3 (items) x 5 (major_axis) x 4 (minor_axis)
Items axis: ItemA to ItemC
Major_axis axis: 2000-01-03 00:00:00 to 2000-01-07 00:00:00
Minor_axis axis: A to D

In [123]: panel['ItemA']

A B C D
2000-01-03 0.166882 -0.597361 -1.200639 0.174260
2000-01-04 -1.759496 -1.514940 -1.872993 -0.581163
2000-01-05 0.901336 -1.640398 0.825210 0.087916
2000-01-06 -0.317478 -1.130643 -0.392715 0.416971
2000-01-07 -0.681335 -0.245890 -1.994150 0.666084
[5 rows x 4 columns]
```

A transformational apply.
```
In [124]: result = panel.apply(lambda x: x*2, axis='items')

In [125]: result

Dimensions: 3 (items) x 5 (major_axis) x 4 (minor_axis)
Items axis: ItemA to ItemC
Major_axis axis: 2000-01-03 00:00:00 to 2000-01-07 00:00:00
Minor_axis axis: A to D

In [126]: result['ItemA']

A B C D
2000-01-03 0.333764 -1.194722 -2.401278 0.348520
2000-01-04 -3.518991 -3.029880 -3.745986 -1.162326
2000-01-05 1.802673 -3.280796 1.650421 0.175832
2000-01-06 -0.634955 -2.261286 -0.785430 0.833943
2000-01-07 -1.362670 -0.491779 -3.988300 1.332168
[5 rows x 4 columns]
```

A reduction operation.
```
In [127]: panel.apply(lambda x: x.dtype, axis='items')

A B C D
2000-01-03 float64 float64 float64 float64
2000-01-04 float64 float64 float64 float64
2000-01-05 float64 float64 float64 float64
2000-01-06 float64 float64 float64 float64
2000-01-07 float64 float64 float64 float64
[5 rows x 4 columns]
```

A similar reduction type operation
```
In [128]: panel.apply(lambda x: x.sum(), axis='major_axis')

ItemA ItemB ItemC
A -1.690090 1.840259 0.010754
B -5.129232 0.860182 0.178018
C -4.635286 0.545328 2.456520
D 0.764068 -3.623586 1.761541
[4 rows x 3 columns]
```

This last reduction is equivalent to
```
In [129]: panel.sum('major_axis')

ItemA ItemB ItemC
A -1.690090 1.840259 0.010754
B -5.129232 0.860182 0.178018
C -4.635286 0.545328 2.456520
D 0.764068 -3.623586 1.761541
[4 rows x 3 columns]
```

A transformation operation that returns a Panel, but is computing the z-score across the major_axis.

```
In [130]: result = panel.apply(lambda x: (x-x.mean())/x.std(), axis='major_axis')

In [131]: result

Dimensions: 3 (items) x 5 (major_axis) x 4 (minor_axis)
Items axis: ItemA to ItemC
Major_axis axis: 2000-01-03 00:00:00 to 2000-01-07 00:00:00
Minor_axis axis: A to D

In [132]: result['ItemA']

A B C D
2000-01-03 0.509389 0.719204 -0.234072 0.045812
2000-01-04 -1.434116 -0.820934 -0.809328 -1.567858
2000-01-05 1.250373 -1.031513 1.499214 -0.138629
2000-01-06 0.020723 -0.175899 0.457175 0.564271
2000-01-07 -0.346370 1.309142 -0.912988 1.096405
[5 rows x 4 columns]
```

Apply can also accept multiple axes in the axis argument. This will pass a DataFrame of the cross-section to the applied function.

```
In [133]: f = lambda x: (x-x.mean(1)/x.std(1))

In [134]: result = panel.apply(f, axis = ['items','major_axis'])

In [135]: result

Dimensions: 4 (items) x 5 (major_axis) x 3 (minor_axis)
Items axis: A to D
Major_axis axis: 2000-01-03 00:00:00 to 2000-01-07 00:00:00
Minor_axis axis: ItemA to ItemC

In [136]: result.loc[:,:,'ItemA']

A B C D
2000-01-03 0.748886 -0.323319 -1.172352 0.370451
2000-01-04 -1.594544 -1.659365 -1.444732 -0.162764
2000-01-05 0.908832 -1.220236 0.237668 0.754405
2000-01-06 -1.024669 -0.081850 -0.792957 0.641960
2000-01-07 -0.884333 -0.472889 -1.474646 -0.671871
[5 rows x 4 columns]
```

This is equivalent to the following
```
In [137]: result = Panel(dict([ (ax,f(panel.loc[:,:,ax])) for ax in panel.minor_axis ]))

In [138]: result

Dimensions: 4 (items) x 5 (major_axis) x 3 (minor_axis)
Items axis: A to D
Major_axis axis: 2000-01-03 00:00:00 to 2000-01-07 00:00:00
Minor_axis axis: ItemA to ItemC

In [139]: result.loc[:,:,'ItemA']

A B C D
2000-01-03 0.748886 -0.323319 -1.172352 0.370451
2000-01-04 -1.594544 -1.659365 -1.444732 -0.162764
2000-01-05 0.908832 -1.220236 0.237668 0.754405
2000-01-06 -1.024669 -0.081850 -0.792957 0.641960
2000-01-07 -0.884333 -0.472889 -1.474646 -0.671871
[5 rows x 4 columns]
```