3D visualization in dynamo

dynamo v1.4 ships a full suite of 3D visualizations (matplotlib, Plotly, PyVista). This tutorial renders them on the pancreatic endocrinogenesis dataset (Ductal → Ngn3 → Pre-endocrine → endocrine), so the 3D velocity arrows can be checked against the 2D flow.

Rendering. Each 3D plot is shown twice: a static image (so it displays on GitHub, which strips interactive JavaScript) followed by the interactive Plotly / PyVista version (rotate & zoom — it renders on nbviewer / readthedocs and in JupyterLab).

import os
os.environ['PYVISTA_OFF_SCREEN'] = 'true'
import warnings; warnings.filterwarnings('ignore')
import io, base64
from IPython.display import HTML, Image, display
import matplotlib.pyplot as plt
import imageio.v2 as imageio
import pyvista as pv
pv.OFF_SCREEN = True
import dynamo as dyn
dyn.configuration.set_figure_params('dynamo', background='white')

def _png(img, w, h):
    plt.close('all')  # drop the empty matplotlib figure dynamo's pv path leaves open
    fig = plt.figure(figsize=(w / 100, h / 100))
    plt.imshow(img); plt.axis('off'); fig.tight_layout(pad=0); plt.show()

def show_pv_both(obj, w=720, h=560):
    """Static screenshot (for GitHub) + interactive scene (for nbviewer / readthedocs)."""
    pl = obj[0] if isinstance(obj, (list, tuple)) else obj
    pl.window_size = [w, h]
    _png(pl.screenshot(return_img=True), w, h)
    pl.export_html('_pv.html'); html = open('_pv.html').read(); os.remove('_pv.html')
    b64 = base64.b64encode(html.encode()).decode()
    display(HTML(f'<iframe src="data:text/html;base64,{b64}" width="{w}" height="{h}" frameborder="0"></iframe>'))

def show_plotly(fig):
    display(HTML(fig.to_html(include_plotlyjs='cdn', full_html=False)))

1. Preprocess and estimate velocity

adata = dyn.sample_data.pancreatic_endocrinogenesis()
celltype_key = 'clusters'
dyn.pp.Preprocessor().preprocess_adata(adata, recipe='monocle')
dyn.tl.dynamics(adata, model='stochastic', cores=4)
adata
|-----> Downloading data to ./data/endocrinogenesis_day15.h5ad
|-----> File ./data/endocrinogenesis_day15.h5ad already exists.
|-----> Running monocle preprocessing pipeline...
|-----------> filtered out 0 outlier cells
|-----------> filtered out 21736 outlier genes
|-----> PCA dimension reduction
|-----> <insert> X_pca to obsm in AnnData Object.
|-----> [Preprocessor-monocle] completed [3.7344s]

╭─ SUMMARY: Preprocessor.preprocess_adata ───────────────────────────╮
  Duration: 3.7363s                                                 
  Shape:    3,696 x 27,998 (Unchanged)                              
                                                                    
  CHANGES DETECTED                                                  
  ────────────────                                                  
   OBS    Size_Factor (float)                                  
 initial_cell_size (float)                            
 initial_spliced_cell_size (float)                    
 initial_unspliced_cell_size (float)                  
 nCounts (float)                                      
 nGenes (int)                                         
 ntr (float)                                          
 pMito (float)                                        
 pass_basic_filter (bool)                             
 spliced_Size_Factor (float)                          
 unspliced_Size_Factor (float)                        
                                                                    
   VAR    frac (float)                                         
 log_cv (float)                                       
 log_m (float)                                        
 nCells (int)                                         
 nCounts (float)                                      
 ntr (float)                                          
 pass_basic_filter (bool)                             
 score (float)                                        
 use_for_pca (bool)                                   
                                                                    
   UNS    PCs                                                  
 explained_variance_ratio_                            
 feature_selection                                    
 pca_mean                                             
 pp                                                   
 velocyto_SVR                                         
                                                                    
   LAYERS X_spliced (sparse matrix, 3696x27998)                
 X_unspliced (sparse matrix, 3696x27998)              
                                                                    
