NCR+可变电荷块3——NCB/cell绘图1

文献method参考:

蛋白质序列数据从uniprot中获取


https://www.uniprot.org/uniprotkb/P46013/entry

https://www.uniprot.org/uniprotkb/P06748/entry、

1,电荷分布计算:

Charge distribution was calculated as the sum of the charges (Arg and Lys, +1; Glu and Asp, −1; phospho-Ser and phospho-Thr, −2) in the indicated window range. A charge block was designated when the area of the charge plot (window size: 35 amino acids) was larger than 20.

(1)电荷分布是计算指定窗口window内净电荷之和,所以dict对应关系是1个window对应1个电荷总和,所以电荷分布图实际上就是绘制(x,y)=(窗口编号,该窗口净电荷总和)的折线图

①首先计算给定窗口内每个氨基酸的净电荷是简单的:

碱性带正电aa:KR,但是没有组氨酸H?

酸性带负电aa:DE

至于磷酸化的氨基酸没有数据可以验证(我们只有序列数据,没法获取,需要实际in vivo数据,也许可以从蛋白质晶体数据库上获取?)

可以另外开一个磷酸化的字典dict,存放不同的aa字符与带电荷的对应关系,其实这样的PTM的字典可以开很多

**②问题是计算电荷分布的时候窗口开多大?
**是method里指定的win=35吗?

这里很容易被误导,实际上不是35。

35指的是charge plot已经绘制出来之后,然后计算特定区域内charge plot的area面积,实际上应该需要进行积分。

有个问题,如何理解这里的area>20,首先要理解block,肯定是单一电荷性质的,所以肯定是看的积分之后的净area,比如说是计算对应的正电荷的正面积之后,以及负电荷的负面积,然后计算这个窗口的电荷总和,所以实际上就是电荷面积计算的一个净net值的绝对值。

**所以在计算charge plot中的window窗口应该设置为多大?
**

可以看到实际上每一幅图指定的window窗口大小尺寸都是25

但是在提供的附近材料(审稿意见答复)中又说是35

当然仅仅只是获取charge plot的话窗口定义多大多小都不影响,因为只是展示:
看复现效果:Ki-67

16个RD重复单元,每个大概在110个aa左右

我们只看R12

实际上可以看到对应紫色那块,也就是K167R 12

实际上大小是112

示例代码如下:

def calculate_charge_distribution(sequence, phospho_sites, window_size=35):
    """计算蛋白质电荷分布

    Args:
        sequence: 蛋白质氨基酸序列 (字符串)
        phospho_sites: 磷酸化位点列表 (例如:[10, 25, 50]),位点编号从1开始,蛋白质序列index是1-index
        window_size: 滑动窗口大小 (整数)

    Returns:
        一个列表,包含每个窗口的净电荷。
    """
    charges = {'R': 1, 'K': 1, 'E': -1, 'D': -1, 'S': 0, 'T': 0}  
    #因为原文考虑到了丝氨酸以及苏氨酸的磷酸化与否对蛋白质电荷块分布的影响,所以这里需要对丝氨酸S以及苏氨酸T设置2个dict
    #但是正电荷没有考虑到组氨酸H,所以这里没有设置组氨酸的正电荷
    phospho_charges = { 'S': -2, 'T':-2}
    #这个PTM翻译后修饰的字典可以根据实际情况进行修改,进行扩展

    sequence = sequence.upper()
    # 将序列转换为大写,以确保一致性
    charge_distribution = []
    # 初始化一个空列表来存储每个窗口的净电荷
    for i in range(len(sequence) - window_size + 1):
        # 遍历的窗口编号,从0开始
        window = sequence[i:i + window_size]
        # 获取当前窗口的序列
        total_charge = 0
        # 初始化当前窗口的总电荷
        for j, aa in enumerate(window):
            # enumerate(iterable, start=0),用于在遍历可迭代对象(如列表、字符串等)时,同时获取元素的索引和值,索引默认从0开始
            # j是索引从0开始,aa是氨基酸字符
            pos = i+j+1 
            #对应氨基酸的真实物理位置1-based,注意i是range从0开始(0:第1个窗口,j是enumrate返回的元组的索引,从0开始),所以pos=i+j+1
            #编号i=0的窗口、索引j=0的序列,实际上是第1个氨基酸pos=1
            charge = charges.get(aa, 0)
            # 需要计算电荷的氨基酸都在charges字典里,如果不在字典里,返回0
            if pos in phospho_sites:
                charge = phospho_charges.get(aa,0)
                #如果当前氨基酸在磷酸化位点列表里,则用磷酸化后的电荷替换掉原来的电荷
            total_charge += charge
            # 将氨基酸的电荷加到当前窗口的总电荷中
        charge_distribution.append(total_charge)
        # 将当前窗口的总电荷添加到charge_distribution列表中
    return charge_distribution
    # 返回电荷分布列表
    
    
