/* * Copyright (c) 2009 The Jackson Laboratory * * This software was developed by Gary Churchill's Lab at The Jackson * Laboratory (see http://research.jax.org/faculty/churchill). * * This 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 software 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 software. If not, see <http://www.gnu.org/licenses/>. */ package org.jax.qtl.fit; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import org.jax.qtl.cross.Cross; import org.jax.qtl.cross.GeneticMarker; import org.jax.r.RAssignmentCommand; import org.jax.r.RCommand; import org.jax.r.RCommandParameter; import org.jax.r.RMethodInvocationCommand; import org.jax.r.RSyntaxException; import org.jax.r.RUtilities; /** * A convenience class for creating a fitqtl R command. * @author <A HREF="mailto:keith.sheppard@jax.org">Keith Sheppard</A> */ public class FitQtlCommand implements RCommand { private static final String FIT_FUNCTION_NAME = "fitqtl"; /** * our logger */ private static final Logger LOG = Logger.getLogger( FitQtlCommand.class.getName()); private final List<FitPredictor> fitPredictors; private final Cross cross; private volatile String fitResultName = null; private volatile String phenotypeToFit = null; private volatile boolean performDropOneAnalysis = false; private volatile boolean estimateQtlEffects = false; /** * Constructor * @param cross * see {@link #getCross()} * @param fitPredictors * see {@link #getFitPredictors()} */ public FitQtlCommand(Cross cross, List<FitPredictor> fitPredictors) { this.cross = cross; this.fitPredictors = fitPredictors; } /** * Getter for the result name. We use this plus the cross name to come * up with an identifier for the fit results * @return * the result name */ public String getFitResultName() { return this.fitResultName; } /** * Setter for the result name * @param fitResultName * the result name */ public void setFitResultName(String fitResultName) { this.fitResultName = fitResultName; } /** * Getter for the phenotype that the fit function should fit * @return * the phenotype */ public String getPhenotypeToFit() { return this.phenotypeToFit; } /** * Setter for the phenotype the fit function should fit * @param phenotypeToFit * the phenotype that we should fit */ public void setPhenotypeToFit(String phenotypeToFit) { this.phenotypeToFit = phenotypeToFit; } /** * Getter for determining if we should perform "drop-one-term" analysis * @return * true if we should do "drop-one-term" */ public boolean getPerformDropOneAnalysis() { return this.performDropOneAnalysis; } /** * Setter for whether or not we should do drop-one-term * @param performDropOneAnalysis * determines if we should do "drop-one-term" analysis or not */ public void setPerformDropOneAnalysis(boolean performDropOneAnalysis) { this.performDropOneAnalysis = performDropOneAnalysis; } /** * Getter for determining if we should estimate QTL effects or not * @return * true iff we should estimate QTL effects */ public boolean getEstimateQtlEffects() { return this.estimateQtlEffects; } /** * Setter for determining if we should estimate QTL effects or not * @param estimateQtlEffects * if true we should estimate QTL effects */ public void setEstimateQtlEffects(boolean estimateQtlEffects) { this.estimateQtlEffects = estimateQtlEffects; } /** * Get the list of predictors that we're using for the fit function * @return * the predictors */ public List<FitPredictor> getFitPredictors() { return this.fitPredictors; } /** * Get the cross that we're doing a fit for * @return * the cross */ public Cross getCross() { return this.cross; } /** * {@inheritDoc} */ public String getCommandText() { List<RCommandParameter> fitParameters = new ArrayList<RCommandParameter>(); Cross cross = this.cross; if(cross != null) { fitParameters.add(new RCommandParameter( "cross", cross.getAccessorExpressionString())); } String phenotypeToFit = this.phenotypeToFit; if(phenotypeToFit != null) { fitParameters.add(new RCommandParameter( "pheno.col", RUtilities.javaStringToRString(phenotypeToFit))); } List<FitPredictor> fitPredictors = new ArrayList<FitPredictor>(this.fitPredictors); if(!fitPredictors.isEmpty() && cross != null) { List<GeneticMarker> markerList = new ArrayList<GeneticMarker>(); for(FitPredictor fitPredictor: fitPredictors) { for(GeneticMarker interactingMarker: fitPredictor.getInteractingMarkers()) { if(!markerList.contains(interactingMarker)) { markerList.add(interactingMarker); } } } if(!markerList.isEmpty()) { MakeQtlCommand makeQtl = new MakeQtlCommand( cross, markerList); fitParameters.add(new RCommandParameter( "qtl", makeQtl.getCommandText())); } StringBuffer formulaBuffer = new StringBuffer("y~"); for(int i = 0; i < fitPredictors.size(); i++) { FitPredictor currFitPredictor = fitPredictors.get(i); List<GeneticMarker> currInteractingMarkers = currFitPredictor.getInteractingMarkers(); for(int j = 0; j < currInteractingMarkers.size(); j++) { GeneticMarker currInteractingMarker = currInteractingMarkers.get(j); int markerIndex = markerList.indexOf(currInteractingMarker); if(markerIndex == -1) { throw new IndexOutOfBoundsException( "marker index is out of bounds"); } formulaBuffer.append("Q" + (markerIndex + 1)); // add a * for all but the last marker if(j < currInteractingMarkers.size() - 1) { formulaBuffer.append('*'); } } // if we have both interacting markers and interacting // phenotypes, then we need to combine the two with // a '*' List<String> currInteractingPhenos = currFitPredictor.getInteractingPhenotypes(); if((!currInteractingMarkers.isEmpty()) && (!currInteractingPhenos.isEmpty())) { formulaBuffer.append('*'); } for(int j = 0; j < currInteractingPhenos.size(); j++) { String currInteractingPheno = currInteractingPhenos.get(j); formulaBuffer.append(currInteractingPheno); // add a * for all but the last pheno if(j < currInteractingPhenos.size() - 1) { formulaBuffer.append('*'); } } // connect seperate predictors using a '+' if(i < fitPredictors.size() - 1) { formulaBuffer.append('+'); } } fitParameters.add(new RCommandParameter( "formula", formulaBuffer.toString())); } if(cross != null) { fitParameters.add(new RCommandParameter( "covar", cross.getAccessorExpressionString() + "$pheno")); } fitParameters.add(new RCommandParameter( "dropone", RUtilities.javaBooleanToRBoolean( this.getPerformDropOneAnalysis()))); fitParameters.add(new RCommandParameter( "get.ests", RUtilities.javaBooleanToRBoolean( this.getEstimateQtlEffects()))); RMethodInvocationCommand methodInvocationCommand = new RMethodInvocationCommand( FIT_FUNCTION_NAME, fitParameters); String fitResultAccessor = this.getFitResultAccessor(); if(fitResultAccessor == null || fitResultAccessor.length() == 0) { return methodInvocationCommand.getCommandText(); } else { RAssignmentCommand assignmentCommand = new RAssignmentCommand( fitResultAccessor, methodInvocationCommand.getCommandText()); return assignmentCommand.getCommandText(); } } /** * Get the accessor string for the result or null if the result was not * assigned to an R variable * @return * the accessor */ public String getFitResultAccessor() { Cross cross = this.getCross(); String fitResultName = this.getFitResultName(); if(cross != null && fitResultName != null) { try { return cross.getAccessorExpressionString() + "." + RUtilities.fromReadableNameToRIdentifier( fitResultName); } catch(RSyntaxException ex) { LOG.log(Level.FINE, "ignoring fit result name because of bad syntax", ex); return null; } } else { return null; } } }