╰────────────────────────────────────────────────────────────────────╯
|-----> dynamics_del_2nd_moments_key is None. Using default value from DynamoAdataConfig: dynamics_del_2nd_moments_key=False
|-----------> removing existing M layers:[]...
|-----------> making adata smooth...
|-----> calculating first/second moments...
|-----> [moments calculation] completed [24.4173s]

╭─ SUMMARY: dynamics ────────────────────────────────────────────────╮
  Duration: 30.5001s                                                
  Shape:    3,696 x 27,998 (Unchanged)                              
                                                                    
  CHANGES DETECTED                                                  
  ────────────────                                                  
   VAR    use_for_dynamics (bool)                              
                                                                    
   UNS    dynamics                                             
 vel_params_names                                     
                                                                    
   OBSP   moments_con (sparse matrix, 3696x3696)               
                                                                    
   LAYERS M_s (sparse matrix, 3696x27998)                      
 M_ss (sparse matrix, 3696x27998)                     
 M_u (sparse matrix, 3696x27998)                      
 M_us (sparse matrix, 3696x27998)                     
 M_uu (sparse matrix, 3696x27998)                     
 velocity_S (sparse matrix, 3696x27998)               
                                                                    
╰────────────────────────────────────────────────────────────────────╯
AnnData object with n_obs × n_vars = 3696 × 27998
    obs: 'clusters_coarse', 'clusters', 'S_score', 'G2M_score', 'nGenes', 'nCounts', 'pMito', 'pass_basic_filter', 'Size_Factor', 'initial_cell_size', 'spliced_Size_Factor', 'initial_spliced_cell_size', 'unspliced_Size_Factor', 'initial_unspliced_cell_size', 'ntr'
    var: 'highly_variable_genes', 'nCells', 'nCounts', 'pass_basic_filter', 'log_m', 'log_cv', 'score', 'frac', 'use_for_pca', 'ntr', 'use_for_dynamics'
    uns: 'clusters_coarse_colors', 'clusters_colors', 'day_colors', 'neighbors', 'pca', 'pp', 'velocyto_SVR', 'feature_selection', 'PCs', 'explained_variance_ratio_', 'pca_mean', 'history_log', 'vel_params_names', 'dynamics'
    obsm: 'X_pca', 'X_umap'
    varm: 'vel_params'
    layers: 'spliced', 'unspliced', 'X_spliced', 'X_unspliced', 'M_u', 'M_uu', 'M_s', 'M_us', 'M_ss', 'velocity_S'
    obsp: 'distances', 'connectivities', 'moments_con'

2. 2D reference flow

The 2D velocity streamlines run from Ductal/Ngn3 progenitors to the endocrine fates — the ground truth we compare the 3D arrows against.

dyn.tl.reduceDimension(adata, basis='umap', n_components=2, enforce=True)
dyn.tl.cell_velocities(adata, basis='umap')
dyn.pl.streamline_plot(adata, color=[celltype_key], basis='umap', figsize=(5, 5))
|-----> retrieve data for non-linear dimension reduction...
|-----> [UMAP] using X_umap with n_pca_components = 30
|-----> <insert> X_umap to obsm in AnnData Object.
|-----> [UMAP] completed [20.6363s]

╭─ SUMMARY: reduceDimension ─────────────────────────────────────────╮
  Duration: 20.6379s                                                
  Shape:    3,696 x 27,998 (Unchanged)                              
                                                                    
  CHANGES DETECTED                                                  
  ────────────────                                                  
   UNS    umap_fit                                             
                                                                    