# 示例用法:
sequence = "KTTKIACKSPQPDPVDTPASTKQRPKRNLRKADVEEEFLALRKRTPSAGKAMDTPKPAVSDEKNINTFVETPVQKLDLLGNLPGSKRQPQTPKEKAEALEDLVGFKELFQTP" #替换为你的氨基酸序列
phospho_sites = [] #替换为你的磷酸化位点
charge_distribution = calculate_charge_distribution(sequence, phospho_sites)
print(charge_distribution)


#绘制图形,需要用到matplotlib库
import matplotlib.pyplot as plt
plt.plot(charge_distribution)
plt.xlabel("Residue")
plt.ylabel("Charge")    
plt.title("Charge plot")
plt.show()


如果窗口是35内计算的净电荷,那R12:

如果是25窗口大小

很显然不对,因为RD12序列就长112,如果是使用窗口的话,实际上是无法延伸到100多位置的氨基酸序列的

所以实际上都和实物图对不上

其实看下面这张图就很清晰了:

这里是105到250都有,所以肯定不是这么画的

——》如果只是单纯看实物图的话,其实实物图肯定是每一个氨基酸位置都考虑进去的,

并且每个位置上的氨基酸序列肯定是计算了不止一个电荷的(所以x轴肯定又不是表征的单个氨基酸序列位置的信息,应该是某个集合或者是窗口下的位置信息之和)

问题就在于如果考虑了所有窗口内的氨基酸序列之后,

那几张又如何计算整个窗口下的

——》索性查看引用这篇NCB的所有文献里的charge plot怎么画的:


https://www.bioinformatics.nl/cgi-bin/emboss/charge

如果我只是输入同样的R12序列,结果是:

确实是能够计算到每一个位置,且电荷显然不是只计算一个,既然肯定有方法计算

我们重点关注的是下面NCPR的图,

we calculated the net charge per residue (NCPR) using a sliding window of 10 residues

没有关于电荷的图,但是倒是有一个有趣的图

总之我们能够看到,计算电荷块,看的多是整体区域的带电性质,也就是NPCR,一般不会用来看DE或者KRH数目的;

所以应该先搞清楚NCPR概念,然后再绘制charge plot(有一个参考来源即可)

参考2010的PNAS:

f+定义为一段给定aa序列中正电荷残基比例(理论上比例应该是这样算的:
f+=(带正电荷残基数x该残基所带正电荷电荷数)/该aa序列长度,

同理f-,

因为KRH和DE都是单位带电荷数,所以实际上就是看aa数目比例

以下图第一段序列为例:
全长29,正电荷氨基酸数9,负电荷为3,所以

f+=9/29=0.31

f-=3/29=0.10

其实就是将正负电荷均分到窗口/全长序列的每个残基中,比如说第一段序列相当于每个残基有31%的正电,同时带10%的负电,净电荷就是21%的正电荷

对应很多文献滑动窗口下的charge曲线实际上有正有负,定义NCPR=f±f-

(2)如何由电荷分布charge plot推导出电荷块charge block:
实际上就是如何表征电荷块

A charge block was designated when the area of the charge plot (window size: 35 amino acids) was larger than 20.

然后单个净电荷如何应用于计算电荷块:

设置一个窗口大小为35aa,但是是什么大于20呢:
带净电荷的aa数目?不合理,万一正的10个,负的10个 55开呢?

sum之后的净电荷值(应该是绝对值)?合理——》进一步评估

(3)必要时可可以进行多序列比对:

用的还是Clustal Omega( https://www.ebi.ac.uk/Tools/msa/clustalo/ )

(4)自定义的电荷模式参数:charge patterning parameters

文献里最终使用的是Blc参数

Dseg:

Blc:

另外还可以看一下reporting summary,实际上看到使用的数据分析方面的工具都是一些普通的python库

一些更细节的地方可以深入分析:一些问题都被peer提出来了

2,电荷模式参数计算:

实际使用参数评估过程中有很多参数可以使用:

kappa参数

SCD参数

Dseg参数

Blc参数

==================================================================

鉴于NCB文献中对于block的模糊定义,还是借鉴cell文献的method:


NCPR依据上面的定义我们能算了,然后窗口就是开10为大小,

window=10,如果NCPR>=5,则为酸性/负电block;

NCPR<=5,则为碱性/正电block

魔改一下我之前写的calc_NCR.py程序

import sys #提供参数
import csv #处理分隔符文件,不一定是csv
import argparse #用于参数解析,帮助手册
import subprocess #用于执行shell命令
# import os #用于文件操作

# 定义一个函数来解析FASTA文件,主要是获得1个蛋白质的ID和氨基酸序列对应的字典sequences
def parse_fasta(fasta_file):    
    with open(fasta_file, 'r') as file:
        #使用with语句打开文件,'r'表示以只读模式打开
        sequences = {}   
        current_id = None
        current_sequence = []
        #初始化了一个空字典sequences来存储蛋白质序列:一个变量current_id来存储当前处理的蛋白质的ID,以及一个列表current_sequence来存储当前蛋白质的氨基酸序列
        for line in file:
            line = line.strip()
            #遍历文件中的每一行,并使用strip()方法去除每行的换行符,这样后面append的时候所有序列会连在一起
            if line.startswith('>'):
            #检查当前行是否以'>'字符开头,如果是,那么这一行就是一个新的蛋白质记录的开始
                if current_id:
                    sequences[current_id] = ''.join(current_sequence)
                    current_sequence = []
                #如果current_id不为空(即之前已经处理过一个蛋白质),那么将当前蛋白质的序列(current_sequence)添加到sequences字典中,并重置current_sequence列表
                parts = line[1:].split()
                #对于每一行,从">"之后开始切割,然后获取['sp|Q6ZN18|AEBP2_HUMAN', 'Zinc', 'finger', 'protein', 'AEBP2', 'OS=Homo', 'sapiens', 'OX=9606', 'GN=AEBP2', 'PE=1', 'SV=2']
                id_info = parts[0].split('|')
                current_id = id_info[2] 
                #实际上获取的sp|Q6ZN18|AEBP2_HUMAN中第3个分隔符字段才是对应的蛋白质名字,当然也可以对照GN字段后面的基因名字
                
            else:
                current_sequence.append(line)
                #对应前面的if,如果当前行不是以'>'字符开头,那么这一行就是一个蛋白质的氨基酸序列,将其添加到current_sequence列表中,直接添加并合并
        if current_id:
            sequences[current_id] = ''.join(current_sequence)
        #确保当前序列已经有一个ID(即 current_id 不为空,然后直接存储对应的序列到字典中
    return sequences

def sliding_window_for_charged_block(sequence, phospho_sites=[],window_size=10, threshold=5):
    """
    滑动窗口算法,用于查找蛋白质序列中NCPR绝对值大于阈值的窗口

    Args:
        sequence (str): 蛋白质序列
        phospho_sites (list): 磷酸化位点列表,默认为空,因为main主函数中是批量化处理,但显然每一个蛋白质序列都有差异的磷酸化位点,如果要分开计算,这个函数模块可以保留下来,但需要修改main函数
        window_size (int): 窗口大小,默认为10
        threshold (float): NCPR阈值,默认为5

    Returns:
        list: 合并后的窗口起点和终点列表,即找出的block
    """
    charges = {'R': 1, 'K': 1,'H':1, 'E': -1, 'D': -1, 'S': 0, 'T': 0}  
    #因为原文考虑到了丝氨酸以及苏氨酸的磷酸化与否对蛋白质电荷块分布的影响,所以这里需要对丝氨酸S以及苏氨酸T设置2个dict
    #NCB文献里正电荷没有考虑到组氨酸H,但是cell里有,还是补上
    phospho_charges = { 'S': -2, 'T':-2}
    #这个PTM翻译后修饰的字典可以根据实际情况进行修改,进行扩展
    
    results_acidic = [] #用于保存找到的酸性block的起点和终点,结果起点终点对保存在列表中
    results_basic = [] #用于保存找到的碱性block的起点和终点
    for i in range(len(sequence) - window_size + 1):
        # 遍历的窗口编号,从0开始  
        window = sequence[i:i + window_size]
        # 获取当前窗口的序列,注意取的是str切片,终点取不到,但是终点-起点+1-1
        total_charge = 0
        # 初始化当前窗口的总电荷
        for j, aa in enumerate(window):
            # enumerate(iterable, start=0),用于在遍历可迭代对象(如列表、字符串等)时,同时获取元素的索引和值,索引默认从0开始
            # j是索引从0开始,aa是氨基酸字符
            pos = i+j+1 
            #对应氨基酸的真实物理位置1-based,注意i是range从0开始(0:第1个窗口,j是enumrate返回的元组的索引,从0开始),所以pos=i+j+1
            #编号i=0的窗口、索引j=0的序列,实际上是第1个氨基酸pos=1
            charge = charges.get(aa, 0)
            # 需要计算电荷的氨基酸都在charges字典里,如果不在字典里,返回0
            if pos in phospho_sites:
                charge = phospho_charges.get(aa,0)
                #如果当前氨基酸在磷酸化位点列表里,则用磷酸化后的电荷替换掉原来的电荷
            total_charge += charge
        # 计算窗口内的总电荷
        if abs(total_charge) > threshold:
            # 窗口内的绝对值大于阈值,则判定为charged block
            if total_charge > 0:
                results_basic.append((i+1, i + window_size))
                # 该起点终点对保存在正电碱性block列表中,(i+1, i + window_size)保存的数据类型是元组tuple,且是实际1-based的physical位置
            else:
                results_acidic.append((i+1, i + window_size))
                # 该起点终点对保存在酸性block列表中,(i+1, i + window_size)保存的数据类型是元组tuple
    
    # 合并重叠的窗口
    merged_results_acidic = []
    merged_results_basic =[] #同前面,对酸性负电以及碱性正电氨基酸的窗口进行合并,设置初始化的空列表
    # 用于保存已经合并的窗口起点终点元组对,注意每次比较都是比较当前元组的起点和上一个已经合并的元组(也就是merged_results[-1])的终点
    
    #下面是合并酸性带负电的窗口
    if results_acidic:
        # 如果酸性负电窗口results列表不为空
        merged_results_acidic = [results_acidic[0]]
        # 将第一个窗口加入merged_results列表中,作为后续比对的起点
        for current in results_acidic[1:]:
            # 遍历results_acidic列表中剩余的窗口
            last = merged_results_acidic[-1]
            # 取出已经合并的窗口的最后一个窗口的元组对
            if current[0] <= last[1] + 1:  
            # 如果当前窗口与最后一个窗口有重叠,也就是当前窗口的起点小于等于已经合并的最后一个窗口的终点+1(overlap以及相邻)
                merged_results_acidic[-1] = (last[0], max(last[1], current[1]))
                # 更新已经合并的最后一个窗口的终点为当前窗口和已经合并的最后一个窗口的终点的最大值,总之就是更新最后一个窗口,起点不变,终点更新
            else:
                merged_results_acidic.append(current)
                # 如果当前窗口与最后一个窗口没有重叠,直接将当前窗口加入merged_results列表中,也就是当前窗口并没有重叠

    #下面是合并碱性带正电的窗口
    if results_basic:
        merged_results_basic = [results_basic[0]]
        for current in results_basic[1:]:
            last = merged_results_basic[-1]
            if current[0] <= last[1] + 1:
                merged_results_basic[-1] = (last[0], max(last[1], current[1]))
            else:
                merged_results_basic.append(current)
    return merged_results_acidic, merged_results_basic
    # 返回合并之后的酸性负电窗口和碱性正电窗口,各自是合并后的窗口起点和终点列表,且每一个元素是元组,后续需要解包赋值
    


def main(fasta_file, output_file,window_size, threshold, delimiter='\t'):  
    #默认输出为tsv格式
    sequences = parse_fasta(fasta_file)
    #调用parse_fasta函数来解析FASTA文件,并返回一个字典sequences,其中键是蛋白质的ID,值是蛋白质的氨基酸序列(字符串)
    
    #处理正电荷block、负电荷block区域输出文件
    with open(output_file, 'w') as csvfile:
    #使用with语句打开一个输出文件(需要自己提供参数命名,'w'表示以写入模式打开
        writer = csv.writer(csvfile, delimiter=delimiter)
        #创建一个csv.writer对象,用于将数据写入csv文件,delimiter='\t'表示使用制表符作为字段分隔符
        writer.writerow(['Protein_Name', 'charged_block_start', 'charged_block_end', 'Sequence', 'charged_block_type'])
        #写入表头,即列名
        for protein_name, sequence in sequences.items():
            #sequences是一个字典,键是蛋白质的ID,值是蛋白质的氨基酸序列,现在从值中获取charged block区域起点以及终点的元组对列表
            merged_results_acidic, merged_results_basic= sliding_window_for_charged_block(sequence.replace('\n', ''),phospho_sites=[],window_size=window_size, threshold=threshold) 
            #.replace('\n', '')双重保证,如果前面fasta序列中没有去除换行符
            
            for results in merged_results_acidic:
                writer.writerow([protein_name, results[0], results[1], sequence[results[0]-1:results[1]], 'Acidic'])
            for results in merged_results_basic:
                writer.writerow([protein_name, results[0], results[1], sequence[results[0]-1:results[1]], 'Basic'])
            #将蛋白质的ID、charged block区域的起点和终点,以及对应的序列切片,以及是酸性还是碱性,写入csv文件中,注意切片是0-based的,而真实位置是1-based的
            #后续可以导入到R中使用tidyverse

    #如果需要统计信息并写入到{output_file}.stats文件中,可以参考我原本calc_NCR.py的代码,这里不再赘述        


#使用 argparse 模块来实现类似于 Linux 工具的命令行参数解析(包括生成帮助手册等)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="FASTA文件处理工具")
    parser.add_argument("--fasta_file", help="输入的FASTA文件路径")
    parser.add_argument("--delimiter", help="输出文件的分隔符(如$'\t'等)")
    parser.add_argument("--window_size", type=int, default=10, help="滑动窗口大小")
    parser.add_argument("--threshold", type=float, default=5, help="NCPR阈值,注意是>=")
    parser.add_argument("--output_file", help="输出可变电荷块文件路径")
    # parser.add_argument("--help", action="help", help="显示帮助信息"),不需要手动添加 --help 选项,因为 argparse 模块已经自动添加了一个 --help 选项来显示帮助信息q

    args = parser.parse_args()
    main(args.fasta_file, args.output_file, args.delimiter, args.window_size, args.threshold)

去掉符号修改之后是

import sys #提供参数
import csv #处理分隔符文件,不一定是csv
import argparse #用于参数解析,帮助手册
import subprocess #用于执行shell命令
# import os #用于文件操作

# 定义一个函数来解析FASTA文件,主要是获得1个蛋白质的ID和氨基酸序列对应的字典sequences
def parse_fasta(fasta_file):    
    with open(fasta_file, 'r') as file:
        #使用with语句打开文件,'r'表示以只读模式打开
        sequences = {}   
        current_id = None
        current_sequence = []
        #初始化了一个空字典sequences来存储蛋白质序列:一个变量current_id来存储当前处理的蛋白质的ID,以及一个列表current_sequence来存储当前蛋白质的氨基酸序列
        for line in file:
            line = line.strip()
            #遍历文件中的每一行,并使用strip()方法去除每行的换行符,这样后面append的时候所有序列会连在一起
            if line.startswith('>'):
            #检查当前行是否以'>'字符开头,如果是,那么这一行就是一个新的蛋白质记录的开始
                if current_id:
                    sequences[current_id] = ''.join(current_sequence)
                    current_sequence = []
                #如果current_id不为空(即之前已经处理过一个蛋白质),那么将当前蛋白质的序列(current_sequence)添加到sequences字典中,并重置current_sequence列表
                parts = line[1:].split()
                #对于每一行,从">"之后开始切割,然后获取['sp|Q6ZN18|AEBP2_HUMAN', 'Zinc', 'finger', 'protein', 'AEBP2', 'OS=Homo', 'sapiens', 'OX=9606', 'GN=AEBP2', 'PE=1', 'SV=2']
                id_info = parts[0].split('|')
                current_id = id_info[2] 
                #实际上获取的sp|Q6ZN18|AEBP2_HUMAN中第3个分隔符字段才是对应的蛋白质名字,当然也可以对照GN字段后面的基因名字
                
            else:
                current_sequence.append(line)
                #对应前面的if,如果当前行不是以'>'字符开头,那么这一行就是一个蛋白质的氨基酸序列,将其添加到current_sequence列表中,直接添加并合并
        if current_id:
            sequences[current_id] = ''.join(current_sequence)
        #确保当前序列已经有一个ID(即 current_id 不为空,然后直接存储对应的序列到字典中
    return sequences

def sliding_window_for_charged_block(sequence, phospho_sites=[],window_size=10, threshold=5):
    """
    滑动窗口算法,用于查找蛋白质序列中NCPR绝对值大于阈值的窗口

    Args:
        sequence (str): 蛋白质序列
        phospho_sites (list): 磷酸化位点列表,默认为空,因为main主函数中是批量化处理,但显然每一个蛋白质序列都有差异的磷酸化位点,如果要分开计算,这个函数模块可以保留下来,但需要修改main函数
        window_size (int): 窗口大小,默认为10
        threshold (float): NCPR阈值,默认为5

    Returns:
        list: 合并后的窗口起点和终点列表,即找出的block
    """
    charges = {'R': 1, 'K': 1,'H':1, 'E': -1, 'D': -1, 'S': 0, 'T': 0}  
    #因为原文考虑到了丝氨酸以及苏氨酸的磷酸化与否对蛋白质电荷块分布的影响,所以这里需要对丝氨酸S以及苏氨酸T设置2个dict
    #NCB文献里正电荷没有考虑到组氨酸H,但是cell里有,还是补上
    phospho_charges = { 'S': -2, 'T':-2}
    #这个PTM翻译后修饰的字典可以根据实际情况进行修改,进行扩展
    
    results_acidic = [] #用于保存找到的酸性block的起点和终点,结果起点终点对保存在列表中
    results_basic = [] #用于保存找到的碱性block的起点和终点
    for i in range(len(sequence) - window_size + 1):
        # 遍历的窗口编号,从0开始  
        window = sequence[i:i + window_size]
        # 获取当前窗口的序列,注意取的是str切片,终点取不到,但是终点-起点+1-1
        total_charge = 0
        # 初始化当前窗口的总电荷
        for j, aa in enumerate(window):
            # enumerate(iterable, start=0),用于在遍历可迭代对象(如列表、字符串等)时,同时获取元素的索引和值,索引默认从0开始
            # j是索引从0开始,aa是氨基酸字符
            pos = i+j+1 
            #对应氨基酸的真实物理位置1-based,注意i是range从0开始(0:第1个窗口,j是enumrate返回的元组的索引,从0开始),所以pos=i+j+1
            #编号i=0的窗口、索引j=0的序列,实际上是第1个氨基酸pos=1
            charge = charges.get(aa, 0)
            # 需要计算电荷的氨基酸都在charges字典里,如果不在字典里,返回0
            if pos in phospho_sites:
                charge = phospho_charges.get(aa,0)
                #如果当前氨基酸在磷酸化位点列表里,则用磷酸化后的电荷替换掉原来的电荷
            total_charge += charge
        # 计算窗口内的总电荷
        if abs(total_charge) >= threshold:
            # 窗口内的绝对值大于等于阈值,则判定为charged block
            if total_charge > 0:
                results_basic.append((i+1, i + window_size))
                # 该起点终点对保存在正电碱性block列表中,(i+1, i + window_size)保存的数据类型是元组tuple,且是实际1-based的physical位置
            else:
                results_acidic.append((i+1, i + window_size))
                # 该起点终点对保存在酸性block列表中,(i+1, i + window_size)保存的数据类型是元组tuple
    
    # 合并重叠的窗口
    merged_results_acidic = []
    merged_results_basic =[] #同前面,对酸性负电以及碱性正电氨基酸的窗口进行合并,设置初始化的空列表
    # 用于保存已经合并的窗口起点终点元组对,注意每次比较都是比较当前元组的起点和上一个已经合并的元组(也就是merged_results[-1])的终点
    
    #下面是合并酸性带负电的窗口
    if results_acidic:
        # 如果酸性负电窗口results列表不为空
        merged_results_acidic = [results_acidic[0]]
        # 将第一个窗口加入merged_results列表中,作为后续比对的起点
        for current in results_acidic[1:]:
            # 遍历results_acidic列表中剩余的窗口
            last = merged_results_acidic[-1]
            # 取出已经合并的窗口的最后一个窗口的元组对
            if current[0] <= last[1] + 1:  
            # 如果当前窗口与最后一个窗口有重叠,也就是当前窗口的起点小于等于已经合并的最后一个窗口的终点+1(overlap以及相邻)
                merged_results_acidic[-1] = (last[0], max(last[1], current[1]))
                # 更新已经合并的最后一个窗口的终点为当前窗口和已经合并的最后一个窗口的终点的最大值,总之就是更新最后一个窗口,起点不变,终点更新
            else:
                merged_results_acidic.append(current)
                # 如果当前窗口与最后一个窗口没有重叠,直接将当前窗口加入merged_results列表中,也就是当前窗口并没有重叠

    #下面是合并碱性带正电的窗口
    if results_basic:
        merged_results_basic = [results_basic[0]]
        for current in results_basic[1:]:
            last = merged_results_basic[-1]
            if current[0] <= last[1] + 1:
                merged_results_basic[-1] = (last[0], max(last[1], current[1]))
            else:
                merged_results_basic.append(current)
    return merged_results_acidic, merged_results_basic
    # 返回合并之后的酸性负电窗口和碱性正电窗口,各自是合并后的窗口起点和终点列表,且每一个元素是元组,后续需要解包赋值
    


def main(fasta_file, output_file,window_size, threshold):  
    #默认输出为tsv格式
    sequences = parse_fasta(fasta_file)
    #调用parse_fasta函数来解析FASTA文件,并返回一个字典sequences,其中键是蛋白质的ID,值是蛋白质的氨基酸序列(字符串)
    
    #处理正电荷block、负电荷block区域输出文件
    with open(output_file, 'w') as csvfile:
    #使用with语句打开一个输出文件(需要自己提供参数命名,'w'表示以写入模式打开
        writer = csv.writer(csvfile, delimiter='\t')
        #创建一个csv.writer对象,用于将数据写入csv文件,delimiter='\t'表示使用制表符作为字段分隔符
        writer.writerow(['Protein_Name', 'charged_block_start', 'charged_block_end', 'Sequence', 'charged_block_type'])
        #写入表头,即列名
        for protein_name, sequence in sequences.items():
            #sequences是一个字典,键是蛋白质的ID,值是蛋白质的氨基酸序列,现在从值中获取charged block区域起点以及终点的元组对列表
            merged_results_acidic, merged_results_basic= sliding_window_for_charged_block(sequence.replace('\n', ''),phospho_sites=[],window_size=window_size, threshold=threshold) 
            #.replace('\n', '')双重保证,如果前面fasta序列中没有去除换行符
            
            for results in merged_results_acidic:
                writer.writerow([protein_name, results[0], results[1], sequence[results[0]-1:results[1]], 'Acidic'])
            for results in merged_results_basic:
                writer.writerow([protein_name, results[0], results[1], sequence[results[0]-1:results[1]], 'Basic'])
            #将蛋白质的ID、charged block区域的起点和终点,以及对应的序列切片,以及是酸性还是碱性,写入csv文件中,注意切片是0-based的,而真实位置是1-based的
            #后续可以导入到R中使用tidyverse

    #如果需要统计信息并写入到{output_file}.stats文件中,可以参考我原本calc_NCR.py的代码,这里不再赘述        


#使用 argparse 模块来实现类似于 Linux 工具的命令行参数解析(包括生成帮助手册等)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="接受uniprot输入的蛋白质原始fasta序列文件(可以是一群蛋白质),计算每个窗口下的NCPR值,输出NCPR值大于等于阈值的窗口(也就是酸性带正电氨基酸block,或碱性带负电氨基酸block)的起点和终点")
    parser.add_argument("--fasta_file", help="输入的蛋白质FASTA文件路径")
    # parser.add_argument("--delimiter", help="输出文件的分隔符(如$'\\t'等),默认是制表符", default='\t')
    parser.add_argument("--window_size", type=int, default=10, help="滑动窗口大小,默认是10")
    parser.add_argument("--threshold", type=float, default=5, help="NCPR阈值,默认是5,注意是>=")
    parser.add_argument("--output_file", help="输出可变电荷块文件路径")
    # parser.add_argument("--help", action="help", help="显示帮助信息"),不需要手动添加 --help 选项,因为 argparse 模块已经自动添加了一个 --help 选项来显示帮助信息q

    args = parser.parse_args()
    main(args.fasta_file, args.output_file, args.window_size, args.threshold)

python3 calc_charged_block.py --fasta_file /mnt/sdb/zht/project/uniprot/uniprot_870.fasta --window_size 10 --threshold 5 --output_file uniprot_870_NCPR_10_5.tsv 

大概效果如下:

实际上是可以和IDR等各种bed区域信息都统一起来

下一步就是结构预测,或者是提取感兴趣的区域做个结构表位分析(motif,奇美拉/pymol中搜索motif表征之类的)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/942394.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

单片机锂电池电量电压检测

一、引言 &#xff08;一&#xff09;锂电池电量检测的重要性简述 在如今这个科技飞速发展的时代&#xff0c;众多电子设备都依赖锂电池来供电&#xff0c;像我们日常使用的智能手机、平板电脑、笔记本电脑&#xff0c;还有出行必备的电动自行车、电动汽车等等&#xff0c;锂…

支付宝订单码支付

1.订单码支付&#xff0c;首先下载官方网站提供的sdk包到你的项目中。 2.选择控制器复制官方文档的获取二维码相关的代码示例。打开sdk包中v2的index.php文件&#xff0c;这个才是你选择语言的具体代码。 3.引用里面所需要的类文件&#xff0c;文件下载到你的项目中后&#xf…

【HarmonyOS 5.0】第十二篇-ArkUI公共属性(一)

一、公共样式类属性 ArkUI框架提供的基础组件直接或者间接的继承自 CommonMethod &#xff0c; CommonMethod 中定义的属性样式属于公共样式。下面就来学习这些样式 1.1.尺寸设置 宽高设置 设置组件的宽高&#xff0c;缺省时使用组件自身内容的宽高&#xff0c;比如充满父布…

VTK知识学习(27)- 图像基本操作(二)

1、图像类型转换 1&#xff09;vtkImageCast 图像数据类型转换在数字图像处理中会频繁用到。一些常用的图像算子(例如梯度算子)在计算时出于精度的考虑&#xff0c;会将结果存储为float或double类型&#xff0c;但在图像显示时&#xff0c;一般要求图像为 unsigned char 类型,…

Go C编程 第6课 无人机 --- 计算旋转角

旋转的秘密---认识角度 rt、lt命令学习 goc电子课程 一、编程步骤 第一步 第二步 第三步 第四步 二、画“四轴无人机” &#xff08;一&#xff09;、画第一根机轴 &#xff08;二&#xff09;、画第二根机轴 &#xff08;三&#xff09;、画完整的无人机 三、画“多轴无人…

cursor保存更改操作技巧

1. 当我们在agent模式时&#xff0c;要求cursor更改代码时&#xff0c;cursor回答后&#xff0c;就已经更改了代码了&#xff0c;这时候就可以对程序进行编译和测试&#xff0c; 不一定先要点” accept“, 先测试如果没有问题再点“accept”&#xff0c;这样composer就会多一条…

graphRAG+llama3.2的MOOC课程资源问答系统

文章目录 参考代码地址anacondapycharmLLaMA 3传统ragGraphRAG初始化提示词微调 prompt tuning来创建更适应知识库的知识图谱使用语言模型&#xff08;LLM&#xff09;从每个文本块中提取实体、关系和声明。检索 query&#xff08;本地搜索&#xff08;Local Search&#xff09…

一键打断线(根据相交点打断)——CAD c# 二次开发

多条相交线根据交点一键打断&#xff0c;如下图&#xff1a; 部分代码如下: finally namespace IFoxDemo; public class Class1 {[CommandMethod("ddx")]public static void Demo(){//"ifox可以了".Print();Database db HostApplicationServices.Workin…

Websocket客户端从Openai Realtime api Sever只收到部分数据问题分析

目录 背景 分析 解决方案 背景 正常情况下&#xff0c;会从Openai Realtime api Sever收到正常的json数据,但是当返回音频数据时&#xff0c;总会返回非json数据。这是什么问题呢&#xff1f; 分析 期望的完整响应数据如下&#xff1a; {"session": {"inp…

flask后端开发(1):第一个Flask项目

目录 一、Helloworddebug、host、port的配置 一、Helloword 一般是会创建两个文件夹和app.py app.py from flask import FlaskappFlask(__name__)app.route(/) def hello_world():return Hello World!if __name__ __main__:app.run()右键运行这个py文件&#xff0c;消息绑定…

OAuth 2.0

简介 OAuth 是一种开放标准的授权协议或框架&#xff0c;它提供了一种安全的方式&#xff0c;使第三方应用程序能够访问用户在其他服务上的受保护资源&#xff0c;而无需共享用户的凭证&#xff08;如用户名和密码&#xff09;。OAuth 的核心思想是通过“授权令牌”来代替直接…

玩原神学编程-原神时钟

前言 最近喜欢玩原神这种开放世界探索的游戏&#xff08;还有黑神话、古墓丽影等&#xff09;&#xff0c;只能说纳塔版本的boss盾真的厚&#xff0c;萌新的我去打boss&#xff0c;从白天打到黑夜&#xff0c;黑夜再打到白天&#xff08;游戏里面的时间&#xff09;。 闲话结…

【Spring】深入解析 Spring 原理:Bean 的多方面剖析(源码阅读)

&#x1f525;个人主页&#xff1a; 中草药 &#x1f525;专栏&#xff1a;【Java】登神长阶 史诗般的Java成神之路 一、Bean的作用域 在 Java Spring 框架中&#xff0c;Bean 的作用域是一个关键概念&#xff0c;它决定了 Bean 的生命周期和实例化方式&#xff0c;对应用的性…

基于高德地图js api实现掩膜效果 中间矢量 周围卫星图

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>管网服务</title><style>html,body,#ma…

Vue.js组件(6):echarts组件

1 前言 本章主要对常用的echars图表展示进行基本的组件封装。使用该组件前需要在项目中引入echarts。官网&#xff1a;Apache ECharts npm install echarts --save 2 图表组件 2.1 折线图组件 组件属性&#xff1a;chartId&#xff0c;指定图表挂载div的id&#xff0c;注意不…

RCE常见姿势

文章目录 常见漏洞执行函数&#xff1a;1.系统命令执行函数2.代码执行函数 命令拼接符读取文件命令绕过&#xff1a;空格过滤绕过关键字绕过长度过滤绕过无参数命令执行绕过无字母数字绕过利用%0A截断利用回溯绕过利用create_function()代码注入无回显RCE1.反弹shell2.dnslog外…

selenium执行js

JS知识 获取元素 document.getElement 移除属性&#xff1a;removeAttribute("xx") 窗口移动&#xff1a;window.scrollTo(0, document.body.scrollHeight)方法 drivier.execute_script(js)场景&#xff1a; 日期选择框&#xff0c;不能输入&#xff0c;只能设置…

三维场景重建与3D高斯点渲染技术探讨

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;编程探索专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年12月25日10点11分 神秘男子影, 秘而不宣藏。 泣意深不见, 男子自持重, 子夜独自沉。 文章源地址(有视频)&#xff1a;链接h…

springboot启动不了 因一个spring-boot-starter-web底下的tomcat-embed-core依赖丢失

这个包丢失了 启动不了 起因是pom中加入了 <tomcat.version></tomcat.version>版本指定&#xff0c;然后idea自动编译后&#xff0c;包丢了&#xff0c;删除这个配置后再也找不回来&#xff0c; 这个包正常在 <dependency><groupId>org.springframe…

Java日志框架:log4j、log4j2、logback

文章目录 配置文件相关1. properties测试 2. XMl使用Dom4j解析XML Log4j与Log4j2日志门面 一、Log4j1.1 Logges1.2 Appenders1.3 Layouts1.4 使用1.5 配置文件详解1.5.1 配置根目录1.5.2 配置日志信息输出目的地Appender1.5.3 输出格式设置 二、Log4j22.1 XML配置文件解析2.2 使…