@@ -854,25 +854,28 @@ def ppcc_plot(x, a, b, dist='tukeylambda', plot=None, N=80):
854
854
return svals , ppcc
855
855
856
856
857
- def _log_mean (logx ):
857
+ def _log_mean (logx , axis ):
858
858
# compute log of mean of x from log(x)
859
- res = special .logsumexp (logx , axis = 0 ) - math .log (logx .shape [0 ])
860
- return res
859
+ return (
860
+ special .logsumexp (logx , axis = axis , keepdims = True )
861
+ - math .log (logx .shape [axis ])
862
+ )
861
863
862
864
863
- def _log_var (logx , xp ):
865
+ def _log_var (logx , xp , axis ):
864
866
# compute log of variance of x from log(x)
865
- logmean = _log_mean (logx )
867
+ logmean = _log_mean (logx , axis = axis )
866
868
# get complex dtype with component dtypes same as `logx` dtype;
867
869
dtype = xp .result_type (logx .dtype , 1j )
868
870
pij = xp .full (logx .shape , pi * 1j , dtype = dtype )
869
871
logxmu = special .logsumexp (xp .stack ((logx , logmean + pij )), axis = 0 )
870
- res = (xp .real (xp .asarray (special .logsumexp (2 * logxmu , axis = 0 )))
871
- - math .log (logx .shape [0 ]))
872
- return res
872
+ return (
873
+ xp .real (xp .asarray (special .logsumexp (2 * logxmu , axis = axis )))
874
+ - math .log (logx .shape [axis ])
875
+ )
873
876
874
877
875
- def boxcox_llf (lmb , data ):
878
+ def boxcox_llf (lmb , data , * , axis = 0 , keepdims = False , nan_policy = 'propagate' ):
876
879
r"""The boxcox log-likelihood function.
877
880
878
881
Parameters
@@ -883,6 +886,26 @@ def boxcox_llf(lmb, data):
883
886
Data to calculate Box-Cox log-likelihood for. If `data` is
884
887
multi-dimensional, the log-likelihood is calculated along the first
885
888
axis.
889
+ axis : int, default: 0
890
+ If an int, the axis of the input along which to compute the statistic.
891
+ The statistic of each axis-slice (e.g. row) of the input will appear in a
892
+ corresponding element of the output.
893
+ If ``None``, the input will be raveled before computing the statistic.
894
+ nan_policy : {'propagate', 'omit', 'raise'
895
+ Defines how to handle input NaNs.
896
+
897
+ - ``propagate``: if a NaN is present in the axis slice (e.g. row) along
898
+ which the statistic is computed, the corresponding entry of the output
899
+ will be NaN.
900
+ - ``omit``: NaNs will be omitted when performing the calculation.
901
+ If insufficient data remains in the axis slice along which the
902
+ statistic is computed, the corresponding entry of the output will be
903
+ NaN.
904
+ - ``raise``: if a NaN is present, a ``ValueError`` will be raised.
905
+ keepdims : bool, default: False
906
+ If this is set to True, the axes which are reduced are left
907
+ in the result as dimensions with size one. With this option,
908
+ the result will broadcast correctly against the input array.
886
909
887
910
Returns
888
911
-------
@@ -955,28 +978,39 @@ def boxcox_llf(lmb, data):
955
978
>>> plt.show()
956
979
957
980
"""
981
+ # _axis_nan_policy decorator does not currently support these for non-NumPy arrays
982
+ kwargs = {}
983
+ if keepdims is not False :
984
+ kwargs [keepdims ] = keepdims
985
+ if nan_policy != 'propagate' :
986
+ kwargs [nan_policy ] = nan_policy
987
+ return _boxcox_llf (data , lmb = lmb , axis = axis , ** kwargs )
988
+
989
+
990
+ @_axis_nan_policy_factory (lambda x : x , n_outputs = 1 , default_axis = 0 ,
991
+ result_to_tuple = lambda x : (x ,))
992
+ def _boxcox_llf (data , axis = 0 , * , lmb ):
958
993
xp = array_namespace (data )
959
994
data = xp_promote (data , force_floating = True , xp = xp )
960
-
961
- N = data .shape [0 ]
995
+ N = data .shape [axis ]
962
996
if N == 0 :
963
- return xp . nan
997
+ return _get_nan ( data , xp = xp )
964
998
965
999
logdata = xp .log (data )
966
1000
967
1001
# Compute the variance of the transformed data.
968
1002
if lmb == 0 :
969
- logvar = xp .log (xp .var (logdata , axis = 0 ))
1003
+ logvar = xp .log (xp .var (logdata , axis = axis ))
970
1004
else :
971
1005
# Transform without the constant offset 1/lmb. The offset does
972
1006
# not affect the variance, and the subtraction of the offset can
973
1007
# lead to loss of precision.
974
1008
# Division by lmb can be factored out to enhance numerical stability.
975
1009
logx = lmb * logdata
976
- logvar = _log_var (logx , xp ) - 2 * math .log (abs (lmb ))
1010
+ logvar = _log_var (logx , xp , axis ) - 2 * math .log (abs (lmb ))
977
1011
978
- res = (lmb - 1 ) * xp .sum (logdata , axis = 0 ) - N / 2 * logvar
979
- res = xp .astype (res , data .dtype , copy = False )
1012
+ res = (lmb - 1 ) * xp .sum (logdata , axis = axis ) - N / 2 * logvar
1013
+ res = xp .astype (res , data .dtype )
980
1014
res = res [()] if res .ndim == 0 else res
981
1015
return res
982
1016
@@ -1081,7 +1115,7 @@ def boxcox(x, lmbda=None, alpha=None, optimizer=None):
1081
1115
Notes
1082
1116
-----
1083
1117
The Box-Cox transform is given by:
1084
-
1118
+
1085
1119
.. math::
1086
1120
1087
1121
y =
0 commit comments