╰────────────────────────────────────────────────────────────────────╯
|-----? Some row sums(out degree) in adata's neighbor graph are zero.
|-----> Neighbor graph is broken, recomputing....
|-----> Start computing neighbor graph...
|-----------> X_data is None, fetching or recomputing...
|-----> fetching X data from layer:None, basis:pca
|-----> method arg is None, choosing methods automatically...
|-----------> method ball_tree selected
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 1.0011%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 2.0022%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 3.0032%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 4.0043%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 5.0054%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 6.0065%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 7.0076%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 8.0087%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 9.0097%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 10.0108%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 11.0119%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 12.0130%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 13.0141%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 14.0152%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 15.0162%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 16.0173%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 17.0184%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 18.0195%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 19.0206%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 20.0216%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 21.0227%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 22.0238%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 23.0249%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 24.0260%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 25.0271%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 26.0281%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 27.0292%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 28.0303%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 29.0314%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 30.0325%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 31.0335%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 32.0346%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 33.0357%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 34.0368%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 35.0379%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 36.0390%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 37.0400%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 38.0411%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 39.0422%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 40.0433%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 41.0444%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 42.0455%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 43.0465%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 44.0476%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 45.0487%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 46.0498%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 47.0509%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 48.0519%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 49.0530%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 50.0541%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 51.0552%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 52.0563%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 53.0574%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 54.0584%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 55.0595%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 56.0606%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 57.0617%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 58.0628%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 59.0639%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 60.0649%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 61.0660%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 62.0671%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 63.0682%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 64.0693%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 65.0703%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 66.0714%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 67.0725%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 68.0736%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 69.0747%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 70.0758%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 71.0768%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 72.0779%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 73.0790%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 74.0801%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 75.0812%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 76.0823%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 77.0833%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 78.0844%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 79.0855%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 80.0866%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 81.0877%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 82.0887%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 83.0898%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 84.0909%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 85.0920%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 86.0931%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 87.0942%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 88.0952%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 89.0963%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 90.0974%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 91.0985%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 92.0996%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 93.1006%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 94.1017%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 95.1028%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 96.1039%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 97.1050%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 98.1061%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 99.1071%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] in progress: 100.0000%
|-----> [calculating transition matrix via pearson kernel with sqrt transform.] completed [3.2030s]
|-----> [projecting velocity vector to low dimensional embedding] in progress: 1.0011%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 2.0022%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 3.0032%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 4.0043%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 5.0054%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 6.0065%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 7.0076%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 8.0087%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 9.0097%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 10.0108%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 11.0119%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 12.0130%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 13.0141%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 14.0152%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 15.0162%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 16.0173%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 17.0184%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 18.0195%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 19.0206%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 20.0216%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 21.0227%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 22.0238%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 23.0249%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 24.0260%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 25.0271%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 26.0281%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 27.0292%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 28.0303%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 29.0314%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 30.0325%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 31.0335%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 32.0346%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 33.0357%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 34.0368%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 35.0379%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 36.0390%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 37.0400%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 38.0411%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 39.0422%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 40.0433%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 41.0444%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 42.0455%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 43.0465%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 44.0476%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 45.0487%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 46.0498%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 47.0509%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 48.0519%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 49.0530%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 50.0541%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 51.0552%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 52.0563%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 53.0574%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 54.0584%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 55.0595%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 56.0606%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 57.0617%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 58.0628%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 59.0639%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 60.0649%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 61.0660%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 62.0671%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 63.0682%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 64.0693%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 65.0703%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 66.0714%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 67.0725%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 68.0736%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 69.0747%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 70.0758%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 71.0768%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 72.0779%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 73.0790%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 74.0801%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 75.0812%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 76.0823%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 77.0833%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 78.0844%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 79.0855%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 80.0866%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 81.0877%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 82.0887%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 83.0898%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 84.0909%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 85.0920%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 86.0931%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 87.0942%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 88.0952%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 89.0963%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 90.0974%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 91.0985%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 92.0996%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 93.1006%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 94.1017%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 95.1028%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 96.1039%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 97.1050%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 98.1061%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 99.1071%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 100.0000%
|-----> [projecting velocity vector to low dimensional embedding] completed [0.3836s]
|-----> method arg is None, choosing methods automatically...
|-----------> method kd_tree selected

