docs: def create_from_cellranger(indir: str, outdir: str = None, genome: str = None) -> str:
  	"""
  	Create a .loom file from 10X Genomics cellranger output
  
  	Args:
  		indir (str):	path to the cellranger output folder (the one that contains 'outs')
  		outdir (str):	output folder wher the new loom file should be saved (default to indir)
  		genome (str):	genome build to load (e.g. 'mm10'; if None, determine species from outs folder)
  
  	Returns:
  		path (str):		Full path to the created loom file.
  
  	Remarks:
  		The resulting file will be named ``{sampleID}.loom``, where the sampleID is the one given by cellranger.
  	"""
  	if outdir is None:
  		outdir = indir
  	sampleid = os.path.split(os.path.abspath(indir))[-1]
  	matrix_folder = os.path.join(indir, 'outs', 'filtered_gene_bc_matrices')
  	if os.path.exists(matrix_folder):
  		if genome is None:
  			genome = [f for f in os.listdir(matrix_folder) if not f.startswith(".")][0]
  		matrix_folder = os.path.join(matrix_folder, genome)
  		matrix = mmread(os.path.join(matrix_folder, "matrix.mtx")).astype("float32").todense()
  		genelines = open(os.path.join(matrix_folder, "genes.tsv"), "r").readlines()
  		bclines = open(os.path.join(matrix_folder, "barcodes.tsv"), "r").readlines()
  	else: # cellranger V3 file locations
  		if genome is None:
  			genome = "" # Genome is not visible from V3 folder
  		matrix_folder = os.path.join(indir, 'outs', 'filtered_feature_bc_matrix')
  		matrix = mmread(os.path.join(matrix_folder, "matrix.mtx.gz")).astype("float32").todense()
  		genelines = [ l.decode() for l in gzip.open(os.path.join(matrix_folder, "features.tsv.gz"), "r").readlines() ]
  		bclines = [ l.decode() for l in gzip.open(os.path.join(matrix_folder, "barcodes.tsv.gz"), "r").readlines() ]
  
  	accession = np.array([x.split("\t")[0] for x in genelines]).astype("str")
  	gene = np.array([x.split("\t")[1].strip() for x in genelines]).astype("str")
  	cellids = np.array([sampleid + ":" + x.strip() for x in bclines]).astype("str")
  
  	col_attrs = {"CellID": cellids}
  	row_attrs = {"Accession": accession, "Gene": gene}
  
  	tsne_file = os.path.join(indir, "outs", "analysis", "tsne", "projection.csv")
  	# In cellranger V2 the file moved one level deeper
  	if not os.path.exists(tsne_file):
  		tsne_file = os.path.join(indir, "outs", "analysis", "tsne", "2_components", "projection.csv")
  	if os.path.exists(tsne_file):
  		tsne = np.loadtxt(tsne_file, usecols=(1, 2), delimiter=',', skiprows=1)
  		col_attrs["X"] = tsne[:, 0].astype('float32')
  		col_attrs["Y"] = tsne[:, 1].astype('float32')
  
  	clusters_file = os.path.join(indir, "outs", "analysis", "clustering", "graphclust", "clusters.csv")
  	if os.path.exists(clusters_file):
  		labels = np.loadtxt(clusters_file, usecols=(1, ), delimiter=',', skiprows=1)
  		col_attrs["ClusterID"] = labels.astype('int') - 1
  
  	path = os.path.join(outdir, sampleid + ".loom")
  	create(path, matrix, row_attrs, col_attrs, file_attrs={"Genome": genome})
  	return path