/* * 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.density; import com.itemanalysis.jmetrik.dao.DatabaseAccessObject; import com.itemanalysis.jmetrik.dao.DatabaseType; import com.itemanalysis.jmetrik.dao.JmetrikDatabaseFactory; import com.itemanalysis.jmetrik.sql.DataTableName; import com.itemanalysis.jmetrik.sql.VariableTableName; import com.itemanalysis.jmetrik.workspace.JmetrikPreferencesManager; import com.itemanalysis.jmetrik.workspace.VariableChangeEvent; import com.itemanalysis.jmetrik.workspace.VariableChangeListener; import com.itemanalysis.psychometrics.data.VariableAttributes; import com.itemanalysis.psychometrics.distribution.UniformDistributionApproximation; import com.itemanalysis.psychometrics.kernel.*; 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.rank.Max; import org.apache.commons.math3.stat.descriptive.rank.Min; import org.apache.commons.math3.util.ResizableDoubleArray; 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 DensityAnalysis extends SwingWorker<DensityPanel, Void> { private DensityCommand command = null; private DensityPanel densityPanel = null; private Throwable theException = null; private Connection conn = null; private static int KERNEL_POINTS = 500; public StopWatch sw = null; static Logger logger = Logger.getLogger("jmetrik-logger"); static Logger scriptLogger = Logger.getLogger("jmetrik-script-logger"); public boolean hasGroupingVariable = false; private VariableAttributes variable = null; private VariableAttributes groupVar = null; private DataTableName tableName = null; private int progressValue = 0; private int lineNumber = 0; private double maxProgress = 0.0; private DatabaseAccessObject dao = null; private ArrayList<VariableChangeListener> variableChangeListeners = null; public DensityAnalysis( Connection conn, DatabaseAccessObject dao, DensityCommand command, DensityPanel densityPanel){ this.conn = conn; this.dao = dao; this.command = command; this.densityPanel = densityPanel; variableChangeListeners = new ArrayList<VariableChangeListener>(); } private void updateProgress(){ progressValue=(int)((100*((double)lineNumber+1.0))/ maxProgress); setProgress(Math.max(0,Math.min(100,progressValue))); lineNumber++; } public XYSeriesCollection summarize()throws SQLException, IllegalArgumentException{ Statement stmt = null; ResultSet rs=null; TreeMap<String, ResizableDoubleArray> data = new TreeMap<String, ResizableDoubleArray>(); //set progress bar information int nrow = 0; JmetrikPreferencesManager pref = new JmetrikPreferencesManager(); String dbType = pref.getDatabaseType(); if(DatabaseType.APACHE_DERBY.toString().equals(dbType)){ JmetrikDatabaseFactory dbFactory = new JmetrikDatabaseFactory(DatabaseType.APACHE_DERBY); nrow = dao.getRowCount(conn, tableName); }else{ //add other databases here when functionality is added } maxProgress = (double)nrow; Table sqlTable = new Table(tableName.getNameForDatabase()); SelectQuery select = new SelectQuery(); select.addColumn(sqlTable, variable.getName().nameForDatabase()); if(hasGroupingVariable) select.addColumn(sqlTable, groupVar.getName().nameForDatabase()); stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); rs=stmt.executeQuery(select.toString()); String conditionalName = ""; ResizableDoubleArray cData = null; double value = Double.NaN; while(rs.next()){ if(groupVar!=null){ String groupName = rs.getString(groupVar.getName().nameForDatabase()); if(rs.wasNull()){ groupName = ""; } conditionalName = groupName; }else{ conditionalName = "Series 1"; } cData = data.get(conditionalName); if(cData==null){ cData = new ResizableDoubleArray((int) maxProgress); data.put(conditionalName, cData); } value = rs.getDouble(variable.getName().nameForDatabase()); if(!rs.wasNull()){ cData.addElement(value); } updateProgress(); } rs.close(); stmt.close(); String kType = command.getSelectOneOption("kernel").getSelectedArgument(); double adjustment = command.getFreeOption("adjust").getDouble(); KernelFactory kernelFactory = new KernelFactory(kType); KernelFunction kernelFunction = kernelFactory.getKernelFunction(); Bandwidth bandwidth = null; KernelDensity density = null; UniformDistributionApproximation uniform = null; Min min = new Min(); Max max = new Max(); double[] x = null; this.firePropertyChange("progress-ind-on", null, null); XYSeriesCollection seriesCollection = new XYSeriesCollection(); XYSeries series = null; for(String s : data.keySet()){ series = new XYSeries(s); x = data.get(s).getElements(); bandwidth = new ScottsBandwidth(x, adjustment); uniform = new UniformDistributionApproximation(min.evaluate(x), max.evaluate(x), KERNEL_POINTS); density = new KernelDensity(kernelFunction, bandwidth, uniform); double[] dens = density.evaluate(x); double[] points = density.getPoints(); for(int i=0;i<dens.length;i++){ series.add(points[i], dens[i]); } seriesCollection.addSeries(series); } return seriesCollection; } protected DensityPanel doInBackground(){ sw = new StopWatch(); this.firePropertyChange("status", "", "Running Density Analysis..."); this.firePropertyChange("progress-on", null, null); try{ //get variable info from db tableName = new DataTableName(command.getPairedOptionList("data").getStringAt("table")); String selectVariable = command.getFreeOption("variable").getString(); variable = dao.getVariableAttributes(conn, new VariableTableName(tableName.toString()), selectVariable); if(command.getFreeOption("groupvar").hasValue()){ String groupByName=command.getFreeOption("groupvar").getString(); groupVar = dao.getVariableAttributes(conn, new VariableTableName(tableName.toString()), groupByName); hasGroupingVariable = true; } densityPanel.updateDataset(summarize()); firePropertyChange("status", "", "Done: " + sw.getElapsedTime()); firePropertyChange("progress-off", null, null); //make statusbar progress not visible }catch(Throwable t){ logger.fatal(t.getMessage(), t); theException=t; } return densityPanel; } //=============================================================================================================== //Handle variable changes here //=============================================================================================================== 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){ System.out.println("TestSclingAnalysis: firing variable changed=" + event.getVariable().getName().toString()); for(VariableChangeListener l : variableChangeListeners){ l.variableChanged(event); } } //=============================================================================================================== @Override protected void done(){ try{ if(theException!=null){ logger.fatal(theException.getMessage(), theException); firePropertyChange("error", "", "Error - Check log for details."); } scriptLogger.info(command.paste()); }catch(Exception ex){ logger.fatal(theException.getMessage(), theException); firePropertyChange("error", "", "Error - Check log for details."); } } }