╭─ SUMMARY: cell_velocities ─────────────────────────────────────────╮

  Duration: 4.1752s                                                 
  Shape:    3,696 x 27,998 (Unchanged)                              
                                                                    
  CHANGES DETECTED                                                  
  ────────────────                                                  
   VAR    use_for_transition (bool)                            
                                                                    
   UNS    grid_velocity_umap                                   
                                                                    
   OBSP   pearson_transition_matrix (sparse matrix, 3696x3696) 
                                                                    
   OBSM   velocity_umap (array, 3696x2)                        
                                                                    
╰────────────────────────────────────────────────────────────────────╯
|-----> method arg is None, choosing methods automatically...
|-----------> method kd_tree selected
|-----------> plotting with basis key=X_umap
|-----------> skip filtering clusters by stack threshold when stacking color because it is not a numeric type
../../_images/4e465d1cbba0fb746df3899281991c38cdef4c5a54e83dbadac77e43c244ceea.png

3. Build a 3D embedding

Compute a 3-component UMAP and project velocities onto it.

dyn.tl.reduceDimension(adata, basis='umap', n_components=3, enforce=True)
dyn.tl.cell_velocities(adata, basis='umap')
print('X_umap:', adata.obsm['X_umap'].shape, '| velocity_umap:', adata.obsm['velocity_umap'].shape)
|-----> retrieve data for non-linear dimension reduction...
|-----> [UMAP] using X_umap with n_pca_components = 30
|-----> <insert> X_umap to obsm in AnnData Object.
|-----> [UMAP] completed [17.5555s]

╭─ SUMMARY: reduceDimension ─────────────────────────────────────────╮
  Duration: 17.557s                                                 
  Shape:    3,696 x 27,998 (Unchanged)                              
                                                                    
  CHANGES DETECTED                                                  
  ────────────────                                                  
╰────────────────────────────────────────────────────────────────────╯
Using existing pearson_transition_matrix found in .obsp.
|-----> [projecting velocity vector to low dimensional embedding] in progress: 1.0011%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 2.0022%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 3.0032%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 4.0043%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 5.0054%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 6.0065%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 7.0076%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 8.0087%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 9.0097%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 10.0108%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 11.0119%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 12.0130%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 13.0141%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 14.0152%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 15.0162%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 16.0173%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 17.0184%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 18.0195%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 19.0206%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 20.0216%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 21.0227%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 22.0238%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 23.0249%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 24.0260%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 25.0271%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 26.0281%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 27.0292%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 28.0303%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 29.0314%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 30.0325%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 31.0335%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 32.0346%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 33.0357%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 34.0368%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 35.0379%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 36.0390%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 37.0400%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 38.0411%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 39.0422%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 40.0433%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 41.0444%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 42.0455%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 43.0465%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 44.0476%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 45.0487%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 46.0498%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 47.0509%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 48.0519%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 49.0530%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 50.0541%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 51.0552%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 52.0563%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 53.0574%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 54.0584%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 55.0595%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 56.0606%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 57.0617%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 58.0628%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 59.0639%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 60.0649%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 61.0660%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 62.0671%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 63.0682%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 64.0693%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 65.0703%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 66.0714%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 67.0725%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 68.0736%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 69.0747%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 70.0758%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 71.0768%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 72.0779%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 73.0790%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 74.0801%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 75.0812%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 76.0823%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 77.0833%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 78.0844%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 79.0855%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 80.0866%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 81.0877%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 82.0887%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 83.0898%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 84.0909%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 85.0920%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 86.0931%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 87.0942%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 88.0952%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 89.0963%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 90.0974%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 91.0985%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 92.0996%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 93.1006%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 94.1017%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 95.1028%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 96.1039%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 97.1050%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 98.1061%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 99.1071%
|-----> [projecting velocity vector to low dimensional embedding] in progress: 100.0000%
|-----> [projecting velocity vector to low dimensional embedding] completed [0.3961s]
|-----> method arg is None, choosing methods automatically...
|-----------> method kd_tree selected

