Java tutorial
/* * Copyright (c) 2012 Patrick Meyer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.itemanalysis.jmetrik.graph.nicc; import com.itemanalysis.jmetrik.dao.DatabaseAccessObject; import com.itemanalysis.jmetrik.sql.DataTableName; import com.itemanalysis.jmetrik.sql.VariableTableName; import com.itemanalysis.jmetrik.workspace.VariableChangeEvent; import com.itemanalysis.jmetrik.workspace.VariableChangeListener; import com.itemanalysis.psychometrics.data.DataType; import com.itemanalysis.psychometrics.data.VariableAttributes; import com.itemanalysis.psychometrics.distribution.UniformDistributionApproximation; import com.itemanalysis.psychometrics.kernel.*; import com.itemanalysis.psychometrics.measurement.KernelRegressionCategories; import com.itemanalysis.psychometrics.measurement.KernelRegressionItem; import com.itemanalysis.psychometrics.tools.StopWatch; import com.itemanalysis.squiggle.base.SelectQuery; import com.itemanalysis.squiggle.base.Table; import org.apache.commons.math3.stat.descriptive.moment.Mean; import org.apache.commons.math3.stat.descriptive.moment.StandardDeviation; import org.apache.commons.math3.stat.descriptive.rank.Max; import org.apache.commons.math3.stat.descriptive.rank.Min; import org.apache.log4j.Logger; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import javax.swing.*; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.TreeMap; public class NonparametricCurveAnalysis extends SwingWorker<String, Void> { private Connection conn = null; private DatabaseAccessObject dao = null; private NonparametricCurveCommand command = null; private NonparametricCurvePanel nonparametricPanel = null; private TreeMap<VariableAttributes, KernelRegressionItem> focalRegression = null; private TreeMap<VariableAttributes, KernelRegressionItem> referenceRegression = null; private TreeMap<VariableAttributes, KernelRegressionCategories> categoryRegression = null; private TreeMap<VariableAttributes, KernelRegressionItem> kernelRegression = null; private ArrayList<VariableChangeListener> variableChangeListeners = null; private TreeMap<VariableAttributes, XYSeriesCollection> xySeriesMap = null; static Logger logger = Logger.getLogger("jmetrik-logger"); static Logger scriptLogger = Logger.getLogger("jmetrik-script-logger"); private VariableAttributes regressorVariable = null; private VariableAttributes groupByVariable = null; private boolean hasGroupVariable = false; private ArrayList<VariableAttributes> variables = null; private double maxProgress = 100.0; private double sampleSize = 0.0; private DataTableName tableName = null; private VariableTableName variableTableName = null; private int progressValue = 0; private int lineNumber = 0; private Throwable theException = null; private StopWatch sw = null; private String focalCode = ""; private String referenceCode = ""; private String savePath = ""; private boolean savePlots = false; private int gridPoints = 51; boolean allCategories = false; private UniformDistributionApproximation uniformDistributionApproximation = null; private double bwAdjustment = 1.0; private NonparametricIccBandwidth bandwidth = null; private KernelFunction kernelFunction = null; public NonparametricCurveAnalysis(Connection conn, DatabaseAccessObject dao, NonparametricCurveCommand command, NonparametricCurvePanel nonparametricPanel) { this.conn = conn; this.dao = dao; this.command = command; this.nonparametricPanel = nonparametricPanel; variableChangeListeners = new ArrayList<VariableChangeListener>(); } private void initializeProgressBar() throws SQLException { sampleSize = dao.getRowCount(conn, tableName); maxProgress = (double) sampleSize; maxProgress *= 2.0; } private void updateProgress() { progressValue = (int) ((100 * ((double) lineNumber + 1.0)) / maxProgress); setProgress(Math.max(0, Math.min(100, progressValue))); lineNumber++; } private void initialize() throws SQLException, IllegalArgumentException { String db = command.getPairedOptionList("data").getStringAt("db"); String table = command.getPairedOptionList("data").getStringAt("table"); tableName = new DataTableName(table); variableTableName = new VariableTableName(table); String xvar = command.getFreeOption("xvar").getString(); regressorVariable = dao.getVariableAttributes(conn, variableTableName, xvar); ArrayList<String> selectedVariables = command.getFreeOptionList("variables").getString(); variables = dao.getSelectedVariables(conn, variableTableName, selectedVariables); allCategories = command.getSelectOneOption("curves").isValueSelected("all"); savePlots = command.getFreeOption("output").hasValue(); if (savePlots) savePath = command.getFreeOption("output").getString(); if (command.getFreeOption("groupvar").hasValue()) { String gvar = command.getFreeOption("groupvar").getString(); groupByVariable = dao.getVariableAttributes(conn, variableTableName, gvar); hasGroupVariable = true; focalCode = command.getPairedOptionList("codes").getStringAt("focal"); referenceCode = command.getPairedOptionList("codes").getStringAt("reference"); allCategories = false; //convert focal and reference codes to double format is they are numbers double focDouble = 0; double refDouble = 0; try { focDouble = Double.parseDouble(focalCode); focalCode = Double.valueOf(focDouble).toString(); } catch (NumberFormatException ex) { //data is string } try { refDouble = Double.parseDouble(referenceCode); referenceCode = Double.valueOf(refDouble).toString(); } catch (NumberFormatException ex) { //data is string } } //initialize progress and compute sample size initializeProgressBar(); //bandwidth adjustment factor bwAdjustment = command.getFreeOption("adjust").getDouble(); //kernel function properties String kernelTypeString = command.getSelectOneOption("kernel").getSelectedArgument(); KernelFactory kernelFactory = new KernelFactory(kernelTypeString); kernelFunction = kernelFactory.getKernelFunction(); initializeGridPoints(); } private void initializeGridPoints() throws SQLException { Statement stmt = null; ResultSet rs = null; //connect to db try { Table sqlTable = new Table(tableName.getNameForDatabase()); SelectQuery select = new SelectQuery(); select.addColumn(sqlTable, regressorVariable.getName().nameForDatabase()); stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); rs = stmt.executeQuery(select.toString()); Min min = new Min(); Max max = new Max(); Mean mean = new Mean(); StandardDeviation sd = new StandardDeviation(); double value = 0.0; while (rs.next()) { value = rs.getDouble(regressorVariable.getName().nameForDatabase()); if (!rs.wasNull()) { min.increment(value); max.increment(value); mean.increment(value); sd.increment(value); } updateProgress(); } rs.close(); stmt.close(); //evaluation points double sdv = sd.getResult(); double mn = mean.getResult(); double lower = mn - 2.5 * sdv; double upper = mn + 2.5 * sdv; bwAdjustment *= sdv; bandwidth = new NonparametricIccBandwidth(sampleSize, bwAdjustment); gridPoints = command.getFreeOption("gridpoints").getInteger(); // uniformDistributionApproximation = new UniformDistributionApproximation( // min.getResult(), max.getResult(), gridPoints); uniformDistributionApproximation = new UniformDistributionApproximation(lower, upper, gridPoints); } catch (SQLException ex) { throw ex; } finally { if (rs != null) rs.close(); if (stmt != null) stmt.close(); } } public void evaluateDIF() throws SQLException { Statement stmt = null; ResultSet rs = null; //create focal map focalRegression = new TreeMap<VariableAttributes, KernelRegressionItem>(); for (VariableAttributes v : variables) { KernelRegressionItem kItem = new KernelRegressionItem(v, kernelFunction, bandwidth, uniformDistributionApproximation); focalRegression.put(v, kItem); } //create reference map if (hasGroupVariable) { referenceRegression = new TreeMap<VariableAttributes, KernelRegressionItem>(); for (VariableAttributes v : variables) { KernelRegressionItem kItem = new KernelRegressionItem(v, kernelFunction, bandwidth, uniformDistributionApproximation); referenceRegression.put(v, kItem); } } //determine whether group variable is double or not boolean groupVariableIsDouble = false; if (groupByVariable.getType().getDataType() == DataType.DOUBLE) groupVariableIsDouble = true; try { //connect to db Table sqlTable = new Table(tableName.getNameForDatabase()); SelectQuery select = new SelectQuery(); for (VariableAttributes v : variables) { select.addColumn(sqlTable, v.getName().nameForDatabase()); } select.addColumn(sqlTable, regressorVariable.getName().nameForDatabase()); select.addColumn(sqlTable, groupByVariable.getName().nameForDatabase()); stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); rs = stmt.executeQuery(select.toString()); KernelRegressionItem kernelRegressionItem; Object itemResponse; Double score; Object tempGroup; String group; //analyze by groups while (rs.next()) { tempGroup = rs.getObject(groupByVariable.getName().nameForDatabase()); if (tempGroup == null) { group = "";//will not be counted if does not match focal or reference code } else { if (groupVariableIsDouble) { group = Double.valueOf((Double) tempGroup).toString(); } else { group = ((String) tempGroup).trim(); } } //get independent variable value //omit examinees with missing data //examinees with missing group code omitted score = rs.getDouble(regressorVariable.getName().nameForDatabase()); if (!rs.wasNull()) { if (focalCode.equals(group)) { for (VariableAttributes v : focalRegression.keySet()) { kernelRegressionItem = focalRegression.get(v); itemResponse = rs.getObject(v.getName().nameForDatabase()); if (itemResponse != null) kernelRegressionItem.increment(score, itemResponse); } } else if (referenceCode.equals(group)) { for (VariableAttributes v : referenceRegression.keySet()) { kernelRegressionItem = referenceRegression.get(v); itemResponse = rs.getObject(v.getName().nameForDatabase()); if (itemResponse != null) kernelRegressionItem.increment(score, itemResponse); } } } updateProgress(); } } catch (SQLException ex) { throw ex; } finally { if (rs != null) rs.close(); if (stmt != null) stmt.close(); } this.firePropertyChange("progress-ind-on", null, null); } public void evaluateAll() throws SQLException { categoryRegression = new TreeMap<VariableAttributes, KernelRegressionCategories>(); for (VariableAttributes v : variables) { KernelRegressionCategories kCat = new KernelRegressionCategories(v, kernelFunction, bandwidth, uniformDistributionApproximation); categoryRegression.put(v, kCat); } //connect to db Table sqlTable = new Table(tableName.getNameForDatabase()); SelectQuery select = new SelectQuery(); for (VariableAttributes v : variables) { select.addColumn(sqlTable, v.getName().nameForDatabase()); } select.addColumn(sqlTable, regressorVariable.getName().nameForDatabase()); if (hasGroupVariable) select.addColumn(sqlTable, groupByVariable.getName().nameForDatabase()); ResultSet rs = null; Statement stmt = null; try { stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); rs = stmt.executeQuery(select.toString()); KernelRegressionCategories kernelRegressionCategories; Object itemResponse; Double score; Object tempGroup; String group; while (rs.next()) { //increment kernel regression objects //omit examinees with missing data score = rs.getDouble(regressorVariable.getName().nameForDatabase()); if (!rs.wasNull()) { for (VariableAttributes v : categoryRegression.keySet()) { kernelRegressionCategories = categoryRegression.get(v); itemResponse = rs.getObject(v.getName().nameForDatabase()); if (itemResponse != null) kernelRegressionCategories.increment(score, itemResponse); } } updateProgress(); } } catch (SQLException ex) { throw ex; } finally { if (rs != null) rs.close(); if (stmt != null) stmt.close(); } this.firePropertyChange("progress-ind-on", null, null); } public void evaluate() throws SQLException { kernelRegression = new TreeMap<VariableAttributes, KernelRegressionItem>(); for (VariableAttributes v : variables) { KernelRegressionItem kItem = new KernelRegressionItem(v, kernelFunction, bandwidth, uniformDistributionApproximation); kernelRegression.put(v, kItem); } ResultSet rs = null; Statement stmt = null; try { //connect to db Table sqlTable = new Table(tableName.getNameForDatabase()); SelectQuery select = new SelectQuery(); for (VariableAttributes v : variables) { select.addColumn(sqlTable, v.getName().nameForDatabase()); } select.addColumn(sqlTable, regressorVariable.getName().nameForDatabase()); if (hasGroupVariable) select.addColumn(sqlTable, groupByVariable.getName().nameForDatabase()); stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); rs = stmt.executeQuery(select.toString()); KernelRegressionItem kernelRegressionItem; Object itemResponse; Double score; Object tempGroup; String group; while (rs.next()) { //increment kernel regression objects //omit examinees with missing data score = rs.getDouble(regressorVariable.getName().nameForDatabase()); if (!rs.wasNull()) { for (VariableAttributes v : kernelRegression.keySet()) { kernelRegressionItem = kernelRegression.get(v); itemResponse = rs.getObject(v.getName().nameForDatabase()); if (itemResponse != null) kernelRegressionItem.increment(score, itemResponse); } } updateProgress(); } } catch (SQLException ex) { throw ex; } finally { if (rs != null) rs.close(); if (stmt != null) stmt.close(); } this.firePropertyChange("progress-ind-on", null, null); } /** * Called from done to run on EDT * * @throws IllegalArgumentException */ private void publishDIFSeries() throws IllegalArgumentException { double[] focalTcc = new double[gridPoints]; double[] referenceTcc = new double[gridPoints]; double[] valueF, valueR; double[] points = uniformDistributionApproximation.getPoints(); double tccMax = 0; double tccMin = 0; for (VariableAttributes v : focalRegression.keySet()) { KernelRegressionItem kItemF, kItemR; XYSeries seriesF; XYSeries seriesR; //add lines to this collection repeat for each item XYSeriesCollection xyCollection = new XYSeriesCollection(); //increment TCC for focal group, also create expected value series seriesF = new XYSeries("Focal Group"); //add line for reference group seriesR = new XYSeries("Reference Group"); kItemF = focalRegression.get(v); valueF = kItemF.getExpectedValues(); tccMin += kItemF.getMinimumPossibleScore(); tccMax += kItemF.getMaximumPossibleScore(); kItemR = referenceRegression.get(v); valueR = kItemR.getExpectedValues(); for (int i = 0; i < focalTcc.length; i++) { //increment focal group focalTcc[i] += valueF[i]; seriesF.add(points[i], valueF[i]); //increment reference group referenceTcc[i] += valueR[i]; seriesR.add(points[i], valueR[i]); } xyCollection.addSeries(seriesF); xyCollection.addSeries(seriesR); if (allCategories) { nonparametricPanel.updateDatasetFor(v.getName().toString(), 0, 1, xyCollection); } else { nonparametricPanel.updateDatasetFor(v.getName().toString(), kItemF.getMinimumPossibleScore(), kItemF.getMaximumPossibleScore(), xyCollection); } } //end loop over items //add series for test characteristic curve XYSeriesCollection xyCollection = new XYSeriesCollection(); XYSeries tccSeries1 = new XYSeries("Focal TCC"); XYSeries tccSeries2 = new XYSeries("Reference TCC"); for (int i = 0; i < focalTcc.length; i++) { tccSeries1.add(points[i], focalTcc[i]); tccSeries2.add(points[i], referenceTcc[i]); } xyCollection.addSeries(tccSeries1); xyCollection.addSeries(tccSeries2); nonparametricPanel.updateDatasetFor("tcc", tccMin, tccMax, xyCollection); } /** * Called from done on EDT * * @throws IllegalArgumentException */ private void publishAllSeries() throws IllegalArgumentException { double[] tcc = new double[gridPoints]; double[] values; double[] points = uniformDistributionApproximation.getPoints(); double tccMin = 0; double tccMax = 0; for (VariableAttributes v : categoryRegression.keySet()) { KernelRegressionCategories kCategories; XYSeries series; //add lines to this collection repeat for each item XYSeriesCollection xyCollection = new XYSeriesCollection(); //increment TCC for focal group, also create expected value series series = new XYSeries(""); kCategories = categoryRegression.get(v); tccMin += kCategories.getMinimumPossibleScore(); tccMax += kCategories.getMaximumPossibleScore(); values = kCategories.getExpectedValues(); for (int i = 0; i < tcc.length; i++) { tcc[i] += values[i]; series.add(points[i], values[i]); } XYSeries catSeries; //add line for every category TreeMap<Object, KernelRegression> kregMap = kCategories.getRegressionMap(); for (Object o : kregMap.keySet()) { catSeries = new XYSeries(o.toString() + "(" + kCategories.getScoreValue(o) + ")"); values = kregMap.get(o).value(); for (int i = 0; i < points.length; i++) catSeries.add(points[i], values[i]); xyCollection.addSeries(catSeries); } if (allCategories) { nonparametricPanel.updateDatasetFor(v.getName().toString(), 0, 1, xyCollection); } else { nonparametricPanel.updateDatasetFor(v.getName().toString(), kCategories.getMinimumPossibleScore(), kCategories.getMaximumPossibleScore(), xyCollection); } } //end loop over items //add series for test characteristic curve XYSeriesCollection xyCollection = new XYSeriesCollection(); XYSeries tccSeries1 = new XYSeries("TCC"); for (int i = 0; i < tcc.length; i++) { tccSeries1.add(points[i], tcc[i]); } xyCollection.addSeries(tccSeries1); nonparametricPanel.updateDatasetFor("tcc", tccMin, tccMax, xyCollection); } /** * Called from done so that is run from EDT * * @throws IllegalArgumentException */ private void publishSeries() throws IllegalArgumentException { double[] tcc = new double[uniformDistributionApproximation.getNumberOfPoints()]; double[] values; double[] points = uniformDistributionApproximation.getPoints(); XYSeriesCollection collection = null; double tccMax = 0; double tccMin = 0; for (VariableAttributes v : kernelRegression.keySet()) { KernelRegressionItem kItem; XYSeries seriesData = new XYSeries(v.getName().toString()); collection = new XYSeriesCollection(); kItem = kernelRegression.get(v); values = kItem.getExpectedValues(); tccMin += kItem.getMinimumPossibleScore(); tccMax += kItem.getMaximumPossibleScore(); //increment tcc for (int i = 0; i < tcc.length; i++) { seriesData.add(points[i], values[i]); tcc[i] += values[i]; } collection.addSeries(seriesData); nonparametricPanel.updateDatasetFor(v.getName().toString(), kItem.getMinimumPossibleScore(), kItem.getMaximumPossibleScore(), collection); } //end loop over items //add series for test characteristic curve XYSeriesCollection tccCollection = new XYSeriesCollection(); XYSeries seriesData = new XYSeries("TCC"); for (int i = 0; i < tcc.length; i++) { seriesData.add(points[i], tcc[i]); } tccCollection.addSeries(seriesData); nonparametricPanel.updateDatasetFor("tcc", tccMin, tccMax, tccCollection); } @Override public String doInBackground() { sw = new StopWatch(); this.firePropertyChange("status", "", "Running Curves..."); this.firePropertyChange("progress-on", null, null); try { initialize(); if (hasGroupVariable) { evaluateDIF(); publishDIFSeries(); } else if (allCategories) { evaluateAll(); publishAllSeries(); } else { evaluate(); publishSeries(); } if (savePlots) nonparametricPanel.savePlots(savePath); } catch (Throwable t) { logger.fatal(t.getMessage(), t); theException = t; } return "Done"; } @Override protected void done() { try { if (theException != null) { logger.fatal(theException.getMessage(), theException); firePropertyChange("error", "", "Error - Check log for details."); } else { logger.info("NICC bandwidth = " + bandwidth.value() + "\n" + "Bandwidth adjustment factor = " + bandwidth.getAdjustmentFactor()); } scriptLogger.info(command.paste()); firePropertyChange("status", "", "Done: " + sw.getElapsedTime()); firePropertyChange("progress-off", null, null); //make statusbar progress not visible } catch (Exception ex) { logger.fatal(ex.getMessage(), ex); firePropertyChange("error", "", "Error - Check log for details."); firePropertyChange("progress-off", null, null); //make statusbar progress not visible } } //=============================================================================================================== //Handle variable changes here // -Dialogs will use these methods to add their variable listeners //=============================================================================================================== public synchronized void addVariableChangeListener(VariableChangeListener l) { variableChangeListeners.add(l); } public synchronized void removeVariableChangeListener(VariableChangeListener l) { variableChangeListeners.remove(l); } public synchronized void removeAllVariableChangeListeners() { variableChangeListeners.clear(); } public void fireVariableChanged(VariableChangeEvent event) { for (VariableChangeListener l : variableChangeListeners) { l.variableChanged(event); } } }