Skip to content

Commit

Permalink
Add boxplots for cell-level data
Browse files Browse the repository at this point in the history
  • Loading branch information
arteymix committed Feb 26, 2025
1 parent 25418b0 commit fc6cbe5
Show file tree
Hide file tree
Showing 24 changed files with 935 additions and 112 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -221,20 +221,19 @@ public CellTypeAssignment locateCellTypeAssignment( ExpressionExperiment express
Assert.isTrue( StringUtils.isNotBlank( cta ), "Cell type assignment name must not be blank." );
cta = StringUtils.strip( cta );
try {
Optional<CellTypeAssignment> c = singleCellExpressionExperimentService.getCellTypeAssignment( expressionExperiment, qt, Long.parseLong( cta ) );
if ( c.isPresent() ) {
return c.get();
CellTypeAssignment c = singleCellExpressionExperimentService.getCellTypeAssignment( expressionExperiment, qt, Long.parseLong( cta ) );
if ( c != null ) {
return c;
}
} catch ( NumberFormatException e ) {
// ignore
}
String finalCta = cta;

return singleCellExpressionExperimentService.getCellTypeAssignment( expressionExperiment, qt, cta )
.orElseThrow( () -> {
List<CellTypeAssignment> possibleValues = singleCellExpressionExperimentService.getCellTypeAssignments( expressionExperiment, qt );
return new NullPointerException( "Could not locate any cell type assignment with identifier or name matching " + finalCta + "." + formatPossibleValues( possibleValues, true ) );
} );
return requireNonNull( singleCellExpressionExperimentService.getCellTypeAssignment( expressionExperiment, qt, cta ), () -> {
List<CellTypeAssignment> possibleValues = singleCellExpressionExperimentService.getCellTypeAssignments( expressionExperiment, qt );
return "Could not locate any cell type assignment with identifier or name matching " + finalCta + "." + formatPossibleValues( possibleValues, true );
} );
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import cern.colt.matrix.DoubleMatrix1D;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import ubic.basecode.dataStructure.matrix.DoubleMatrix;
import ubic.basecode.math.MatrixStats;
import ubic.gemma.core.analysis.preprocess.detect.InferredQuantitationMismatchException;
Expand All @@ -30,11 +31,15 @@
import ubic.gemma.core.analysis.preprocess.filter.ExpressionExperimentFilter;
import ubic.gemma.core.datastructure.matrix.ExpressionDataDoubleMatrix;
import ubic.gemma.model.common.quantitationtype.*;
import ubic.gemma.model.expression.bioAssayData.DataVector;
import ubic.gemma.model.expression.biomaterial.BioMaterial;
import ubic.gemma.model.expression.designElement.CompositeSequence;

import javax.annotation.CheckReturnValue;
import java.util.List;
import java.beans.PropertyDescriptor;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;

import static ubic.gemma.core.analysis.preprocess.detect.QuantitationTypeDetectionUtils.detectSuspiciousValues;
Expand Down Expand Up @@ -278,4 +283,50 @@ private static boolean isHeterogeneous( ExpressionDataDoubleMatrix expressionDat
return false;
}

/**
* Convert a collection of vectors.
* @param createQtFunc a function to create a converted {@link QuantitationType}
* @param doToVector a consumer to post-process the created vector (first argument) given the original vector
* (second argument)
* @param vectorType the type of vector to produce
*/
public static <T extends DataVector> Collection<T> convertVectors( Collection<T> vectors, Function<QuantitationType, QuantitationType> createQtFunc, BiConsumer<T, T> doToVector, Class<T> vectorType ) {
ArrayList<T> result = new ArrayList<>( vectors.size() );
Map<QuantitationType, QuantitationType> convertedQts = new HashMap<>();
String[] ignoredProperties = getDataVectorIgnoredProperties( vectorType );
for ( T vector : vectors ) {
QuantitationType convertedQt = convertedQts.computeIfAbsent( vector.getQuantitationType(), createQtFunc );
result.add( createVector( vector, vectorType, convertedQt, doToVector, ignoredProperties ) );
}
return result;
}


/**
* Convert a single vector.
*/
public static <T extends DataVector> T convertVector( T vector, Function<QuantitationType, QuantitationType> createQtFunc, BiConsumer<T, T> doToVector, Class<T> vectorType ) {
return createVector( vector, vectorType, createQtFunc.apply( vector.getQuantitationType() ), doToVector, getDataVectorIgnoredProperties( vectorType ) );
}

private static <T extends DataVector> T createVector( T vector, Class<T> vectorType, QuantitationType convertedQt, BiConsumer<T, T> doToVector, String[] ignoredProperties ) {
T convertedVector = BeanUtils.instantiate( vectorType );
BeanUtils.copyProperties( vector, convertedVector, ignoredProperties );
convertedVector.setQuantitationType( convertedQt );
doToVector.accept( convertedVector, vector );
return convertedVector;
}

/**
* List of properties to copy over when converting a vector to a different QT.
*/
private static String[] getDataVectorIgnoredProperties( Class<? extends DataVector> vectorType ) {
List<String> ignoredPropertiesList = new ArrayList<>();
for ( PropertyDescriptor pd : BeanUtils.getPropertyDescriptors( vectorType ) ) {
if ( pd.getName().equals( "quantitationType" ) || ( pd.getName().startsWith( "data" ) && !pd.getName().equals( "dataIndices" ) ) ) {
ignoredPropertiesList.add( pd.getName() );
}
}
return ignoredPropertiesList.toArray( new String[0] );
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
package ubic.gemma.core.analysis.preprocess.convert;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import ubic.gemma.model.common.quantitationtype.PrimitiveType;
import ubic.gemma.model.common.quantitationtype.QuantitationType;
import ubic.gemma.model.expression.bioAssayData.DataVector;

import java.beans.PropertyDescriptor;
import java.util.*;
import java.util.Collection;

import static ubic.gemma.persistence.util.ByteArrayUtils.doubleArrayToBytes;

/**
* Convert {@link ubic.gemma.model.expression.bioAssayData.DataVector} from different representations.
* Convert {@link DataVector} to different {@link PrimitiveType}.
* @author poirigui
*/
public class RepresentationConversionUtils {
Expand All @@ -21,51 +19,21 @@ public class RepresentationConversionUtils {
* Convert a collection of vectors to a desired representation.
*/
public static <T extends DataVector> Collection<T> convertVectors( Collection<T> vectors, PrimitiveType toRepresentation, Class<T> vectorType ) {
ArrayList<T> result = new ArrayList<>( vectors.size() );
Map<QuantitationType, QuantitationType> convertedQts = new HashMap<>();
List<String> ignoredPropertiesList = new ArrayList<>();
for ( PropertyDescriptor pd : BeanUtils.getPropertyDescriptors( vectorType ) ) {
if ( pd.getName().equals( "quantitationType" ) || ( pd.getName().startsWith( "data" ) && !pd.getName().equals( "dataIndices" ) ) ) {
ignoredPropertiesList.add( pd.getName() );
}
}
String[] ignoredProperties = ignoredPropertiesList.toArray( new String[0] );
for ( T vector : vectors ) {
QuantitationType qt = vector.getQuantitationType();
QuantitationType convertedQt = convertedQts.computeIfAbsent( qt, qt2 -> {
QuantitationType quantitationType = QuantitationType.Factory.newInstance( qt2 );
String description;
if ( StringUtils.isNotBlank( qt.getDescription() ) ) {
description = StringUtils.appendIfMissing( StringUtils.strip( qt.getDescription() ), "." ) + " ";
} else {
description = "";
}
description += "Data was converted from " + qt.getRepresentation() + " to " + toRepresentation + ".";
quantitationType.setDescription( description );
quantitationType.setRepresentation( toRepresentation );
return quantitationType;
} );
T convertedVector = BeanUtils.instantiate( vectorType );
BeanUtils.copyProperties( vector, convertedVector, ignoredProperties );
convertedVector.setQuantitationType( convertedQt );
convertedVector.setData( convertData( vector, toRepresentation ) );
result.add( convertedVector );
}
return result;
return QuantitationTypeConversionUtils.convertVectors( vectors, qt -> getConvertedQuantitationType( qt, toRepresentation ), ( vec, origVec ) -> vec.setData( convertData( origVec, toRepresentation ) ), vectorType );
}

/**
* Convert a single vector to a desired representation.
*/
public static <T extends DataVector> T convertVector( T vector, PrimitiveType toRepresentation, Class<T> vectorType ) {
QuantitationType qt = vector.getQuantitationType();
QuantitationType convertedQt = QuantitationType.Factory.newInstance( qt );
convertedQt.setRepresentation( toRepresentation );
T convertedVector = BeanUtils.instantiate( vectorType );
BeanUtils.copyProperties( vector, convertedVector );
convertedVector.setQuantitationType( convertedQt );
convertedVector.setData( convertData( vector, toRepresentation ) );
return convertedVector;
private static QuantitationType getConvertedQuantitationType( QuantitationType qt, PrimitiveType toRepresentation ) {
QuantitationType quantitationType = QuantitationType.Factory.newInstance( qt );
String description;
if ( StringUtils.isNotBlank( qt.getDescription() ) ) {
description = StringUtils.appendIfMissing( StringUtils.strip( qt.getDescription() ), "." ) + " ";
} else {
description = "";
}
description += "Data was converted from " + qt.getRepresentation() + " to " + toRepresentation + ".";
quantitationType.setDescription( description );
quantitationType.setRepresentation( toRepresentation );
return quantitationType;
}

private static byte[] convertData( DataVector vector, PrimitiveType to ) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package ubic.gemma.core.analysis.preprocess.convert;

import org.apache.commons.lang3.StringUtils;
import ubic.gemma.model.common.quantitationtype.PrimitiveType;
import ubic.gemma.model.common.quantitationtype.QuantitationType;
import ubic.gemma.model.common.quantitationtype.ScaleType;
import ubic.gemma.model.common.quantitationtype.StandardQuantitationType;
import ubic.gemma.model.expression.bioAssayData.DataVector;

import java.util.Collection;

/**
* Utilities for converting data vectors to different scales and representations.
* Convert {@link DataVector} to different {@link ScaleType}.
* <p>
* For now, all conversions produce doubles
* For now, all conversions produce {@link PrimitiveType#DOUBLE}.
* @author poirigui
*/
public class ScaleTypeConversionUtils {
Expand All @@ -18,6 +22,25 @@ public class ScaleTypeConversionUtils {
private static final ThreadLocal<float[]> ONE_FLOAT_VALUE = ThreadLocal.withInitial( () -> new float[1] );
private static final ThreadLocal<double[]> ONE_DOUBLE_VALUE = ThreadLocal.withInitial( () -> new double[1] );

public static <T extends DataVector> Collection<T> convertVectors( Collection<T> vectors, ScaleType toScale, Class<T> vectorType ) {
return QuantitationTypeConversionUtils.convertVectors( vectors, qt -> getConvertedQuantitationType( qt, toScale ), ( vec, origVec ) -> vec.setDataAsDoubles( convertData( origVec, toScale ) ), vectorType );
}

private static QuantitationType getConvertedQuantitationType( QuantitationType qt, ScaleType toScale ) {
QuantitationType quantitationType = QuantitationType.Factory.newInstance( qt );
String description;
if ( StringUtils.isNotBlank( qt.getDescription() ) ) {
description = StringUtils.appendIfMissing( StringUtils.strip( qt.getDescription() ), "." ) + " ";
} else {
description = "";
}
description += "Data was converted from " + qt.getScale() + " to " + toScale + ".";
quantitationType.setDescription( description );
quantitationType.setScale( toScale );
quantitationType.setRepresentation( PrimitiveType.DOUBLE );
return quantitationType;
}

/**
* Convert a single number.
* <p>
Expand All @@ -31,19 +54,19 @@ public static double convertScalar( Number val, QuantitationType qt, ScaleType s
if ( val instanceof Float ) {
float[] vec = ONE_FLOAT_VALUE.get();
vec[0] = val.floatValue();
return convertVector( vec, qt, scaleType )[0];
return convertData( vec, qt, scaleType )[0];
} else if ( val instanceof Double ) {
double[] vec = ONE_DOUBLE_VALUE.get();
vec[0] = val.doubleValue();
return convertVector( vec, qt, scaleType )[0];
return convertData( vec, qt, scaleType )[0];
} else if ( val instanceof Integer ) {
int[] vec = ONE_INT_VALUE.get();
vec[0] = val.intValue();
return convertVector( vec, scaleType )[0];
return convertData( vec, scaleType )[0];
} else if ( val instanceof Long ) {
long[] vec = ONE_LONG_VALUE.get();
vec[0] = val.longValue();
return convertVector( vec, scaleType )[0];
return convertData( vec, scaleType )[0];
} else {
throw new UnsupportedOperationException( "Cannot convert " + val.getClass().getSimpleName() + " to " + scaleType + " scale." );
}
Expand All @@ -65,16 +88,16 @@ public static void clearScalarConversionThreadLocalStorage() {
* @throws IllegalArgumentException if the conversion is not possible
* @throws UnsupportedOperationException if the conversion is not supported
*/
public static double[] convertVector( DataVector vec, ScaleType scaleType ) {
public static double[] convertData( DataVector vec, ScaleType scaleType ) {
switch ( vec.getQuantitationType().getRepresentation() ) {
case FLOAT:
return convertVector( vec.getDataAsFloats(), vec.getQuantitationType(), scaleType );
return convertData( vec.getDataAsFloats(), vec.getQuantitationType(), scaleType );
case DOUBLE:
return convertVector( vec.getDataAsDoubles(), vec.getQuantitationType(), scaleType );
return convertData( vec.getDataAsDoubles(), vec.getQuantitationType(), scaleType );
case INT:
return convertVector( vec.getDataAsInts(), scaleType );
return convertData( vec.getDataAsInts(), scaleType );
case LONG:
return convertVector( vec.getDataAsLongs(), scaleType );
return convertData( vec.getDataAsLongs(), scaleType );
default:
throw new UnsupportedOperationException( "Conversion of " + vec.getQuantitationType().getRepresentation() + " is not supported." );
}
Expand All @@ -83,23 +106,23 @@ public static double[] convertVector( DataVector vec, ScaleType scaleType ) {
/**
* Convert a vector of float data to the target scale.
*/
public static double[] convertVector( float[] vec, QuantitationType quantitationType, ScaleType scaleType ) {
return convertVector( float2double( vec ), quantitationType.getType(), quantitationType.getScale(), scaleType );
public static double[] convertData( float[] vec, QuantitationType quantitationType, ScaleType scaleType ) {
return convertData( float2double( vec ), quantitationType.getType(), quantitationType.getScale(), scaleType );
}

/**
* Convert a vector of double data to the target scale.
*/
public static double[] convertVector( double[] vec, QuantitationType quantitationType, ScaleType scaleType ) {
return convertVector( vec, quantitationType.getType(), quantitationType.getScale(), scaleType );
public static double[] convertData( double[] vec, QuantitationType quantitationType, ScaleType scaleType ) {
return convertData( vec, quantitationType.getType(), quantitationType.getScale(), scaleType );
}

/**
* Convert a vector of counting data to the target scale.
* <p>
* The type and scale are assumed to be counts.
*/
public static double[] convertVector( int[] vec, ScaleType scaleType ) {
public static double[] convertData( int[] vec, ScaleType scaleType ) {
if ( scaleType == ScaleType.LINEAR || scaleType == ScaleType.COUNT ) {
return int2double( vec );
}
Expand Down Expand Up @@ -129,7 +152,7 @@ public static double[] convertVector( int[] vec, ScaleType scaleType ) {
* <p>
* The type and scale are assumed to be counts.
*/
public static double[] convertVector( long[] vec, ScaleType scaleType ) {
public static double[] convertData( long[] vec, ScaleType scaleType ) {
if ( scaleType == ScaleType.LINEAR || scaleType == ScaleType.COUNT ) {
return long2double( vec );
}
Expand All @@ -154,7 +177,7 @@ public static double[] convertVector( long[] vec, ScaleType scaleType ) {
return result;
}

public static double[] convertVector( double[] vec, StandardQuantitationType fromType, ScaleType fromScale, ScaleType scaleType ) {
public static double[] convertData( double[] vec, StandardQuantitationType fromType, ScaleType fromScale, ScaleType scaleType ) {
if ( fromScale == scaleType ) {
return vec;
}
Expand Down
Loading

0 comments on commit fc6cbe5

Please sign in to comment.