╭─ SUMMARY: cell_velocities ─────────────────────────────────────────╮
  Duration: 0.6477s                                                 
  Shape:    3,696 x 27,998 (Unchanged)                              
                                                                    
  CHANGES DETECTED                                                  
  ────────────────                                                  
╰────────────────────────────────────────────────────────────────────╯
X_umap: (3696, 3) | velocity_umap: (3696, 3)

4. Rotating 3D velocity field (animated)

A rotating animation reads the 3D velocity field far better than a single static view. We render the matplotlib cell_wise_vectors_3d once and orbit the camera. With thousands of cells, subsample the arrows (cell_inds) and lengthen them (quiver_3d_kwargs['length']) so they are legible.

quiver_kwargs = {'linewidth': 2.0, 'edgecolors': 'white', 'alpha': 1,
                 'length': 35, 'arrow_length_ratio': 0.4, 'cmap': None}
axes = dyn.pl.cell_wise_vectors_3d(
    adata, basis='umap', color=[celltype_key], plot_method='matplotlib',
    cell_inds=200, pointsize=0.04, quiver_3d_kwargs=quiver_kwargs,
    elev=20, azim=-60, figsize=(6, 6), save_show_or_return='return',
)
ax = axes[0] if isinstance(axes, (list, tuple)) else axes
fig = ax.get_figure(); fig.set_size_inches(6, 6)
frames = []
for azim in range(-90, 270, 10):
    ax.view_init(elev=20, azim=azim)
    buf = io.BytesIO(); fig.savefig(buf, format='png', dpi=85); buf.seek(0)
    frames.append(imageio.imread(buf))
plt.close(fig)
imageio.mimsave('velocity_3d.gif', frames, fps=12, loop=0)
Image(filename='velocity_3d.gif')
|-----> X shape: (3696, 3) V shape: (3696, 3)
|-----------> plotting with basis key=X_umap
|-----------> skip filtering clusters by stack threshold when stacking color because it is not a numeric type
../../_images/07619ef19ca367e2eaee578bf9b0416b9dc7d6743a6a87bf3f475eac42541a7d.gif

5. 3D scatter — scatters_interactive

A static PyVista screenshot (GitHub), then the interactive Plotly scatter (drag to rotate on nbviewer / readthedocs).

pl = dyn.pl.scatters_interactive(
    adata, basis='umap', color=celltype_key, plot_method='pv', save_show_or_return='return'
)
pl = pl[0] if isinstance(pl, (list, tuple)) else pl
pl.window_size = [700, 560]
_png(pl.screenshot(return_img=True), 700, 560)

fig, _ = dyn.pl.scatters_interactive(
    adata, basis='umap', color=celltype_key, plot_method='plotly', save_show_or_return='return'
)
fig.update_layout(width=700, height=550)
show_plotly(fig)
|-----------> plotting with basis key=X_umap
|-----------> skip filtering clusters by stack threshold when stacking color because it is not a numeric type
|-----------> plotting with basis key=X_umap
|-----------> skip filtering clusters by stack threshold when stacking color because it is not a numeric type
../../_images/da750e9660478238fd179e562c8277ea9beb5d9b8da900ae32f126260eff619f.png
../../_images/093fb7a35e481a69f4b26774b1cd708beed5994db071a70818f0180b5102b774.png

6. 3D velocity vectors — cell_wise_vectors_3d (static)

The matplotlib renderer at two viewing angles; the arrows follow the same direction as the 2D streamlines.

