基于Scanpy的AnnData对象obs索引匹配标签赋值及报错修复
问题:AnnData对象obs与anno索引匹配赋值时触发KeyError并实现需求
原代码
import pandas as pd import numpy as np import os import scanpy as sc for subdir, dirs, files in os.walk(directory_path): for file in files: if file.endswith("_ccRCC.h5"): file_path = os.path.join(subdir, file) id = file.split("_")[0] dfs[id] = sc.read_10x_h5(file_path, genome="GRCh38", backup_url=None) dfs[id].var_names_make_unique() results_file[id] = id + ".h5ad" for i in dfs: ids = anno.index.str.extract(r'([^_]*)$')[0].tolist() if any(j in dfs[i].obs.index.map(str).tolist() for j in ids): for k in ids: dfs[i].obs = dfs[i].obs.join(anno.set_index(k)).dropna()
报错信息
--------------------------------------------------------------------------- KeyError Traceback (most recent call last) Input In [124], in <cell line: 1>() 3 if any(j in dfs[i].obs.index.map(str).tolist() for j in ids): 4 for k in ids: ----> 5 dfs[i].obs = dfs[i].obs.join(anno.set_index(k)).dropna() File ~/.local/lib/python3.9/site-packages/pandas/core/frame.py:5859, in DataFrame.set_index(self, keys, drop, append, inplace, verify_integrity) 5856 missing.append(col) 5858 if missing: -> 5859 raise KeyError(f"None of {missing} are in the columns") 5861 if inplace: 5862 frame = self KeyError: "None of ['AAACCTGCAAGTAGTA-1'] are in the columns"
输入数据
- dfs结构
{'GSM4819737': AnnData object with n_obs × n_vars = 1916 × 2252 obs: 'n_genes', 'n_genes_by_counts', 'total_counts', 'total_counts_mt', 'pct_counts_mt', 'leiden' var: 'gene_ids', 'n_cells', 'mt', 'n_cells_by_counts', 'mean_counts', 'pct_dropout_by_counts', 'total_counts', 'highly_variable', 'means', 'dispersions', 'dispersions_norm', 'mean', 'std' uns: 'hvg', 'leiden', 'leiden_colors', 'leiden_sizes', 'log1p', 'neighbors', 'paga', 'pca', 'rank_genes_groups', 'umap' obsm: 'X_pca', 'X_umap' varm: 'PCs' obsp: 'connectivities', 'distances', 'GSM4819735': AnnData object with n_obs × n_vars = 713 × 2619 obs: 'n_genes', 'n_genes_by_counts', 'total_counts', 'total_counts_mt', 'pct_counts_mt', 'leiden' var: 'gene_ids', 'n_cells', 'mt', 'n_cells_by_counts', 'mean_counts', 'pct_dropout_by_counts', 'total_counts', 'highly_variable', 'means', 'dispersions', 'dispersions_norm', 'mean', 'std' uns: 'hvg', 'leiden', 'leiden_colors', 'leiden_sizes', 'log1p', 'neighbors', 'paga', 'pca', 'rank_genes_groups', 'umap' obsm: 'X_pca', 'X_umap' varm: 'PCs' obsp: 'connectivities', 'distances'}
- dfs["GSM4819737"].obs前两行
pd.DataFrame({'n_genes': {'AAACCTGGTAAATGTG-1': 2261, 'AAACCTGCAAGTAGTA-1': 2497}, 'n_genes_by_counts': {'AAACCTGGTAAATGTG-1': 2261, 'AAACCTGCAAGTAGTA-1': 2497}, 'total_counts': {'AAACCTGGTAAATGTG-1': 5744.0, 'AAACCTGCAAGTAGTA-1': 10502.0}, 'total_counts_mt': {'AAACCTGGTAAATGTG-1': 146.0, 'AAACCTGCAAGTAGTA-1': 18.0}, 'pct_counts_mt': {'AAACCTGGTAAATGTG-1': 2.5417826175689697, 'AAACCTGCAAGTAGTA-1': 0.1713959276676178}, 'leiden': {'AAACCTGGTAAATGTG-1': '2', 'AAACCTGCAAGTAGTA-1': '1'}})
- anno前两行最后两列
pd.DataFrame({'label': {'SI_18854_AAACCTGCAAGTAGTA-1': '1:Tumor', 'SI_18854_AAACCTGTCCACTGGG-1': '1:Tumor'}, 'patient': {'SI_18854_AAACCTGCAAGTAGTA-1': 'SS_2005', 'SI_18854_AAACCTGTCCACTGGG-1': 'SS_2005'}})
期望输出
pd.DataFrame({'n_genes': {'AAACCTGCAAGTAGTA-1': 2497}, 'n_genes_by_counts': {'AAACCTGCAAGTAGTA-1': 2497}, 'total_counts': {'AAACCTGCAAGTAGTA-1': 10502.0}, 'total_counts_mt': {'AAACCTGCAAGTAGTA-1': 18.0}, 'pct_counts_mt': {'AAACCTGCAAGTAGTA-1': 0.1713959276676178}, 'leiden': {'AAACCTGCAAGTAGTA-1': '1'}, 'label': {'AAACCTGCAAGTAGTA-1': '1:Tumor'}})
解决方案
报错原因
原代码中anno.set_index(k)逻辑错误:k是从anno索引提取的子串(如AAACCTGCAAGTAGTA-1),但anno的列中不存在该名称,因此触发KeyError。
修正代码
import pandas as pd import numpy as np import os import scanpy as sc # 读取数据部分保持不变 for subdir, dirs, files in os.walk(directory_path): for file in files: if file.endswith("_ccRCC.h5"): file_path = os.path.join(subdir, file) id = file.split("_")[0] dfs[id] = sc.read_10x_h5(file_path, genome="GRCh38", backup_url=None) dfs[id].var_names_make_unique() results_file[id] = id + ".h5ad" # 预处理anno:提取索引最后一个下划线后的子串作为匹配用的cell_id列 anno['cell_id'] = anno.index.str.split('_').str[-1] # 遍历每个AnnData对象完成匹配赋值 for sample_id in dfs: adata = dfs[sample_id] # 将obs的索引转为列,方便后续合并 adata.obs = adata.obs.reset_index().rename(columns={'index': 'cell_id'}) # 内连接合并,自动过滤无匹配的行,仅保留需要的label列 merged_obs = pd.merge(adata.obs, anno[['cell_id', 'label']], on='cell_id', how='inner') # 恢复obs的索引结构为cell_id merged_obs = merged_obs.set_index('cell_id') # 更新AnnData的obs数据 adata.obs = merged_obs # 同步更新AnnData的观测数(可选,替换obs后会自动更新,显式处理更稳妥) adata._n_obs = len(merged_obs)
代码说明
- 预处理anno:新增
cell_id列存储索引最后一个下划线后的子串,作为匹配的键。 - 合并匹配:将obs的索引转为列后,用内连接(
how='inner')自动过滤无匹配的行,同时关联anno的label列。 - 恢复结构:合并完成后重新设置obs的索引为
cell_id,保证AnnData的结构符合后续分析要求。
内容的提问来源于stack exchange,提问作者Anon
相关产品推荐
相关产品推荐

