001 /* 002 // $Id: //open/mondrian/src/main/mondrian/udf/InverseNormalUdf.java#4 $ 003 // This software is subject to the terms of the Common Public License 004 // Agreement, available at the following URL: 005 // http://www.opensource.org/licenses/cpl.html. 006 // Copyright (C) 2005-2008 Julian Hyde 007 // All Rights Reserved. 008 // You must accept the terms of that agreement to use this software. 009 */ 010 package mondrian.udf; 011 012 import org.apache.commons.math.MathException; 013 import org.apache.commons.math.distribution.DistributionFactory; 014 import org.apache.commons.math.distribution.NormalDistribution; 015 import org.apache.log4j.Logger; 016 017 import mondrian.olap.Evaluator; 018 import mondrian.olap.Syntax; 019 import mondrian.olap.fun.MondrianEvaluationException; 020 import mondrian.olap.type.NumericType; 021 import mondrian.olap.type.Type; 022 import mondrian.spi.UserDefinedFunction; 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 */ 044 public class InverseNormalUdf implements UserDefinedFunction { 045 private static final Logger LOGGER = Logger.getLogger(InverseNormalUdf.class); 046 047 private static DistributionFactory distributionFactory = DistributionFactory.newInstance(); 048 private static NormalDistribution nd = distributionFactory.createNormalDistribution(); 049 050 public String getName() { 051 return "InverseNormal"; 052 } 053 054 public String getDescription() { 055 return "Returns inverse normal distribution of its argument"; 056 } 057 058 public Syntax getSyntax() { 059 return Syntax.Function; 060 } 061 062 public Type getReturnType(Type[] types) { 063 return new NumericType(); 064 } 065 066 public Type[] getParameterTypes() { 067 return new Type[] {new NumericType()}; 068 } 069 070 public Object execute(Evaluator evaluator, Argument[] args) { 071 final Object argValue = args[0].evaluateScalar(evaluator); 072 LOGGER.debug("Inverse Normal argument was : " + argValue); 073 if (!(argValue instanceof Number)) { 074 // Argument might be a RuntimeException indicating that 075 // the cache does not yet have the required cell value. The 076 // function will be called again when the cache is loaded. 077 return null; 078 } 079 080 final Double d = new Double(((Number) argValue).doubleValue()); 081 LOGGER.debug("Inverse Normal argument as Double was : " + d); 082 083 if (d.isNaN()) { 084 return null; 085 } 086 /* 087 If probability is nonnumeric or 088 probability < 0 or 089 probability > 1, 090 returns an error. 091 */ 092 double dbl = d.doubleValue(); 093 if (dbl < 0.0 || dbl > 1.0) { 094 LOGGER.debug("Invalid value for inverse normal distribution: " + dbl); 095 throw new MondrianEvaluationException("Invalid value for inverse normal distribution: " + dbl); 096 } 097 try { 098 Double result = new Double(nd.inverseCumulativeProbability(dbl)); 099 LOGGER.debug("Inverse Normal result : " + result.doubleValue()); 100 return result; 101 } catch (MathException e) { 102 LOGGER.debug("Exception calculating inverse normal distribution: " + dbl, e); 103 throw new MondrianEvaluationException("Exception calculating inverse normal distribution: " + dbl); 104 } 105 } 106 107 public String[] getReservedWords() { 108 return null; 109 } 110 111 } 112 113 // End InverseNormalUdf.java