dyn.pl.cell_wise_vectors_3d(
    adata, basis='umap', color=[celltype_key], plot_method='matplotlib',
    cell_inds=120, pointsize=0.05, quiver_3d_kwargs=quiver_kwargs, elev=25, azim=-60,
)
|-----> X shape: (3696, 3) V shape: (3696, 3)
|-----------> plotting with basis key=X_umap
|-----------> skip filtering clusters by stack threshold when stacking color because it is not a numeric type
../../_images/2780f9b23f34d14ad9bc7e1bab94e8c5264c1203b32154c5f7a38ddf402a12b7.png
dyn.pl.cell_wise_vectors_3d(
    adata, basis='umap', color=[celltype_key], plot_method='matplotlib',
    cell_inds=120, pointsize=0.05, quiver_3d_kwargs=quiver_kwargs, elev=20, azim=40,
)
|-----> X shape: (3696, 3) V shape: (3696, 3)
|-----------> plotting with basis key=X_umap
|-----------> skip filtering clusters by stack threshold when stacking color because it is not a numeric type
../../_images/b451fd51c1cc7a090a2ad397bc2935004f7be07b7b6cb6d062bb2d767b0821ef.png

7. 3D velocity vectors — cell_wise_vectors_3d (interactive)

The PyVista backend: static screenshot, then an interactive scene to rotate/zoom on the velocity glyphs.

pl = dyn.pl.cell_wise_vectors_3d(
    adata, basis='umap', color=[celltype_key], plot_method='pv', save_show_or_return='return'
)
show_pv_both(pl)
|-----> X shape: (3696, 3) V shape: (3696, 3)
|-----------> plotting with basis key=X_umap
|-----------> skip filtering clusters by stack threshold when stacking color because it is not a numeric type
../../_images/6edbb56ebc6d3ff15b441fa68faf30266c425afdc71ddbe226aac63392b21e8f.png

8. 3D vector-field topography — topography_3D

Reconstruct a continuous 3D vector field (dyn.vf.VectorField, dims=3) and its fixed points (dyn.vf.topography); static matplotlib, then static + interactive PyVista.

dyn.vf.VectorField(adata, basis='umap', dims=3)
dyn.vf.topography(adata, basis='umap', n=10)
dyn.pl.topography_3D(
    adata, basis='umap', color=[celltype_key], plot_method='matplotlib', save_show_or_return='return'
)
|-----> VectorField reconstruction begins...
|-----> Retrieve X and V based on basis: UMAP. 
        Vector field will be learned in the UMAP space.
|-----> Generating high dimensional grids and convert into a row matrix.
|-----> Learning vector field with method: sparsevfc.
|-----> [SparseVFC] begins...
|-----> Sampling control points based on data velocity magnitude...
|-----> method arg is None, choosing methods automatically...
|-----------> method kd_tree selected
|-----> [SparseVFC] completed [3.4957s]
|-----> [VectorField] completed [3.6084s]

╭─ SUMMARY: VectorField ─────────────────────────────────────────────╮
  Duration: 3.6113s                                                 
  Shape:    3,696 x 27,998 (Unchanged)                              
                                                                    
  CHANGES DETECTED                                                  
  ────────────────                                                  
   OBS    control_point_umap (bool)                            
 inlier_prob_umap (float)                             
 obs_vf_angle_umap (float)                            
                                                                    
   UNS    VecFld_umap                                          
                                                                    
   OBSM   X_umap_SparseVFC (array, 3696x3)                     
 velocity_umap_SparseVFC (array, 3696x3)              
                                                                    
╰────────────────────────────────────────────────────────────────────╯
|-----> method arg is None, choosing methods automatically...
|-----------> method kd_tree selected

╭─ SUMMARY: topography ──────────────────────────────────────────────╮
  Duration: 0.066s                                                  
  Shape:    3,696 x 27,998 (Unchanged)                              
                                                                    
  CHANGES DETECTED                                                  
  ────────────────                                                  
╰────────────────────────────────────────────────────────────────────╯
|-----------> plotting with basis key=X_umap
|-----------> skip filtering clusters by stack threshold when stacking color because it is not a numeric type

