当前位置:首页>python>完整的单细胞RNA-seq分析流程 python-GPU的单细胞空间转录组学

完整的单细胞RNA-seq分析流程 python-GPU的单细胞空间转录组学

  • 2026-01-18 04:56:13
完整的单细胞RNA-seq分析流程 python-GPU的单细胞空间转录组学
完整的单细胞RNA-seq分析流程  python-GPU的单细胞空间转录组学
"""完整的单细胞RNA-seq分析流程 (Python版)功能特性: 智能断点续跑 - 自动检测已完成步骤 GPU加速支持 - 自动检测并使用GPU 多种拟时序方法 - PAGA → DPT → 跳过 完整日志记录 - 详细记录每步进度 结果汇总报告 - 生成分析总结作者:itwangyang版本:v2.0日期:2026-01-11"""import osimport sysimport pickleimport loggingfrom datetime import datetimefrom pathlib import Pathimport warningswarnings.filterwarnings('ignore')import numpy as npimport pandas as pdimport matplotlib.pyplot as pltimport seaborn as sns# 单细胞分析核心库import scanpy as scimport anndata as ad# GPU支持检测try:    import rapids_singlecell as rsc    GPU_AVAILABLE = True    print("✓ 检测到GPU支持 - 将使用RAPIDS加速")except ImportError:    GPU_AVAILABLE = False    print("ℹ GPU支持未安装 - 使用CPU模式")    print("ℹ 安装GPU加速: pip install cupy-cuda11x rapids-singlecell")    print("ℹ 注意: 需要NVIDIA GPU和CUDA环境\n")# 可选依赖try:    import celltypist    CELLTYPIST_AVAILABLE = Trueexcept ImportError:    CELLTYPIST_AVAILABLE = False# 设置sc.settings.verbosity = 1sc.settings.set_figure_params(dpi=100, dpi_save=300                              frameon=False, figsize=(66),                              fontsize=12)#######################  配置参数  #######################class Config:    """分析配置参数"""    # 路径设置    WORK_DIR = "C:\\Users\\wangyang\\Desktop\\ST\\03Seurat"  # 修改为你的路径    # 阈值参数    LOG_FC_FILTER = 1.0    ADJ_PVAL_FILTER = 0.05    # 质控参数    MIN_GENES = 200    MIN_CELLS = 5    MAX_MT_PERCENT = 15    MIN_GENES_AFTER_QC = 100    # 降维参数    N_PCS = 20    N_NEIGHBORS = 15    # 聚类参数    RESOLUTION = 0.6    # 高变基因数    N_TOP_GENES = 1500    # 是否强制重新运行    FORCE_RERUN = False    # 是否使用GPU    USE_GPU = GPU_AVAILABLE#######################  辅助函数  #######################class AnalysisLogger:    """分析日志管理器"""    def __init__(self, log_file="analysis_progress.log"):        self.log_file = log_file        self.completed_steps = set()        self.checkpoint_file = "analysis_checkpoint.pkl"        # 配置日志        logging.basicConfig(            level=logging.INFO,            format='[%(asctime)s] [%(levelname)s] %(message)s',            handlers=[                logging.FileHandler(log_file, encoding='utf-8'),                logging.StreamHandler(sys.stdout)            ]        )        self.logger = logging.getLogger(__name__)        # 加载检查点        self.load_checkpoint()    def log(self, message, level="INFO"):        """记录日志"""        if level == "INFO":            self.logger.info(message)        elif level == "SUCCESS":            self.logger.info(f"✓ {message}")        elif level == "WARN":            self.logger.warning(message)        elif level == "ERROR":            self.logger.error(message)        elif level == "SKIP":            self.logger.info(f"⊘ {message}")    def load_checkpoint(self):        """加载检查点"""        if os.path.exists(self.checkpoint_file):            with open(self.checkpoint_file, 'rb'as f:                self.completed_steps = pickle.load(f)            self.log("从检查点恢复分析""INFO")        else:            self.log("开始新的分析""INFO")    def save_checkpoint(self):        """保存检查点"""        with open(self.checkpoint_file, 'wb'as f:            pickle.dump(self.completed_steps, f)    def is_completed(self, step_name):        """检查步骤是否完成"""        if Config.FORCE_RERUN:            return False        return step_name in self.completed_steps    def mark_completed(self, step_name):        """标记步骤完成"""        self.completed_steps.add(step_name)        self.save_checkpoint()        self.log(f"{step_name} 已完成""SUCCESS")def check_files_exist(files):    """检查文件是否存在"""    return all(os.path.exists(f) for f in files)def print_banner(title):    """打印步骤横幅"""    print("\n" + "=" * 64)    print(f"  {title}")    print("=" * 64)def save_figure(fig, filename):    """保存图片到当前工作目录"""    try:        filepath = os.path.join(os.getcwd(), filename)        fig.savefig(filepath, bbox_inches='tight', dpi=300)        print(f"  → 图片已保存: {filepath}")        plt.close(fig)    except Exception as e:        print(f"  ✗ 保存图片失败 {filename}{str(e)}")        plt.close(fig)#######################  主分析流程  #######################class SeuratAnalysisPipeline:    """单细胞分析主流程"""    def __init__(self, config=Config):        self.config = config        self.logger = AnalysisLogger()        self.adata = None        # 切换工作目录        os.chdir(config.WORK_DIR)        self.logger.log(f"工作目录: {os.getcwd()}""INFO")        # 打印欢迎信息        self._print_welcome()    def _print_welcome(self):        """打印欢迎信息"""        print("\n" + "╔" + "=" * 62 + "╗")        print("║" + " " * 10 + "Scanpy单细胞RNA-seq完整分析流程 v2.0" + " " * 15 + "║")        print("║" + " " * 62 + "║")        print("║  功能: 从数据加载到差异分析和拟时序的完整流程" + " " * 16 + "║")        print("║  特性: 智能断点续跑 + GPU加速 + 详细日志" + " " * 21 + "║")        print("╚" + "=" * 62 + "╝\n")    def _list_generated_files(self, filenames):        """列出生成的文件"""        print("\n  生成的文件:")        for fname in filenames:            fpath = os.path.join(os.getcwd(), fname)            if os.path.exists(fpath):                size_mb = os.path.getsize(fpath) / 1024 / 1024                print(f"    ✓ {fname} ({size_mb:.2f} MB)")            else:                print(f"    ✗ {fname} (未找到)")        print()    def run(self):        """运行完整分析流程"""        try:            self.step01_load_and_qc()            self.step02_pca_and_harmony()            self.step03_clustering()            self.step04_cell_annotation()            self.step05_differential_analysis()            self.step06_trajectory_analysis()            self.generate_summary_report()        except Exception as e:            self.logger.log(f"分析出错: {str(e)}""ERROR")            raise    #######################  STEP 01  #######################    def step01_load_and_qc(self):        """步骤01:数据加载和质量控制"""        step_files = [            "01.featureViolin.pdf",            "01.featureCor.pdf",            "01.featureVar.pdf",            "step01_adata.h5ad"        ]        if self.logger.is_completed("step01"and check_files_exist(step_files):            self.logger.log("步骤01已完成,跳过""SKIP")            self.adata = sc.read_h5ad("step01_adata.h5ad")            return        print_banner("STEP 01: 数据加载和质量控制")        self.logger.log("开始步骤01:数据加载和质控""INFO")        # 读取10X数据        self.logger.log("读取10X Genomics数据...""INFO")        data_dirs = [d for d in Path(self.config.WORK_DIR).iterdir()                     if d.is_dir() and d.name != '__pycache__']        adatas = []        for data_dir in data_dirs:            try:                sample_name = data_dir.name                adata_sample = sc.read_10x_mtx(                    data_dir,                    var_names='gene_symbols',                    cache=True                )                adata_sample.obs['sample'] = sample_name                adatas.append(adata_sample)                self.logger.log(f"  加载样本 {sample_name}{adata_sample.n_obs} cells""INFO")            except Exception as e:                self.logger.log(f"  跳过 {data_dir.name}{str(e)}""WARN")        # 合并数据        self.adata = ad.concat(adatas, label="batch", keys=[a.obs['sample'][0for a in adatas])        self.logger.log(f"初始数据 - 细胞数: {self.adata.n_obs}, 基因数: {self.adata.n_vars}""INFO")        # 计算QC指标        self.adata.var['mt'] = self.adata.var_names.str.startswith('MT-')        sc.pp.calculate_qc_metrics(            self.adata,             qc_vars=['mt'],             percent_top=None            log1p=False            inplace=True        )        # QC可视化(过滤前)        self.logger.log("生成质控图...""INFO")        fig, axes = plt.subplots(13, figsize=(154))        # 手动绘制violin plot避免版本兼容问题        import seaborn as sns        qc_metrics = ['n_genes_by_counts''total_counts''pct_counts_mt']        titles = ['Number of Genes''Total Counts''Mitochondrial %']        for idx, (metric, title) in enumerate(zip(qc_metrics, titles)):            data_to_plot = self.adata.obs[metric].values            axes[idx].violinplot([data_to_plot], positions=[0], showmeans=True, widths=0.7)            axes[idx].set_ylabel(title)            axes[idx].set_xticks([])            axes[idx].grid(axis='y', alpha=0.3)        plt.tight_layout()        save_figure(fig, "01.featureViolin.pdf")        # 质控过滤        n_cells_before = self.adata.n_obs        sc.pp.filter_cells(self.adata, min_genes=self.config.MIN_GENES_AFTER_QC)        sc.pp.filter_genes(self.adata, min_cells=self.config.MIN_CELLS)        self.adata = self.adata[self.adata.obs.pct_counts_mt < self.config.MAX_MT_PERCENT, :]        self.logger.log(f"质控后 - 细胞数: {self.adata.n_obs} (过滤掉 {n_cells_before - self.adata.n_obs})"                       "INFO")        # 特征相关性图        self.logger.log("生成特征相关性图...""INFO")        fig, axes = plt.subplots(12, figsize=(125))        # 手动绘制散点图        axes[0].scatter(self.adata.obs['total_counts'],                        self.adata.obs['n_genes_by_counts'],                       s=1, alpha=0.5, c='steelblue')        axes[0].set_xlabel('Total Counts')        axes[0].set_ylabel('Number of Genes')        axes[0].set_title('Feature Correlation')        axes[1].scatter(self.adata.obs['total_counts'],                        self.adata.obs['pct_counts_mt'],                       s=1, alpha=0.5, c='coral')        axes[1].set_xlabel('Total Counts')        axes[1].set_ylabel('Mitochondrial %')        axes[1].set_title('MT% vs Total Counts')        plt.tight_layout()        save_figure(fig, "01.featureCor.pdf")        # 数据标准化        self.logger.log("数据标准化...""INFO")        sc.pp.normalize_total(self.adata, target_sum=1e4)        sc.pp.log1p(self.adata)        # 识别高变基因        self.logger.log("识别高变基因...""INFO")        sc.pp.highly_variable_genes(            self.adata,             n_top_genes=self.config.N_TOP_GENES,            flavor='seurat'        )        top_genes = self.adata.var_names[self.adata.var.highly_variable][:10].tolist()        self.logger.log(f"Top 10高变基因: {', '.join(top_genes)}""INFO")        # 高变基因可视化        self.logger.log("生成高变基因图...""INFO")        fig, axes = plt.subplots(12, figsize=(145))        # 左图:所有基因的分散度        mean_expr = np.array(self.adata.var['means'])        dispersions = np.array(self.adata.var['dispersions'])        highly_variable = self.adata.var['highly_variable'].values        axes[0].scatter(mean_expr[~highly_variable], dispersions[~highly_variable],                        s=1, alpha=0.3, c='gray', label='Other')        axes[0].scatter(mean_expr[highly_variable], dispersions[highly_variable],                        s=2, alpha=0.7, c='red', label='Highly Variable')        axes[0].set_xlabel('Mean Expression')        axes[0].set_ylabel('Dispersion')        axes[0].set_title('Highly Variable Genes')        axes[0].legend()        axes[0].set_xscale('log')        axes[0].set_yscale('log')        # 右图:标注top10基因        axes[1].scatter(mean_expr[~highly_variable], dispersions[~highly_variable],                        s=1, alpha=0.3, c='gray')        axes[1].scatter(mean_expr[highly_variable], dispersions[highly_variable],                        s=2, alpha=0.7, c='red')        # 标注top10基因        for gene in top_genes:            if gene in self.adata.var_names:                idx = self.adata.var_names.get_loc(gene)                axes[1].annotate(gene,                                (mean_expr[idx], dispersions[idx]),                               fontsize=8, alpha=0.8)        axes[1].set_xlabel('Mean Expression')        axes[1].set_ylabel('Dispersion')        axes[1].set_title('Top 10 Highly Variable Genes')        axes[1].set_xscale('log')        axes[1].set_yscale('log')        plt.tight_layout()        save_figure(fig, "01.featureVar.pdf")        # 保存结果        self.adata.write("step01_adata.h5ad")        self.logger.mark_completed("step01")        # 列出生成的图片        self._list_generated_files(["01.featureViolin.pdf""01.featureCor.pdf""01.featureVar.pdf"])    #######################  STEP 02  #######################    def step02_pca_and_harmony(self):        """步骤02:PCA降维和Harmony批次校正"""        step_files = [            "02.pcaGene.pdf",            "02.PCA.pdf",            "02.pcaVariance.pdf",            "step02_adata.h5ad"        ]        if self.logger.is_completed("step02"and check_files_exist(step_files):            self.logger.log("步骤02已完成,跳过""SKIP")            self.adata = sc.read_h5ad("step02_adata.h5ad")            return        print_banner("STEP 02: PCA降维和Harmony批次校正")        self.logger.log("开始步骤02:PCA降维和批次校正""INFO")        if self.adata is None:            self.adata = sc.read_h5ad("step01_adata.h5ad")        # 数据缩放        self.logger.log("数据缩放...""INFO")        sc.pp.scale(self.adata, max_value=10)        # PCA降维        self.logger.log("执行PCA降维...""INFO")        if self.config.USE_GPU:            rsc.pp.pca(self.adata, n_comps=self.config.N_PCS)        else:            sc.tl.pca(self.adata, n_comps=self.config.N_PCS, svd_solver='arpack')        # Harmony批次校正        if len(self.adata.obs['batch'].unique()) > 1:            self.logger.log("Harmony批次校正...""INFO")            try:                import scanpy.external as sce                sce.pp.harmony_integrate(self.adata, 'batch', basis='X_pca', adjusted_basis='X_pca_harmony')                use_rep = 'X_pca_harmony'            except Exception as e:                self.logger.log(f"Harmony失败,使用原始PCA: {str(e)}""WARN")                use_rep = 'X_pca'        else:            use_rep = 'X_pca'        self.adata.uns['use_rep'] = use_rep        # PCA基因贡献图        self.logger.log("生成PCA基因贡献图...""INFO")        fig, axes = plt.subplots(22, figsize=(1210))        axes = axes.flatten()        for i, pc in enumerate([0123]):            loadings = self.adata.varm['PCs'][:, pc]            top_idx = np.argsort(np.abs(loadings))[-20:]            y_pos = np.arange(len(top_idx))            axes[i].barh(y_pos, loadings[top_idx])            axes[i].set_yticks(y_pos)            axes[i].set_yticklabels(self.adata.var_names[top_idx], fontsize=8)            axes[i].set_xlabel('Loading')            axes[i].set_title(f'PC{pc+1}')            axes[i].grid(axis='x', alpha=0.3)        plt.tight_layout()        save_figure(fig, "02.pcaGene.pdf")        # PCA散点图        self.logger.log("生成PCA散点图...""INFO")        fig, axes = plt.subplots(12, figsize=(125))        pca_coords = self.adata.obsm['X_pca']        colors = plt.cm.tab10(self.adata.obs['batch'].cat.codes)        axes[0].scatter(pca_coords[:, 0], pca_coords[:, 1],                        c=colors, s=1, alpha=0.5)        axes[0].set_xlabel('PC1')        axes[0].set_ylabel('PC2')        axes[0].set_title('PCA (PC1 vs PC2)')        axes[1].scatter(pca_coords[:, 2], pca_coords[:, 3],                        c=colors, s=1, alpha=0.5)        axes[1].set_xlabel('PC3')        axes[1].set_ylabel('PC4')        axes[1].set_title('PCA (PC3 vs PC4)')        # 添加图例        from matplotlib.patches import Patch        legend_elements = [Patch(facecolor=plt.cm.tab10(i), label=cat)                          for i, cat in enumerate(self.adata.obs['batch'].cat.categories)]        axes[0].legend(handles=legend_elements, loc='best', fontsize=8)        plt.tight_layout()        save_figure(fig, "02.PCA.pdf")        # 方差解释图        self.logger.log("生成方差解释图...""INFO")        fig, ax = plt.subplots(figsize=(105))        variance_ratio = self.adata.uns['pca']['variance_ratio']        x = np.arange(1len(variance_ratio) + 1)        ax.bar(x, variance_ratio, color='steelblue', alpha=0.7)        ax.plot(x, np.cumsum(variance_ratio), 'ro-', linewidth=2, markersize=4,               label='Cumulative Variance')        ax.set_xlabel('Principal Component')        ax.set_ylabel('Variance Ratio')        ax.set_title('PCA Variance Ratio')        ax.legend()        ax.grid(axis='y', alpha=0.3)        plt.tight_layout()        save_figure(fig, "02.pcaVariance.pdf")        # 保存结果        self.adata.write("step02_adata.h5ad")        self.logger.mark_completed("step02")        self._list_generated_files(["02.pcaGene.pdf""02.PCA.pdf""02.pcaVariance.pdf"])    #######################  STEP 03  #######################    def step03_clustering(self):        """步骤03:细胞聚类和Marker基因识别"""        step_files = [            "03.cluster.pdf",            "03.Cluster.txt",            "03.clusterMarkers.txt",            "03.clusterHeatmap.pdf",            "step03_adata.h5ad"        ]        if self.logger.is_completed("step03"and check_files_exist(step_files):            self.logger.log("步骤03已完成,跳过""SKIP")            self.adata = sc.read_h5ad("step03_adata.h5ad")            return        print_banner("STEP 03: 细胞聚类和Marker基因识别")        self.logger.log("开始步骤03:细胞聚类""INFO")        if self.adata is None:            self.adata = sc.read_h5ad("step02_adata.h5ad")        use_rep = self.adata.uns.get('use_rep''X_pca')        # 构建邻居图        self.logger.log("构建最近邻图...""INFO")        if self.config.USE_GPU:            rsc.pp.neighbors(self.adata, n_neighbors=self.config.N_NEIGHBORS,                            n_pcs=self.config.N_PCS, use_rep=use_rep)        else:            sc.pp.neighbors(self.adata, n_neighbors=self.config.N_NEIGHBORS,                           n_pcs=self.config.N_PCS, use_rep=use_rep)        # Leiden聚类        self.logger.log("Leiden聚类...""INFO")        if self.config.USE_GPU:            rsc.tl.leiden(self.adata, resolution=self.config.RESOLUTION, key_added='leiden')        else:            sc.tl.leiden(self.adata, resolution=self.config.RESOLUTION, key_added='leiden')        n_clusters = len(self.adata.obs['leiden'].unique())        self.logger.log(f"聚类完成 - 共 {n_clusters} 个cluster""INFO")        # tSNE降维        self.logger.log("tSNE降维...""INFO")        if self.config.USE_GPU:            rsc.tl.tsne(self.adata, use_rep=use_rep)        else:            sc.tl.tsne(self.adata, use_rep=use_rep)        # UMAP降维        self.logger.log("UMAP降维...""INFO")        if self.config.USE_GPU:            rsc.tl.umap(self.adata)        else:            sc.tl.umap(self.adata)        # 聚类可视化        self.logger.log("生成聚类图...""INFO")        fig, axes = plt.subplots(12, figsize=(146))        # tSNE        tsne_coords = self.adata.obsm['X_tsne']        clusters = self.adata.obs['leiden'].astype(int)        n_clusters = len(np.unique(clusters))        colors = plt.cm.tab20(clusters / n_clusters)        axes[0].scatter(tsne_coords[:, 0], tsne_coords[:, 1],                        c=colors, s=1, alpha=0.6)        axes[0].set_xlabel('tSNE 1')        axes[0].set_ylabel('tSNE 2')        axes[0].set_title('tSNE Clustering')        # 添加cluster标签        for cluster_id in np.unique(clusters):            cluster_mask = clusters == cluster_id            center_x = tsne_coords[cluster_mask, 0].mean()            center_y = tsne_coords[cluster_mask, 1].mean()            axes[0].text(center_x, center_y, str(cluster_id),                         fontsize=12, weight='bold',                        ha='center', va='center',                        bbox=dict(boxstyle='round,pad=0.3'                                facecolor='white', alpha=0.7))        # UMAP        umap_coords = self.adata.obsm['X_umap']        axes[1].scatter(umap_coords[:, 0], umap_coords[:, 1],                        c=colors, s=1, alpha=0.6)        axes[1].set_xlabel('UMAP 1')        axes[1].set_ylabel('UMAP 2')        axes[1].set_title('UMAP Clustering')        # 添加cluster标签        for cluster_id in np.unique(clusters):            cluster_mask = clusters == cluster_id            center_x = umap_coords[cluster_mask, 0].mean()            center_y = umap_coords[cluster_mask, 1].mean()            axes[1].text(center_x, center_y, str(cluster_id),                         fontsize=12, weight='bold',                        ha='center', va='center',                        bbox=dict(boxstyle='round,pad=0.3'                                facecolor='white', alpha=0.7))        plt.tight_layout()        save_figure(fig, "03.cluster.pdf")        # 保存聚类结果        cluster_df = pd.DataFrame({            'cell_id'self.adata.obs_names,            'cluster'self.adata.obs['leiden']        })        cluster_df.to_csv("03.Cluster.txt", sep='\t', index=False)        # 寻找marker基因        self.logger.log("寻找各cluster的marker基因(耗时约5-10分钟)...""INFO")        sc.tl.rank_genes_groups(            self.adata,             'leiden'            method='wilcoxon',            key_added='rank_genes_leiden'        )        # 提取显著marker        markers_df = self._extract_significant_markers('leiden''rank_genes_leiden')        markers_df.to_csv("03.clusterMarkers.txt", sep='\t', index=False)        self.logger.log(f"发现 {len(markers_df)} 个显著marker基因""INFO")        # Marker基因热图        self.logger.log("生成marker基因热图...""INFO")        top_genes = self._get_top_markers_per_cluster('leiden''rank_genes_leiden', n=10)        # 确保基因存在        top_genes = [g for g in top_genes if g in self.adata.var_names]        if len(top_genes) > 0:            # 提取表达数据            gene_indices = [self.adata.var_names.get_loc(g) for g in top_genes]            expr_data = self.adata.X[:, gene_indices].toarray() if hasattr(self.adata.X, 'toarray'else self.adata.X[:, gene_indices]            # 按cluster排序            cluster_order = np.argsort(self.adata.obs['leiden'].values)            expr_data_sorted = expr_data[cluster_order, :]            # 绘制热图            fig, ax = plt.subplots(figsize=(1210))            im = ax.imshow(expr_data_sorted.T, aspect='auto', cmap='RdYlBu_r'                          interpolation='nearest')            ax.set_yticks(range(len(top_genes)))            ax.set_yticklabels(top_genes, fontsize=6)            ax.set_xlabel('Cells (ordered by cluster)')            ax.set_title('Top Marker Genes per Cluster')            plt.colorbar(im, ax=ax, label='Expression')            plt.tight_layout()            save_figure(fig, "03.clusterHeatmap.pdf")        else:            self.logger.log("没有找到足够的marker基因绘制热图""WARN")        # 保存结果        self.adata.write("step03_adata.h5ad")        self.logger.mark_completed("step03")        self._list_generated_files(["03.cluster.pdf""03.Cluster.txt"                                    "03.clusterMarkers.txt""03.clusterHeatmap.pdf"])    def _extract_significant_markers(self, groupby, key):        """提取显著的marker基因"""        result = self.adata.uns[key]        groups = result['names'].dtype.names        markers_list = []        for group in groups:            gene_names = result['names'][group]            logfcs = result['logfoldchanges'][group]            pvals_adj = result['pvals_adj'][group]            for i, gene in enumerate(gene_names):                if abs(logfcs[i]) > self.config.LOG_FC_FILTER and pvals_adj[i] < self.config.ADJ_PVAL_FILTER:                    markers_list.append({                        'cluster': group,                        'gene': gene,                        'avg_log2FC': logfcs[i],                        'p_val_adj': pvals_adj[i]                    })        return pd.DataFrame(markers_list)    def _get_top_markers_per_cluster(self, groupby, key, n=10):        """获取每个cluster的top N marker基因"""        result = self.adata.uns[key]        groups = result['names'].dtype.names        top_genes = []        for group in groups:            top_genes.extend(result['names'][group][:n])        return list(set(top_genes))  # 去重    #######################  STEP 04  #######################    def step04_cell_annotation(self):        """步骤04:SingleR细胞类型自动注释"""        step_files = [            "04.clusterAnn.txt",            "04.cellAnn.txt",            "04.cellAnn.pdf",            "04.cellMarkers.txt",            "step04_adata.h5ad"        ]        if self.logger.is_completed("step04"and check_files_exist(step_files):            self.logger.log("步骤04已完成,跳过""SKIP")            self.adata = sc.read_h5ad("step04_adata.h5ad")            return        print_banner("STEP 04: 细胞类型自动注释")        self.logger.log("开始步骤04:细胞类型注释""INFO")        if self.adata is None:            self.adata = sc.read_h5ad("step03_adata.h5ad")        # 尝试多种注释方法        annotation_success = False        # 方法1: CellTypist        if CELLTYPIST_AVAILABLE and not annotation_success:            try:                self.logger.log("尝试方法1: CellTypist...""INFO")                import celltypist                from celltypist import models                # 下载模型                model = models.Model.load(model='Immune_All_Low.pkl')                # 预测                predictions = celltypist.annotate(                    self.adata,                     model=model,                    majority_voting=True                )                self.adata.obs['cell_type'] = predictions.predicted_labels.majority_voting                annotation_success = True                self.logger.log("✓ CellTypist注释完成""SUCCESS")            except Exception as e:                self.logger.log(f"CellTypist失败: {str(e)}""WARN")        # 方法2: 基于marker基因的简单注释        if not annotation_success:            self.logger.log("方法2: 基于marker基因注释...""INFO")            # 定义常见细胞类型的marker基因            cell_type_markers = {                'T cells': ['CD3D''CD3E''CD3G'],                'B cells': ['CD79A''CD79B''MS4A1'],                'NK cells': ['GNLY''NKG7''NCAM1'],                'Monocytes': ['CD14''LYZ''S100A8'],                'Dendritic cells': ['FCER1A''CST3'],                'Megakaryocytes': ['PPBP']            }            # 为每个cluster分配细胞类型            cluster_annotations = {}            for cluster in self.adata.obs['leiden'].unique():                cluster_cells = self.adata.obs['leiden'] == cluster                # 计算每种细胞类型的得分                scores = {}                for cell_type, markers in cell_type_markers.items():                    available_markers = [m for m in markers if m in self.adata.var_names]                    if available_markers:                        score = self.adata[cluster_cells, available_markers].X.mean()                        scores[cell_type] = score                # 分配得分最高的类型                if scores:                    best_type = max(scores, key=scores.get)                    cluster_annotations[cluster] = best_type                else:                    cluster_annotations[cluster] = f'Unknown_{cluster}'            # 应用注释            self.adata.obs['cell_type'] = self.adata.obs['leiden'].map(cluster_annotations)            annotation_success = True            self.logger.log("✓ 基于marker基因的注释完成""SUCCESS")        # 保存注释结果        cluster_ann = pd.DataFrame({            'cluster'self.adata.obs['leiden'].unique(),            'cell_type': [self.adata.obs[self.adata.obs['leiden'] == c]['cell_type'].iloc[0                         for c in self.adata.obs['leiden'].unique()]        })        cluster_ann.to_csv("04.clusterAnn.txt", sep='\t', index=False)        cell_ann = pd.DataFrame({            'cell_id'self.adata.obs_names,            'cell_type'self.adata.obs['cell_type']        })        cell_ann.to_csv("04.cellAnn.txt", sep='\t', index=False)        # 统计细胞类型分布        self.logger.log("细胞类型分布:""INFO")        type_counts = self.adata.obs['cell_type'].value_counts()        for ct, count in type_counts.items():            pct = 100 * count / len(self.adata)            self.logger.log(f"  {ct}{count} cells ({pct:.1f}%)""INFO")        # 可视化        self.logger.log("生成细胞类型注释图...""INFO")        fig, axes = plt.subplots(12, figsize=(146))        # 创建颜色映射        cell_types = self.adata.obs['cell_type'].astype('category')        cell_type_codes = cell_types.cat.codes        n_types = len(cell_types.cat.categories)        colors = plt.cm.tab20(cell_type_codes / max(n_types, 1))        # tSNE        tsne_coords = self.adata.obsm['X_tsne']        axes[0].scatter(tsne_coords[:, 0], tsne_coords[:, 1],                        c=colors, s=1, alpha=0.6)        axes[0].set_xlabel('tSNE 1')        axes[0].set_ylabel('tSNE 2')        axes[0].set_title('Cell Types (tSNE)')        # 添加标签        for i, cell_type in enumerate(cell_types.cat.categories):            mask = cell_type_codes == i            if mask.sum() > 0:                center_x = tsne_coords[mask, 0].mean()                center_y = tsne_coords[mask, 1].mean()                axes[0].text(center_x, center_y, cell_type,                            fontsize=8, weight='bold',                           ha='center', va='center',                           bbox=dict(boxstyle='round,pad=0.3'                                   facecolor='white', alpha=0.7))        # UMAP        umap_coords = self.adata.obsm['X_umap']        axes[1].scatter(umap_coords[:, 0], umap_coords[:, 1],                        c=colors, s=1, alpha=0.6)        axes[1].set_xlabel('UMAP 1')        axes[1].set_ylabel('UMAP 2')        axes[1].set_title('Cell Types (UMAP)')        # 添加标签        for i, cell_type in enumerate(cell_types.cat.categories):            mask = cell_type_codes == i            if mask.sum() > 0:                center_x = umap_coords[mask, 0].mean()                center_y = umap_coords[mask, 1].mean()                axes[1].text(center_x, center_y, cell_type,                            fontsize=8, weight='bold',                           ha='center', va='center',                           bbox=dict(boxstyle='round,pad=0.3'                                   facecolor='white', alpha=0.7))        plt.tight_layout()        save_figure(fig, "04.cellAnn.pdf")        # 细胞类型marker基因        self.logger.log("寻找细胞类型marker基因...""INFO")        sc.tl.rank_genes_groups(            self.adata,            'cell_type',            method='wilcoxon',            key_added='rank_genes_celltype'        )        markers_df = self._extract_significant_markers('cell_type''rank_genes_celltype')        markers_df.to_csv("04.cellMarkers.txt", sep='\t', index=False)        self.logger.log(f"发现 {len(markers_df)} 个细胞类型marker基因""INFO")        # 保存结果        self.adata.write("step04_adata.h5ad")        self.logger.mark_completed("step04")        self._list_generated_files(["04.clusterAnn.txt""04.cellAnn.txt"                                    "04.cellAnn.pdf""04.cellMarkers.txt"])    #######################  STEP 05  #######################    def step05_differential_analysis(self):        """步骤05:组间差异基因分析"""        if self.logger.is_completed("step05"):            self.logger.log("步骤05已完成,跳过""SKIP")            return        print_banner("STEP 05: 组间差异基因分析")        self.logger.log("开始步骤05:组间差异分析""INFO")        if self.adata is None:            self.adata = sc.read_h5ad("step04_adata.h5ad")        # 构建分组信息(转换Categorical为字符串)        self.adata.obs['group'] = (self.adata.obs['batch'].astype(str) + '_' +                                    self.adata.obs['cell_type'].astype(str))        unique_cell_types = self.adata.obs['cell_type'].unique()        self.logger.log(f"检测到 {len(unique_cell_types)} 种细胞类型""INFO")        diff_gene_count = 0        for cell_type in unique_cell_types:            # 检查是否有足够的样本            control_group = f'Control_{cell_type}'            treat_group = f'Treat_{cell_type}'            control_count = sum(self.adata.obs['group'] == control_group)            treat_count = sum(self.adata.obs['group'] == treat_group)            if control_count > 5 and treat_count > 5:                self.logger.log(f"分析 {cell_type}: Control={control_count}, Treat={treat_count}""INFO")                try:                    # 执行差异分析                    sc.tl.rank_genes_groups(                        self.adata,                        groupby='group',                        groups=[treat_group],                        reference=control_group,                        method='wilcoxon',                        key_added=f'rank_genes_{cell_type}'                    )                    # 提取显著差异基因                    result = self.adata.uns[f'rank_genes_{cell_type}']                    diff_genes = []                    gene_names = result['names'][treat_group]                    logfcs = result['logfoldchanges'][treat_group]                    pvals_adj = result['pvals_adj'][treat_group]                    for i, gene in enumerate(gene_names):                        if abs(logfcs[i]) > self.config.LOG_FC_FILTER and pvals_adj[i] < self.config.ADJ_PVAL_FILTER:                            diff_genes.append({                                'gene': gene,                                'avg_log2FC': logfcs[i],                                'p_val_adj': pvals_adj[i]                            })                    if diff_genes:                        diff_df = pd.DataFrame(diff_genes)                        safe_name = cell_type.replace(' ''_').replace('/''_')                        output_file = f"05.{safe_name}.diffGene.txt"                        diff_df.to_csv(output_file, sep='\t', index=False)                        self.logger.log(f"  ✓ {cell_type}: 发现 {len(diff_genes)} 个差异基因""SUCCESS")                        diff_gene_count += 1                    else:                        self.logger.log(f"  - {cell_type}: 未发现显著差异基因""INFO")                except Exception as e:                    self.logger.log(f"  ✗ {cell_type} 分析出错: {str(e)}""ERROR")            else:                self.logger.log(f"跳过 {cell_type}: 样本量不足 (Control={control_count}, Treat={treat_count})"                              "WARN")        self.logger.log(f"完成差异分析,共 {diff_gene_count} 个细胞类型有显著差异""SUCCESS")        # 保存最终结果        self.adata.write("Scanpy.h5ad")        self.logger.mark_completed("step05")    #######################  STEP 06  #######################    def step06_trajectory_analysis(self):        """步骤06:拟时序分析(可选)"""        if self.logger.is_completed("step06"):            self.logger.log("步骤06已完成,跳过""SKIP")            return        print_banner("STEP 06: 拟时序分析(可选)")        self.logger.log("开始步骤06:拟时序分析""INFO")        if self.adata is None:            self.adata = sc.read_h5ad("step04_adata.h5ad")        trajectory_success = False        # 方法1: PAGA        try:            self.logger.log("尝试方法1: PAGA...""INFO")            # 计算PAGA            sc.tl.paga(self.adata, groups='leiden')            # 可视化            self.logger.log("生成PAGA轨迹图...""INFO")            fig, axes = plt.subplots(12, figsize=(146))            # PAGA图            paga_coords = self.adata.uns['paga']['pos']            connectivities = self.adata.uns['paga']['connectivities'].toarray()            # 绘制连接            for i in range(len(paga_coords)):                for j in range(i+1len(paga_coords)):                    if connectivities[i, j] > 0.1:                        axes[0].plot([paga_coords[i, 0], paga_coords[j, 0]],                                   [paga_coords[i, 1], paga_coords[j, 1]],                                   'k-', alpha=connectivities[i, j], linewidth=2)            # 绘制节点            axes[0].scatter(paga_coords[:, 0], paga_coords[:, 1],                           s=200, c=range(len(paga_coords)), cmap='tab20')            for i, pos in enumerate(paga_coords):                axes[0].text(pos[0], pos[1], str(i),                            ha='center', va='center', fontsize=10, weight='bold')            axes[0].set_title('PAGA Graph')            axes[0].axis('off')            # UMAP with PAGA            umap_coords = self.adata.obsm['X_umap']            clusters = self.adata.obs['leiden'].astype(int)            colors = plt.cm.tab20(clusters / len(np.unique(clusters)))            axes[1].scatter(umap_coords[:, 0], umap_coords[:, 1],                          c=colors, s=1, alpha=0.5)            axes[1].set_xlabel('UMAP 1')            axes[1].set_ylabel('UMAP 2')            axes[1].set_title('PAGA with Clusters')            plt.tight_layout()            save_figure(fig, "06.trajectory.paga.pdf")            # 计算伪时间            sc.tl.dpt(self.adata, n_dcs=15)            # 保存伪时间            pseudotime_df = pd.DataFrame({                'cell_id'self.adata.obs_names,                'pseudotime'self.adata.obs['dpt_pseudotime'],                'cluster'self.adata.obs['leiden']            })            pseudotime_df.to_csv("06.pseudotime.txt", sep='\t', index=False)            # 可视化伪时间            self.logger.log("生成伪时间图...""INFO")            fig, ax = plt.subplots(figsize=(86))            umap_coords = self.adata.obsm['X_umap']            pseudotime = self.adata.obs['dpt_pseudotime'].values            scatter = ax.scatter(umap_coords[:, 0], umap_coords[:, 1],                               c=pseudotime, s=2, cmap='viridis', alpha=0.8)            ax.set_xlabel('UMAP 1')            ax.set_ylabel('UMAP 2')            ax.set_title('Pseudotime (DPT)')            plt.colorbar(scatter, ax=ax, label='Pseudotime')            plt.tight_layout()            save_figure(fig, "06.pseudotime.pdf")            self.logger.log("✓ PAGA拟时序分析完成""SUCCESS")            trajectory_success = True        except Exception as e:            self.logger.log(f"PAGA失败: {str(e)}""WARN")        # 如果PAGA失败,尝试简单的Diffusion Pseudotime        if not trajectory_success:            try:                self.logger.log("尝试方法2: Diffusion Pseudotime...""INFO")                sc.tl.diffmap(self.adata)                sc.tl.dpt(self.adata)                fig, ax = plt.subplots(figsize=(86))                umap_coords = self.adata.obsm['X_umap']                pseudotime = self.adata.obs['dpt_pseudotime'].values                scatter = ax.scatter(umap_coords[:, 0], umap_coords[:, 1],                                   c=pseudotime, s=2, cmap='viridis', alpha=0.8)                ax.set_xlabel('UMAP 1')                ax.set_ylabel('UMAP 2')                ax.set_title('Pseudotime (DPT)')                plt.colorbar(scatter, ax=ax, label='Pseudotime')                plt.tight_layout()                save_figure(fig, "06.pseudotime.simple.pdf")                self.logger.log("✓ 简单伪时间分析完成""SUCCESS")                trajectory_success = True            except Exception as e:                self.logger.log(f"伪时间分析失败: {str(e)}""WARN")        if not trajectory_success:            self.logger.log("拟时序分析未能完成,跳过此步骤""WARN")            self.logger.log("提示: 拟时序分析为可选步骤,不影响主要分析结果""INFO")        self.logger.mark_completed("step06")    #######################  总结报告  #######################    def generate_summary_report(self):        """生成分析总结报告"""        print("\n\n")        print("╔" + "=" * 62 + "╗")        print("║" + " " * 25 + "分析完成总结" + " " * 25 + "║")        print("╚" + "=" * 62 + "╝\n")        # 步骤完成状态        print("【步骤完成状态】")        print("─" * 64)        steps = {            "step01""数据加载和质控",            "step02""PCA降维和批次校正",            "step03""细胞聚类和Marker基因",            "step04""细胞类型注释",            "step05""组间差异分析",            "step06""拟时序分析(可选)"        }        for step_id, step_name in steps.items():            status = "✓ 已完成" if step_id in self.logger.completed_steps else "✗ 未完成"            print(f"{status}{step_name}")        print()        # 数据统计        if self.adata is not None:            print("【数据统计】")            print("─" * 64)            print(f"总细胞数: {self.adata.n_obs}")            print(f"总基因数: {self.adata.n_vars}")            if 'leiden' in self.adata.obs:                print(f"聚类数: {len(self.adata.obs['leiden'].unique())}")            if 'cell_type' in self.adata.obs:                print("\n细胞类型分布:")                type_counts = self.adata.obs['cell_type'].value_counts()                for ct, count in type_counts.items():                    pct = 100 * count / self.adata.n_obs                    print(f"  - {ct}{count} cells ({pct:.1f}%)")            print()        # 生成的文件        print("【生成的文件】")        print("─" * 64)        output_files = sorted([f for f in os.listdir('.'                              if f.endswith(('.pdf''.txt''.h5ad'))])        for category in ['01''02''03''04''05''06']:            category_files = [f for f in output_files if f.startswith(category)]            if category_files:                print(f"\n步骤{category}:")                for f in category_files:                    print(f"  - {f}")        print("\n【重要结果文件】")        print("─" * 64)        print("  - Scanpy.h5ad                       # 完整AnnData对象")        print("  - 04.cellAnn.txt                    # 细胞类型注释")        print("  - 03.clusterMarkers.txt             # Cluster marker基因")        print("  - 04.cellMarkers.txt                # 细胞类型marker基因")        print("  - 05.*.diffGene.txt                 # 差异基因列表")        print("  - analysis_progress.log             # 详细分析日志")        print()        print("【工作目录】")        print("─" * 64)        print(f"  {os.getcwd()}")        print()        print("【下一步分析建议】")        print("─" * 64)        print("  1. 查看 analysis_progress.log 了解详细过程")        print("  2. 对差异基因进行GO/KEGG富集分析")        print("  3. 使用 Scanpy.h5ad 进行后续定制化分析")        print("  4. 根据生物学意义解释细胞类型和差异基因")        print()        print("╔" + "=" * 62 + "╗")        print("║" + " " * 22 + "分析流程全部完成!" + " " * 22 + "║")        print("╚" + "=" * 62 + "╝\n")#######################  主函数  #######################def main():    """主函数"""    # 创建分析流程    pipeline = SeuratAnalysisPipeline()    # 运行分析    pipeline.run()if __name__ == "__main__":    main()

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-02-09 14:16:24 HTTP/2.0 GET : https://f.mffb.com.cn/a/462142.html
  2. 运行时间 : 0.124896s [ 吞吐率:8.01req/s ] 内存消耗:4,799.38kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=0a2c5bb4d47da26fe332011be84c53fd
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000548s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000898s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000370s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000244s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000501s ]
  6. SELECT * FROM `set` [ RunTime:0.000177s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000543s ]
  8. SELECT * FROM `article` WHERE `id` = 462142 LIMIT 1 [ RunTime:0.000624s ]
  9. UPDATE `article` SET `lasttime` = 1770617784 WHERE `id` = 462142 [ RunTime:0.010041s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.000394s ]
  11. SELECT * FROM `article` WHERE `id` < 462142 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.015290s ]
  12. SELECT * FROM `article` WHERE `id` > 462142 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.002564s ]
  13. SELECT * FROM `article` WHERE `id` < 462142 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.016019s ]
  14. SELECT * FROM `article` WHERE `id` < 462142 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.002409s ]
  15. SELECT * FROM `article` WHERE `id` < 462142 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.000994s ]
0.126600s