/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.addthis.hydra.data.query.op; import java.util.List; import com.addthis.basis.util.LessStrings; import com.addthis.bundle.core.Bundle; import com.addthis.bundle.core.BundleField; import com.addthis.bundle.core.list.ListBundle; import com.addthis.bundle.core.list.ListBundleFormat; import com.addthis.bundle.table.DataTable; import com.addthis.bundle.table.DataTableFactory; import com.addthis.bundle.util.BundleColumnBinder; import com.addthis.bundle.value.ValueFactory; import com.addthis.hydra.data.query.AbstractTableOp; import com.addthis.hydra.data.util.ChangePoint; import com.addthis.hydra.data.util.FindChangePoints; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.netty.channel.ChannelProgressivePromise; /** * Detect significant changes within a column of a DataTable. */ public class OpChangePoints extends AbstractTableOp { private Logger log = LoggerFactory.getLogger(OpChangePoints.class); int timeColumn; int valColumn; // Only one column is supported at the moment int minChange; double minRatio; double minZScore; int inactiveThreshold; int windowSize; public OpChangePoints(DataTableFactory factory, String args, ChannelProgressivePromise queryPromise) { super(factory, queryPromise); try { String[] opt = args.split(":"); timeColumn = opt.length >= 1 ? Integer.parseInt(opt[0]) : 0; valColumn = opt.length >= 2 ? Integer.parseInt(opt[1]) : 1; minChange = opt.length >= 3 ? Integer.parseInt(opt[2]) : 10; minRatio = opt.length >= 4 ? Double.parseDouble(opt[3]) : .3; minZScore = opt.length >= 5 ? Double.parseDouble(opt[4]) : 1.5; inactiveThreshold = opt.length >= 6 ? Integer.parseInt(opt[5]) : 1; windowSize = opt.length >= 7 ? Integer.parseInt(opt[6]) : 5; log.info("Initiated changepoints with parameters " + LessStrings.join(new Object[]{valColumn, minChange, minRatio, minZScore, inactiveThreshold}, ",")); } catch (Exception ex) { log.error("", ex); } } /** * Runs FindChangePoints on a column of the data table. * * @param result Input data table * @return A list of significant change points, giving the time, type, and size of each change */ @Override public DataTable tableOp(final DataTable result) { if (result == null || result.size() == 0) { return result; } Long[] data = new Long[result.size()]; BundleField[] fields = new BundleColumnBinder(result.get(0)).getFields(); BundleField timeField = fields[timeColumn]; for (int i = 0; i < result.size(); i++) { data[i] = result.get(i).getValue(fields[valColumn]).asLong().getLong(); } List<ChangePoint> changePoints = FindChangePoints.findSignificantPoints(data, minChange, minRatio, minZScore, inactiveThreshold, windowSize); DataTable table = createTable(changePoints.size()); ListBundleFormat format = new ListBundleFormat(); BundleField outTimeField = format.getField("time"); BundleField outTypeField = format.getField("type"); BundleField outSizeField = format.getField("size"); for (ChangePoint pt : changePoints) { Bundle row = new ListBundle(format); row.setValue(outTimeField, result.get(pt.getIndex()).getValue(timeField)); row.setValue(outTypeField, ValueFactory.create(pt.getType().toString())); row.setValue(outSizeField, ValueFactory.create(pt.getSize())); table.append(row); } return table; } }