UMAP已升级,并且v0.4已发布。
从
2020/02/10开始,您可以使用
看来您可以按原样输入稀疏矩阵,并且已经添加了各种函数,但是在这里,我将尝试绘图函数,嵌入非欧几里得空间并进行逆转换。
仅更改数据,并且文档中编写的代码示例几乎不变,因此有关每个示例的详细信息,请转到UMAP文档。
数据
在PARC存储库中使用scRNA-seq数据集和注释进行实验(Zheng等,2017,10X PBMC)。 68,579个单元格,使用PCA预压缩到50个尺寸。数据太大了,不能随便做,因此将其减少到约10,000个单元并使用。
1 2 3 | import numpy as np import pandas as pd import umap |
读取数据。 10,000个细胞,50个主要成分。
1 2 | dat = np.loadtxt('./data/pca50_pbmc10k.txt', delimiter=',') dat.shape |
1 | (10000, 50) |
批注如下所示。
1 2 3 4 5 6 7 | labels = [] for line in open('./data/zheng17_annotations_10k.txt'): labels.append(line.rstrip()) import collections import pprint pprint.PrettyPrinter(indent=4).pprint(collections.Counter(labels)) |
1 2 3 4 5 6 7 8 9 10 11 | Counter({ 'CD4+/CD45RA+/CD25- Naive T': 1853, 'CD8+ Cytotoxic T': 1694, 'CD8+/CD45RA+ Naive Cytotoxic': 1599, 'CD4+/CD45RO+ Memory': 1425, 'CD56+ NK': 1211, 'CD4+/CD25 T Reg': 828, 'CD14+ Monocyte': 595, 'CD19+ B': 583, 'Dendritic': 127, 'CD4+ T Helper2': 55, 'CD34+': 30}) |
暂时,UMAP正常。
1 2 | model = umap.UMAP(verbose=True) model.fit(dat) |
绘图功能
umap.plot现在可以单独使用umap进行绘图。
与使用matplotlib进行绘图几乎相同,但是很容易。
请注意,使用
umap.plot时,需要与umap分开使用datashader,bokeh和holoview。安装每个。
1 | import umap.plot |
如果提供训练模型的实例,它将绘制散点图。
1 | umap.plot.points(model) |
另外,如果将每个点的标签数据赋予
1 | umap.plot.points(model, labels=labels) |
要提供的数据可以是连续值。由于此处没有合适的数据,因此将给出原始数据的平均值。
准备了背景图和颜色图的组合,因此可以通过使用
有9种主题可供选择。
1 | umap.plot._themes.keys() |
1 | dict_keys(['fire', 'viridis', 'inferno', 'blue', 'red', 'green', 'darkblue', 'darkred', 'darkgreen']) |
1 2 3 | umap.plot.points(model, values=dat.mean(axis=1), theme='viridis') |
您也可以使用Bokeh绘制交互式绘图。
1 | umap.plot.output_notebook() |
预先以
1 2 3 4 | df_labels = pd.DataFrame(labels, columns=['celltype']) p = umap.plot.interactive(model, labels=labels, hover_data=df_labels, point_size=2) umap.plot.show(p) |
另外,有一个功能可以可视化嵌入UMAP时使用的邻域图。边缘权重也以等级显示。验证已学习了哪些连通性时可能很有用。
1 2 | umap.plot.connectivity(model, show_points=True, edge_cmap='viridis') |
另外,似乎还有一个诊断绘图功能,用于诊断带有各种指示器的嵌入。
嵌入非欧几里德空间
默认情况下,UMAP嵌入在欧几里德空间中(为目标优化了低维空间中的欧几里得距离),但是它似乎可以嵌入到其他类型的空间中,例如球体。
通过使用
首先,尝试将其嵌入球形表面。对于球形嵌入,请指定Haversine公式。
1 2 | sphere_mapper = umap.UMAP(output_metric='haversine') sphere_mapper.fit(dat) |
生成的坐标以球坐标系显示,因此我不确定是否按原样绘制它们。因此,根据教科书将其转换为笛卡尔坐标系,然后进行绘制。
1 2 3 | x = np.sin(sphere_mapper.embedding_[:, 0]) * np.cos(sphere_mapper.embedding_[:, 1]) y = np.sin(sphere_mapper.embedding_[:, 0]) * np.sin(sphere_mapper.embedding_[:, 1]) z = np.cos(sphere_mapper.embedding_[:, 0]) |
该图似乎无法单独工作,因此我用matplotlib自己绘制了它。
1 2 3 4 5 6 7 8 9 10 11 | import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D import seaborn as sns sns.set(style='white') categories = sorted(list(set(labels))) label_ids = [categories.index(l) for l in labels] fig = plt.figure(figsize=(12, 12)) ax = fig.add_subplot(111, projection='3d') ax.scatter(x, y, z, c=label_ids, cmap='Spectral') |
每种细胞类型都组织在球形表面上。
在3D中很难理解,因此我们将其扩展为2D。
1 2 3 4 | x = np.arctan2(x, y) y = np.arccos(z) fig = plt.figure(figsize=(12, 12)) plt.scatter(x, y, c=label_ids, cmap='Spectral') |
左侧和右侧最初已连接。
我不确定何时使用它,但是当数据具有固有的周期性特性时,它是否有效?
另外,还介绍了在双曲空间中的嵌入。我对此不太确定,因此我将仅遵循文档的流程。看来Poincare磁盘模型本身很难优化,因此似乎是通过针对双曲面模型进行学习。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | hyperbolic_mapper = umap.UMAP(output_metric='hyperboloid') hyperbolic_mapper.fit(dat) x = hyperbolic_mapper.embedding_[:, 0] y = hyperbolic_mapper.embedding_[:, 1] z = np.sqrt(1 + np.sum(hyperbolic_mapper.embedding_**2, axis=1)) disk_x = x / (1 + z) disk_y = y / (1 + z) fig = plt.figure(figsize=(12,12)) ax = fig.add_subplot(111) ax.scatter(disk_x, disk_y, c=label_ids, cmap='Spectral') boundary = plt.Circle((0,0), 1, fc='none', ec='k') ax.add_artist(boundary) ax.axis('off'); |
逆转换
一种从已学习嵌入的低维一侧的坐标生成相应高维样本的方法。
可能难以像VAE这样的生成模型那样使用,但快速了解嵌入式低维度是什么可能很有用。
对于高维样本生成,单细胞数据不是很有趣,因此在这里我将尝试使用Kuzushiji-MNIST数据(随机选择10,000个)。
1 2 | dat = np.load('./data/kmnist-train-imgs_10k.npy') dat.shape |
1 | (10000, 784) |
1 2 | labels = np.load('./data/kmnist-train-labels_10k.npy') labels |
1 | array([4, 5, 0, ..., 6, 9, 0], dtype=uint8) |
首先,尝试正常计算UMAP。
1 2 3 | model = umap.UMAP(n_epochs=500, verbose=True).fit(dat) umap.plot.points(model, labels=labels) |
我想通过从该空间进行插值来对似乎有趣的区域进行逆变换,所以
选择聚类8左上,聚类9的左下,聚类0的右上和聚类3的右下附近的点,并进行100个测试点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | x = model.embedding_ top_left = x[labels == 8, :][x[labels == 8, 0].argmin()] btm_left = x[labels == 9, :][x[labels == 9, 1].argmin()] top_right = x[labels == 0, :][x[labels == 0, 0].argmax()] btm_right = x[labels == 3, :][x[labels == 3, 1].argmin()] test_pts = np.array([ (top_left*(1-x) + top_right*x)*(1-y) + (btm_left*(1-x) + btm_right*x)*y for y in np.linspace(0, 1, 10) for x in np.linspace(0, 1, 10) ]) print(top_left) print(btm_left) print(top_right) print(btm_right) |
1 2 3 4 | [-3.0056033 9.982167 ] [2.0912035 2.2021403] [14.147088 10.8581085] [10.669375 2.6710818] |
执行反向转换。为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | inv_transformed_points = model.inverse_transform(test_pts) from matplotlib.gridspec import GridSpec fig = plt.figure(figsize=(12,6)) gs = GridSpec(10, 20, fig) scatter_ax = fig.add_subplot(gs[:, :10]) kuzushiji_axes = np.zeros((10, 10), dtype=object) for i in range(10): for j in range(10): kuzushiji_axes[i, j] = fig.add_subplot(gs[i, 10 + j]) scatter_ax.scatter(model.embedding_[:, 0], model.embedding_[:, 1], c=labels.astype(np.int32), cmap='Spectral', s=0.1) scatter_ax.set(xticks=[], yticks=[]) scatter_ax.scatter(test_pts[:, 0], test_pts[:, 1], marker='x', c='k', s=15) for i in range(10): for j in range(10): kuzushiji_axes[i, j].imshow(inv_transformed_points[i*10 + j].reshape(28, 28), cmap='viridis') kuzushiji_axes[i, j].set(xticks=[], yticks=[]) |
有点粗糙,但是我可以猜出它是如何沿轴过渡的,以及为什么簇如此靠近。