001/* 002// This software is subject to the terms of the Eclipse Public License v1.0 003// Agreement, available at the following URL: 004// http://www.eclipse.org/legal/epl-v10.html. 005// You must accept the terms of that agreement to use this software. 006// 007// Copyright (C) 2005-2013 Pentaho 008// All Rights Reserved. 009*/ 010package mondrian.udf; 011 012import mondrian.olap.Evaluator; 013import mondrian.olap.Syntax; 014import mondrian.olap.fun.MondrianEvaluationException; 015import mondrian.olap.type.NumericType; 016import mondrian.olap.type.Type; 017import mondrian.spi.UserDefinedFunction; 018 019import org.apache.commons.math.MathException; 020import org.apache.commons.math.distribution.NormalDistribution; 021import org.apache.commons.math.distribution.NormalDistributionImpl; 022import org.apache.log4j.Logger; 023 024 025/** 026 * A user-defined function which returns the inverse normal distribution value 027 * of its argument. 028 * 029 * <p>This particular function is useful in Six Sigma calculations, for 030 * example, 031 * 032 * <blockquote><code><pre> 033 * WITH MEMBER [Measures].[Yield] 034 * AS '([Measures].[Number of Failures] / [Measures].[Population])', 035 * FORMAT_STRING = "0.00%" 036 * MEMBER [Measures].[Sigma] 037 * AS 'IIf([Measures].[Yield] <> 0, 038 * IIf([Measures].[Yield] > 0.5, 039 * 0, 040 * InverseNormal(1 - ([Measures].[Yield])) + 1.5), 6)', 041 * FORMAT_STRING = "0.0000" 042 * </pre></code></blockquote> 043 */ 044public class InverseNormalUdf implements UserDefinedFunction { 045 private static final Logger LOGGER = 046 Logger.getLogger(InverseNormalUdf.class); 047 048 049 // the zero arg constructor sets the mean equal to zero and standard 050 // deviation equal to one 051 private static final NormalDistribution nd = new NormalDistributionImpl(); 052 053 public String getName() { 054 return "InverseNormal"; 055 } 056 057 public String getDescription() { 058 return "Returns inverse normal distribution of its argument"; 059 } 060 061 public Syntax getSyntax() { 062 return Syntax.Function; 063 } 064 065 public Type getReturnType(Type[] types) { 066 return new NumericType(); 067 } 068 069 public Type[] getParameterTypes() { 070 return new Type[] {new NumericType()}; 071 } 072 073 public Object execute(Evaluator evaluator, Argument[] args) { 074 final Object argValue = args[0].evaluateScalar(evaluator); 075 LOGGER.debug("Inverse Normal argument was : " + argValue); 076 if (!(argValue instanceof Number)) { 077 // Argument might be a RuntimeException indicating that 078 // the cache does not yet have the required cell value. The 079 // function will be called again when the cache is loaded. 080 return null; 081 } 082 083 final Double d = new Double(((Number) argValue).doubleValue()); 084 LOGGER.debug("Inverse Normal argument as Double was : " + d); 085 086 if (d.isNaN()) { 087 return null; 088 } 089 090 // If probability is nonnumeric or 091 // probability < 0 or 092 // probability > 1, 093 // returns an error. 094 double dbl = d.doubleValue(); 095 if (dbl < 0.0 || dbl > 1.0) { 096 LOGGER.debug( 097 "Invalid value for inverse normal distribution: " + dbl); 098 throw new MondrianEvaluationException( 099 "Invalid value for inverse normal distribution: " + dbl); 100 } 101 try { 102 Double result = new Double(nd.inverseCumulativeProbability(dbl)); 103 LOGGER.debug("Inverse Normal result : " + result.doubleValue()); 104 return result; 105 } catch (MathException e) { 106 LOGGER.debug( 107 "Exception calculating inverse normal distribution: " + dbl, e); 108 throw new MondrianEvaluationException( 109 "Exception calculating inverse normal distribution: " + dbl); 110 } 111 } 112 113 public String[] getReservedWords() { 114 return null; 115 } 116 117} 118 119// End InverseNormalUdf.java