[JAVA] Create advanced functions for Yellowfin

Purpose

In addition to the functions originally provided in Yellowfin (BI tool), there are "advanced functions" that you can create and extend by yourself. I haven't used it before, and I haven't touched it because I have to write a program, but there was a request to find the geometric mean, and I wrote an article about how I made it. Personally, honestly, it was difficult to understand just by using the official Wiki, so follow that. I hope I can do it.

Introduction

The basic way to start development is to use Eclipse, but Basics of advanced functions If you set while looking at it, the required method will be automatically overridden from AnalyticalFunction and added, so you can set the parameters in it and describe the necessary processing in the applyAnalyticFunction method.

If it is a function that calculates the sum of one column (cumulative sum), you can find it by looking at Create advanced function. I think. In this example, you just add one record at a time from the selected column. As a result, the return value of applyAnalyticFunction is returned to the report.

Excerpt from the official wiki ↓

simpleAdvancedFunction.java


import com.hof.mi.interfaces.AnalyticalFunction;
   
public class AccumulativeTotal
    extends AnalyticalFunction{
   
    private Double total = 0.0;
    public String getName()
    {
        return "Accumulative Total";
    }
    public String getDescription()
    {
        return "Calculates the accumulative total for the selected field";
    }
   
    public String getCategory()
    {
        return "Analysis";
    }
    public String getColumnHeading(String colName)
    {
        return "Accumulative Total of " + colName;
    }
    public int getReturnType()
    {
            return TYPE_NUMERIC;
    }
    public boolean acceptsNativeType(int type)
    {
        return type == TYPE_NUMERIC;
    }
    public Object applyAnalyticFunction(int index, Object value) throws Exception
    {
            if (value == null) return null;
            this.total += Double.valueOf(value.toString());
            return this.total;
    }
}

For constants like TYPE_NUMERIC, see Advanced Functions Appendix. Either an Integer value or a constant is fine.

Processing of numbers for the entire column (eg geometric mean)

image.png

I didn't mention this in detail, so I tried it by groping, but in Creating advanced functions Use the well-written preAnalyticFunction method. Used to perform operations across the dataset. It is written, but it is difficult to imagine because the sample is not written. Only the part of the code added / changed earlier is described.

As an explanation, since the object array of column values is stored for the record in the argument selectedCol of preAnalyticFunction, assign it to this.total only at the beginning and then multiply by incrementing with cnt how many times the process was repeated. I will. Finally, the number of repeated processing in Math.pow is simply returned (displayed) as it is in applyAnalyticFunction.

geometricMean.java


    private int cnt = 0;//add
	public Object applyAnalyticFunction(int index, Object value) throws Exception {//modify method
		// TODO Auto-generated method stub
		if (value == null) return null;
        return this.total;
	}

	public void preAnalyticFunction(Object[] selectedCol){//add method
	    this.total=0.0;
	    for (Object value : selectedCol) {
	        if (value!=null) {
	    		if(this.total==0.0){
	    			this.total = Double.valueOf(value.toString());
	    		} else {
	    			this.total= this.total * Double.valueOf(value.toString());
	    		}
	    		cnt++;
	    	}
	    }
	    this.total = Math.pow( this.total, (double)1/cnt);
	}

	public Object applyAnalyticFunction(int index, Object value) throws Exception {//modify
		// TODO Auto-generated method stub
		if (value == null) return null;
        return this.total;
	}

When you want to compare other columns with the selected column ・ When you want to process including other columns (eg covariance)

image.png

In addition to columns that use advanced functions, it seems that it is possible to let the user specify a column and execute the function when you want to influence / compare any column. This was also difficult to understand on the wiki, but getParameterValue by adding it to the parameter through the addParameter method of setupParameters in Parameter setup. You can get the set parameter with ("Unique key"). At this time, you can select any column by setting the parameter setDataType to TYPE_FIELD (100). image.png

This is a code explanation, but this also only describes the changes from the code of the first wiki. Get the object array of the value (selectedCol) to which the advanced function is applied by preAnalyticFunction and the object array of the column value (inputColumn) of the unique key "FIELD_SELECTION" explained earlier by getParameterValue (element 1 in the above image). Cast each to a Double type and add it to the array. So we pass these two arrays to covariance to get the covariance value. As for the covariance and sum methods, we will omit the explanation because they only calculate the covariance and sum.

covariance.java


import java.util.ArrayList;//add
import java.util.List;//add

	private List<Double> items1 = new ArrayList<>();//add
	private List<Double> items2 = new ArrayList<>();//add

	protected void setupParameters() {//add
	   Parameter p = new Parameter();
	   p.setUniqueKey("FIELD_SELECTION");
	   p.setDisplayName("Column");
	   p.setDescription("Compare this numeric field to the selected field");
	   p.setDataType(TYPE_FIELD);//100
	   p.setAcceptsFieldType(TYPE_NUMERIC, true);
	   p.setDisplayType(DISPLAY_SELECT);//6
	   addParameter(p);
	}

	public void preAnalyticFunction(Object[] selectedCol){//add
		this.total=0.0;
		Object [] inputColumn = (Object[]) getParameterValue("FIELD_SELECTION");
	      for (Object value : selectedCol) {
	    	items1.add(Double.valueOf(value.toString()));
	      }
	      for (Object value : inputColumn) {
	    	  items2.add(Double.valueOf(value.toString()));
	      }
	      Double r = covariance(items1, items2);
	      this.total = r;
	}

	public Object applyAnalyticFunction(int index, Object value) throws Exception {//modify
		// TODO Auto-generated method stub
		if (value == null) return null;
        return this.total;
	}

	public Double covariance(final List<Double> items1, final List<Double> items2) {//add
		List<Double> items3 = new ArrayList<>();
		int n = items1.size();
		Double i1Mean = sum(items1)/n;
		Double i2Mean = sum(items2)/n;
		for (int i = 0; i < n; i++) {
			items3.add((i1Mean - items1.get(i)) * (i2Mean - items2.get(i)));
		}
		Double i3Sum = sum(items3);
		return i3Sum / n;
	}

	public Double sum(final List<Double> items) {//add
		Double result = 0.0;
		for (Double item : items) {
			result += item;
		}
		return result;
	}

So

It's been a bit long to explain, but by creating an advanced function, it's a little annoying to express it in a report (multiple reportFromReport etc.) so that you can get a value as soon as you can write it in Java. It takes a lot of coding time, but you will be able to easily perform processing such as applying a certain formula that is always used in the company and finding the relationship between two columns.

Recommended Posts

Create advanced functions for Yellowfin
[Android] Create validation for date input!
Create your own encode for String.getBytes ()
Create a fluentd server for testing