╭─ EXPLANATION: FIXED POINTS ────────────────────────────────────────╮
  What it measures:
  Fixed points are steady states of the dynamical system where dx/dt = 0.
  Full circles are stable/unstable fixed points; half-circles are saddle points.
                                                                    
  Interpreting values:
   Absorbing / attractors (black, filled): Stable terminal cell    
    types — all trajectories                                        
    converge here (e.g.,                                            
    melanophore, iridophore,                                        
    xanthophore, unknown                                            
    terminal states)                                                
   Emitting / repellers (red, filled): Progenitor/source states —  
    cells spontaneously                                             
    differentiate outward from                                      
    these points (e.g., the                                         
    multipotent pigment                                             
    progenitor hub)                                                 
   Saddles / unstable (blue, half-filled): Bifurcation/decision    
    points — stable in some                                         
    directions, unstable in                                         
    others; mark where cell                                         
    fates diverge into                                              
    different lineages                                              
    (e.g., iridophore vs.                                           
    melanophore split,                                              
    neuron vs. satellite                                            
    glia split)                                                     
   Confidence (color intensity): Brighter color = fixed point is   
    closer to observed cells and                                    
    therefore more confident                                        
   Biological meaning: Together these fixed points map the full    
    cellular landscape: sources (progenitors),                      
    sinks (terminal states), and the decision                       
    hubs between them                                               
╰────────────────────────────────────────────────────────────────────╯
<Axes3D: title={'center': 'clusters'}, xlabel='umap_1', ylabel='umap_2', zlabel='umap_3'>
../../_images/638fcfb0c9781faef5fea4b99f8bf520781d89e9fd5d374f2f01a5782d633ad6.png
pl = dyn.pl.topography_3D(
    adata, basis='umap', color=[celltype_key], plot_method='pv', save_show_or_return='return'
)
show_pv_both(pl)
|-----------> plotting with basis key=X_umap
|-----------> skip filtering clusters by stack threshold when stacking color because it is not a numeric type

╭─ EXPLANATION: FIXED POINTS ────────────────────────────────────────╮
  What it measures:
  Fixed points are steady states of the dynamical system where dx/dt = 0.
  Full circles are stable/unstable fixed points; half-circles are saddle points.
                                                                    
  Interpreting values:
   Absorbing / attractors (black, filled): Stable terminal cell    
    types — all trajectories                                        
    converge here (e.g.,                                            
    melanophore, iridophore,                                        
    xanthophore, unknown                                            
    terminal states)                                                
   Emitting / repellers (red, filled): Progenitor/source states —  
    cells spontaneously                                             
    differentiate outward from                                      
    these points (e.g., the                                         
    multipotent pigment                                             
    progenitor hub)                                                 
   Saddles / unstable (blue, half-filled): Bifurcation/decision    
    points — stable in some                                         
    directions, unstable in                                         
    others; mark where cell                                         
    fates diverge into                                              
    different lineages                                              
    (e.g., iridophore vs.                                           
    melanophore split,                                              
    neuron vs. satellite                                            
    glia split)                                                     
   Confidence (color intensity): Brighter color = fixed point is   
    closer to observed cells and                                    
    therefore more confident                                        
   Biological meaning: Together these fixed points map the full    
    cellular landscape: sources (progenitors),                      
    sinks (terminal states), and the decision                       
    hubs between them                                               
╰────────────────────────────────────────────────────────────────────╯
../../_images/93836846f7c46598322741126b7c83ecfb789a1a4f3dee2f0874db0adaf9b240.png

9. Fate streamtubes and animation

Fate streamtubes (plot_3d_streamtube, Plotly) and 3D animation (dyn.mv.PyvistaAnim):

dyn.pl.plot_3d_streamtube(adata, color=celltype_key, layer='X', group=celltype_key,
                          init_group='Ductal', basis='umap')

dyn.pd.fate(adata, basis='umap', init_cells=adata.obs_names[:50])
dyn.mv.PyvistaAnim(adata, basis='umap', filename='fate_animation.gif').animate()

Summary

  • The 3D velocity arrows follow the same Ductal → endocrine direction as the 2D streamlines.

  • Each 3D plot is shown as a static image (GitHub) and an interactive Plotly/PyVista scene (nbviewer / readthedocs), plus a rotating GIF of the velocity field.