Skip to content

Commit

Permalink
Merge pull request #120 from mastodon-sc/t-sne
Browse files Browse the repository at this point in the history
Add T-SNE dimensionality reduction method
  • Loading branch information
stefanhahmann authored Dec 3, 2024
2 parents 6c178cd + e0fb205 commit eb53b5e
Show file tree
Hide file tree
Showing 53 changed files with 3,235 additions and 1,185 deletions.
40 changes: 30 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,29 +304,33 @@ Tree2
## Dimensionality reduction

For visualizing high-dimensional data, e.g. in two dimensions, potentially getting more insights into your data, you can
reduce the dimensionality of the measurements, using this algorithm:
reduce the dimensionality of the measurements, using these algorithms:

* [Uniform Manifold Approximation Projection (UMAP)](https://arxiv.org/abs/1802.03426)
* [UMAP Python implementation](https://umap-learn.readthedocs.io/en/latest/)
* UMAP
* [Uniform Manifold Approximation Projection (UMAP)](https://arxiv.org/abs/1802.03426)
* [UMAP Python implementation](https://umap-learn.readthedocs.io/en/latest/)
* t-SNE
* [t-distributed Stochastic Neighbor Embedding (t-SNE)](https://lvdmaaten.github.io/tsne/)
* [t-SNE Python implementation](https://scikit-learn.org/stable/modules/generated/sklearn.manifold.TSNE.html)

### Usage

* Menu Location: `Plugins > Compute Feature > Dimensionality reduction > UMAP`
* Menu Location: `Plugins > Compute Feature > Dimensionality reduction`

Select the graph type whose features should be dimensionality reduced, either the Model Graph with Features for Spots
and Links or the Branch Graph with Features on BranchSpots and BranchLinks.
Next, select the feature + feature projections that should be dimensionality reduced. Prefer to select features, which
describe the phenotype (e.g. size, shape, velocity, number of neighbors, etc.).
Only select positional features (e.g. centroid, coordinates, timeframe, etc.) if the position of cells within
Only select positional features (e.g. centroid, coordinates, timeframe, etc.), if the position of cells within
the image are descriptive for the phenotype. If you are unsure, you can select all features and then remove the
positional features later.

### Description

The UMAP algorithm reduces the dimensionality of the selected features and adds the reduced features to the table.
In order to do so, the UMAP algorithm uses the data matrix from the spot or branch spot table, where each row represents
a spot or branch spot and each column represents a feature. The link and branch link features can be included in the
algorithm.
The available algorithms reduce the dimensionality of the selected features and adds the results as a new feature to the
table. In order to do so, the selected algorithm uses the data matrix from the spot or branch spot table, where each row
represents a spot or branch spot and each column represents a feature. The link and branch link features can be included
in the algorithm.

If they are selected, the algorithm will use the link feature value of its incoming edge or the average of all values of
all incoming edges, if there is more than one incoming edge.
Expand All @@ -338,12 +342,17 @@ By default, all measurements are selected in the box.

### Parameters

#### Common Parameters

* Standardize: Whether to standardize the data before reducing the dimensionality. Standardization is recommended when
the data has different scales / units.
Further
reading: [Standardization](https://scikit-learn.org/stable/modules/preprocessing.html#standardization-or-mean-removal-and-variance-scaling).
* Number of dimensions: The number of reduced dimensions to use. The default is 2, but 3 is also common.
Further reading: [Number of Dimensions](https://umap-learn.readthedocs.io/en/latest/parameters.html#n-components).

#### UMAP Parameters

* Number of neighbors: The size of the local neighborhood (in terms of number of neighboring sample points) used for
manifold approximation.
Larger values result in more global views of the manifold, while smaller values result in more local data being
Expand All @@ -354,7 +363,18 @@ By default, all measurements are selected in the box.
representation. This parameter controls how tightly UMAP is allowed to pack points together.
Further reading: [Minimum Distance](https://umap-learn.readthedocs.io/en/latest/parameters.html#min-dist).

When you are done with the selection, click on `Compute UMAP`.
#### t-SNE Parameters

* Perplexity: The perplexity is related to the number of nearest neighbors that are used in other manifold learning
algorithms. Larger datasets usually require a larger perplexity. The recommended range is between 5 and 50.
Further
reading: [Perplexity](https://scikit-learn.org/stable/modules/generated/sklearn.manifold.TSNE.html#sklearn.manifold.TSNE).
* Maximum number of iterations: The maximum number of iterations for the optimization. The default is 1000. More
iterations will give more accurate results, but will also take longer to compute.
Further
reading: [Maximum Number of Iterations](https://scikit-learn.org/stable/modules/generated/sklearn.manifold.TSNE.html#sklearn.manifold.TSNE).

When you are done with the selection, click on `Compute`.
The resulting values will be added as additional columns to the selected table.

![umap_table.png](doc/dimensionalityreduction/umap_table.png)
Expand Down
Binary file modified doc/dimensionalityreduction/umap_dialog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 8 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@
<version>1.0</version>
</dependency>

<!-- t-SNE -->
<dependency>
<groupId>com.github.lejon.T-SNE-Java</groupId>
<artifactId>tsne</artifactId>
<version>v2.6.4</version>
</dependency>

<!-- Standardization for UMAP preprocessing -->
<dependency>
<groupId>org.apache.commons</groupId>
Expand Down Expand Up @@ -213,7 +220,7 @@
<repositories>
<repository>
<id>scijava.public</id>
<url>https://maven.scijava.org/content/groups/public</url>
<url>https://maven.scijava.org/content/repositories/public/</url>
</repository>
</repositories>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.mastodon.mamut.feature.branch.dimensionalityreduction;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

import org.mastodon.io.FileIdToObjectMap;
import org.mastodon.io.ObjectToFileIdMap;
import org.mastodon.io.properties.DoublePropertyMapSerializer;
import org.mastodon.mamut.feature.branch.BranchFeatureSerializer;
import org.mastodon.mamut.feature.dimensionalityreduction.AbstractOutputFeature;
import org.mastodon.mamut.model.ModelGraph;
import org.mastodon.mamut.model.Spot;
import org.mastodon.mamut.model.branch.BranchSpot;
import org.mastodon.mamut.model.branch.ModelBranchGraph;
import org.mastodon.properties.DoublePropertyMap;

public abstract class BranchOutputSerializerTools
{

private BranchOutputSerializerTools()
{
// prevent instantiation
}

public static void serialize( final AbstractOutputFeature< BranchSpot > feature, final ObjectToFileIdMap< Spot > idmap,
final ObjectOutputStream oos, final ModelBranchGraph branchGraph, final ModelGraph graph ) throws IOException
{
oos.writeInt( feature.getOutputMaps().size() );
for ( DoublePropertyMap< BranchSpot > outputMap : feature.getOutputMaps() )
{
final DoublePropertyMap< Spot > spotMap = BranchFeatureSerializer.branchSpotMapToMap( outputMap, branchGraph, graph );
final DoublePropertyMapSerializer< Spot > propertyMapSerializer = new DoublePropertyMapSerializer<>( spotMap );
propertyMapSerializer.writePropertyMap( idmap, oos );
}
}

public static < T extends AbstractOutputFeature< BranchSpot > > T deserialize( final FileIdToObjectMap< Spot > idmap,
final ObjectInputStream ois, final ModelBranchGraph branchGraph, final ModelGraph graph,
final Function< List< DoublePropertyMap< BranchSpot > >, T > featureCreator ) throws ClassNotFoundException, IOException
{
int numDimensions = ois.readInt();
List< DoublePropertyMap< BranchSpot > > outputMaps = new ArrayList<>( numDimensions );
for ( int i = 0; i < numDimensions; i++ )
{
final DoublePropertyMap< Spot > spotMap = new DoublePropertyMap<>( graph.vertices(), Double.NaN );
DoublePropertyMapSerializer< Spot > serializer = new DoublePropertyMapSerializer<>( spotMap );
serializer.readPropertyMap( idmap, ois );

DoublePropertyMap< BranchSpot > output = BranchFeatureSerializer.mapToBranchSpotMap( spotMap, branchGraph );
outputMaps.add( output );
}
return featureCreator.apply( outputMaps );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*-
* #%L
* mastodon-deep-lineage
* %%
* Copyright (C) 2022 - 2024 Stefan Hahmann
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
package org.mastodon.mamut.feature.branch.dimensionalityreduction.tsne;

import java.util.List;

import org.mastodon.feature.Feature;
import org.mastodon.feature.FeatureProjectionKey;
import org.mastodon.feature.FeatureProjectionSpec;
import org.mastodon.feature.FeatureSpec;
import org.mastodon.feature.Multiplicity;
import org.mastodon.mamut.feature.dimensionalityreduction.tsne.feature.AbstractTSneFeature;
import org.mastodon.mamut.model.branch.BranchSpot;
import org.mastodon.properties.DoublePropertyMap;
import org.scijava.plugin.Plugin;

/**
* Represents a t-SNE feature for BranchSpots in the Mastodon project.
* <br>
* This feature is used to store the t-SNE outputs for BranchSpots.
* <br>
* The t-SNE outputs are stored in a list of {@link DoublePropertyMap}s. The size of the list is equal to the number of dimensions of the t-SNE output.
*/
public class BranchTSneFeature extends AbstractTSneFeature< BranchSpot >
{
public static final String KEY = "Branch t-SNE outputs";

private final BranchSpotTSneFeatureSpec adaptedSpec;

public static final BranchSpotTSneFeatureSpec GENERIC_SPEC = new BranchSpotTSneFeatureSpec();

public BranchTSneFeature( final List< DoublePropertyMap< BranchSpot > > outputMaps )
{
super( outputMaps );
FeatureProjectionSpec[] projectionSpecs =
projectionMap.keySet().stream().map( FeatureProjectionKey::getSpec ).toArray( FeatureProjectionSpec[]::new );
this.adaptedSpec = new BranchSpotTSneFeatureSpec( projectionSpecs );
}

@Plugin( type = FeatureSpec.class )
public static class BranchSpotTSneFeatureSpec extends FeatureSpec< BranchTSneFeature, BranchSpot >
{
public BranchSpotTSneFeatureSpec()
{
super( KEY, HELP_STRING, BranchTSneFeature.class, BranchSpot.class, Multiplicity.SINGLE );
}

public BranchSpotTSneFeatureSpec( final FeatureProjectionSpec... projectionSpecs )
{
super( KEY, HELP_STRING, BranchTSneFeature.class, BranchSpot.class, Multiplicity.SINGLE, projectionSpecs );
}
}

@Override
public FeatureSpec< ? extends Feature< BranchSpot >, BranchSpot > getSpec()
{
return adaptedSpec;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*-
* #%L
* mastodon-deep-lineage
* %%
* Copyright (C) 2022 - 2024 Stefan Hahmann
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
package org.mastodon.mamut.feature.branch.dimensionalityreduction.tsne;

import java.util.Collection;
import java.util.List;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.mastodon.RefPool;
import org.mastodon.mamut.feature.dimensionalityreduction.tsne.feature.AbstractTSneFeature;
import org.mastodon.mamut.feature.dimensionalityreduction.tsne.feature.AbstractTSneFeatureComputer;
import org.mastodon.mamut.model.Model;
import org.mastodon.mamut.model.branch.BranchLink;
import org.mastodon.mamut.model.branch.BranchSpot;
import org.mastodon.mamut.model.branch.ModelBranchGraph;
import org.mastodon.properties.DoublePropertyMap;
import org.scijava.Context;

public class BranchTSneFeatureComputer extends AbstractTSneFeatureComputer< BranchSpot, BranchLink, ModelBranchGraph >
{

public BranchTSneFeatureComputer( final Model model, final Context context )
{
super( model, context );
}

@Override
protected AbstractTSneFeature< BranchSpot > createFeatureInstance( final List< DoublePropertyMap< BranchSpot > > umapOutputMaps )
{
return new BranchTSneFeature( umapOutputMaps );
}

@Override
protected RefPool< BranchSpot > getRefPool()
{
return model.getBranchGraph().vertices().getRefPool();
}

@Override
protected ReentrantReadWriteLock getLock( final ModelBranchGraph branchGraph )
{
return branchGraph.getLock();
}

@Override
protected Collection< BranchSpot > getVertices()
{
return model.getBranchGraph().vertices();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*-
* #%L
* mastodon-deep-lineage
* %%
* Copyright (C) 2022 - 2024 Stefan Hahmann
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
package org.mastodon.mamut.feature.branch.dimensionalityreduction.tsne;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import org.mastodon.feature.FeatureSpec;
import org.mastodon.feature.io.FeatureSerializer;
import org.mastodon.io.FileIdToObjectMap;
import org.mastodon.io.ObjectToFileIdMap;
import org.mastodon.mamut.feature.branch.BranchFeatureSerializer;
import org.mastodon.mamut.feature.branch.dimensionalityreduction.BranchOutputSerializerTools;
import org.mastodon.mamut.model.ModelGraph;
import org.mastodon.mamut.model.Spot;
import org.mastodon.mamut.model.branch.BranchSpot;
import org.mastodon.mamut.model.branch.ModelBranchGraph;
import org.scijava.plugin.Plugin;

/**
* De-/serializes {@link BranchTSneFeature}
*/
@Plugin( type = FeatureSerializer.class )
public class BranchTSneFeatureSerializer implements BranchFeatureSerializer< BranchTSneFeature, BranchSpot, Spot >
{
@Override
public FeatureSpec< BranchTSneFeature, BranchSpot > getFeatureSpec()
{
return BranchTSneFeature.GENERIC_SPEC;
}

@Override
public void serialize( final BranchTSneFeature feature, final ObjectToFileIdMap< Spot > idmap, final ObjectOutputStream oos,
final ModelBranchGraph branchGraph, final ModelGraph graph ) throws IOException
{
BranchOutputSerializerTools.serialize( feature, idmap, oos, branchGraph, graph );
}

@Override
public BranchTSneFeature deserialize( final FileIdToObjectMap< Spot > idmap, final ObjectInputStream ois,
final ModelBranchGraph branchGraph, final ModelGraph graph ) throws ClassNotFoundException, IOException
{
return BranchOutputSerializerTools.deserialize( idmap, ois, branchGraph, graph, BranchTSneFeature::new );
}
}
Loading

0 comments on commit eb53b5e

Please sign in to comment.