一、環境准備:
搭建 Python 高效開發環境: Pycharm + Anaconda
二、安裝 scanpy
pip install scanpy
三、AnnData
1、AnnData 介紹與結構
AnnData 是用於存儲數據的對象,一般作為 scanpy 的數據存儲格式。

主要由以下幾部分構成:
| 功能 | 數據類型 | |
|---|---|---|
| adata.X | 矩陣數據 | numpy,scipy sparse,matrix |
| adata.obs | 觀察值數據 | pandas dataframe |
| adata.var | 特征和高可變基因數據 | pandas dataframe |
| adata.uns | 非結構化數據 | dict |
下面我們動手構建一個用於創建 AnnoData 的虛擬數據
import numpy as np
import pandas as pd
import anndata as ad
from string import ascii_uppercase
# 設置觀測值數量
n_obs = 1000
# 生成觀察時間
obs = pd.DataFrame()
obs['time'] = np.random.choice(['day 1', 'day 2', 'day 4', 'day 8'], n_obs)
# 設置特征名
var_names = [i*letter for i in range(1, 10) for letter in ascii_uppercase]
# 特征數量
n_vars = len(var_names)
# 特征注釋數據框
var = pd.DataFrame(index=var_names)
# 生成數據矩陣
X = np.arange(n_obs*n_vars).reshape(n_obs, n_vars)
2、AnnoData 初始化
# 初始化 AnnoData 對象
# AnnoData 對象默認使用數據類型為 `float32`, 可以更精確的存儲數據
# 這里設置為整數,為了演示方便
adata = ad.AnnData(X, obs=obs, var=var, dtype='int32')
# 一般默認將變量或特征存儲在數據框的行
# 查看數據
print(adata)
3、AnnoData 切片特性
可以看到 AnnData 具有和 dataframe 或 Array 相似的長相,同樣具備相似的特性,比如切片:
# 通過切片查看觀測值和變量
print(adata.obs_names[:10].tolist())
print(adata.obs_names[-10:].tolist())
print(adata.var_names[:10].tolist())
print(adata.var_names[-10:].tolist())
# 查看矩陣
print(X)

3、AnnoData 的 view 特性
AnnoData 可以實現與 numpy 中的 view 相似的功能。
換句話說就是,我們每次操作 AnnoData 時,並不是再新建一個 AnnoData 來存儲數據,而是直接找到已經之前初始化好的 AnnoData 的內存地址,通過內存地址來直接改變 AnnoData 的值。這樣做的好處是:
- 無需分配多余的內存
- 可以直接修改已經初始化后的 AnnoData 對象
view 可以使用 .copy() 來得到 AnnoData 對象。
# 查看 'A' 列的頭三個元素
print(adata[:3, 'A'].X)
# 設置 'A' 列的頭三個元素
adata[:3, 'A'].X = [0, 0, 0]
# 查看 'A' 列的頭五個元素
print(adata[:5, 'A'].X)
其實我們在調用 .[] 時,AnnoData已經在內部實現了該操作,也就是說該 view 會成為保存數據的 AnnoData 對象。
但是,如果將 AnnoData 對象的 view 中的一部分賦值,該內容會復制一份並生成新的數據存儲對象。
adata_subset = adata[:5, ['A', 'B']]
print(adata_subset)
adata_subset.obs['foo'] = range(5)
可以看到,這時賦值會直接將 AnnoData 對象復制一份。現在 adata_subset 會重新得到一塊內存用於存儲實際數據,而不再僅僅是對 adata 的內存地址引用。
4、備份到本地
# 計算對象大小的函數
def print_size_in_MB(x):
print('{:.3} MB'.format(x.__sizeof__()/1e6))
# 查看 adata 對象大小
print_size_in_MB(adata)
# 查看是否備份
adata.isbacked
# 設置備份地址
adata.filename = './write/test.h5ad'
# 查看是否備份成功
adata.isbacked
可以看到,我們的 adata 對象已經備份成功,而且就在本地 './write/test.h5ad' 目錄。
前邊提到的 view 特性在這里同樣適用,我們來看看 adata_subset 是否備份成功。
adata_subset.isbacked
adata_subset.filename = './write/adata_subset_test.h5ad'
adata_subset.isbacked

adata_subset 並沒有被啟用備份模式,重新設置備份模式。
需要注意的是:備份僅影響數據矩陣 X,所有注釋信息都保留在內存中。如果想對全部數據的更改保存,則必須將導出到本地。
5、導出到本地
adata.write("./write/my_results.h5ad")
adata.write_csvs('./write/my_results_csvs', )
6、讀取數據
import scanpy as sc
import pandas as pd
# 初始化數據
adata = sc.read(filename)
# 加入數據
anno = pd.read_csv(filename_sample_annotation)
# 加入樣本分組信息
adata.obs['cell_groups'] = anno['cell_groups'] # categorical annotation of type pandas.Categorical
# 加入時間信息
adata.obs['time'] = anno['time'] # numerical annotation of type float
# 甚至可以直接賦值 dataframe
adata.obs = anno




