10X单细胞(10X空间转录组)TCR数据分析之TCRdist3(4)

前面已经说了很多的基础概念和分析算法,但是大家应该注意到了,前面的分析都限制在TCRαβ序列,分析还是具有一定的限制性,今天我们稍微做一下扩展,优化TCR分析距离的同时,分析γδ TCRs,and at-scale computation with sparse data representations and parallelized, byte-compiled code.文献在TCR meta-clonotypes for biomarker discovery with tcrdist3: identification of public, HLA restricted SARS-CoV-2 associated TCR features,这些文献都是具有承前启后的作用,这个专题,内容真的太多了。

来,看看分析框架

1、实验性抗原富集可以发现具有生化相似neighbors的 TCR

Searching for identical TCRs within a repertoire - arising either from clonal expansion or convergent nucleotide encoding of amino acids in the CDR3 - is a common strategy for identifying functionally important receptors。(这也是唯一实用的策略),然而,在缺乏实验富集程序的情况下,在大量样本中观察到具有相同氨基酸 TCR 序列的 T 细胞是很少见的。例如,在来自脐带血样本的 10,000 个 β 链 TCR 中,少于 1% 的 TCR 氨基酸序列被多次观察到,包括可能的克隆扩增(疾病确实会导致TCR的特异性扩增,这是研究的核心).
图片.png
  • 图注:TCR repertoire subsets obtained by single-cell sorting with peptide-MHC tetramers。

2、TCR biochemical neighborhood density is heterogeneous in antigen-enriched repertoires

We next investigated the proportion of unique TCRs with at least one biochemically similar neighbor among TCRs with the same putative antigen specificity.We and others have shown that a single peptide-MHC epitope is often recognized by many distinct TCRs with closely related amino acid sequences(识别抗原的TCR序列具有多样性,多对一的关系,这就复杂了),这个时候就必须找序列之间的相似性(也就是前面提到的TCR instance),以寻求共性。We observed the highest density neighborhoods within repertoires that were sorted based on peptide-MHC tetramer binding(看来刺激的作用很明显)。these observations suggest that biochemical neighborhood density is highly heterogeneous among TCRs and that it may depend on mechanisms of antigen-recognition as well as receptor V(D)J recombination biases。(按照这个情况,这个难以研究)。

3、Meta-clonotype radius can be tuned to balance a biomarker’s sensitivity and specificity

基于 TCR 的生物标志物的效用取决于 TCR 的抗原特异性 ,a key constraint on distance-based clustering is the presence of similar TCR sequences that may lack the ability to recognize the target antigen.(说白了就行要定义相似性的半径),To be useful, a meta-clonotype definition should be broad enough to capture multiple biochemically similar TCRs with shared antigen-recognition, but not excessively broad as to include a high proportion of non-specific TCRs, which might be found in unenriched background repertoires that are largely antigen-naïve(半径的大小要合适)。但是TCR“邻居”的相似性密度是异质的。
An ideal radius-defined meta-clonotype would include a high density of TCRs in antigen experienced individuals indicative of shared antigen specificity, yet a low density of TCRs among an antigen-naïve background.接下来就是寻找抗原转移性的TCR序列了。我们来看看分析的代码(TCRdist3)。

第一部分代码,TCRdist

看看输入的数据格式,跟我们10X分析出来的结果很类似
图片.png
来,现场教大家写代码
103338268-aa3ee180-4a32-11eb-8149-056fb385b33b.gif
默认参数
"""
If you just want a 'tcrdistances' using pre-set default setting.

    You can access distance matrices:
        tr.pw_alpha     - alpha chain pairwise distance matrix
        tr.pw_beta      - alpha chain pairwise distance matrix
        tr.pw_cdr3_a_aa - cdr3 alpha chain distance matrix
        tr.pw_cdr3_b_aa - cdr3 beta chain distance matrix
"""
import pandas as pd
from tcrdist.repertoire import TCRrep

df = pd.read_csv("dash.csv")
tr = TCRrep(cell_df = df, 
            organism = 'mouse', 
            chains = ['alpha','beta'], 
            db_file = 'alphabeta_gammadelta_db.tsv')

tr.pw_alpha
tr.pw_beta
tr.pw_cdr3_a_aa
tr.pw_cdr3_b_aa

调整一个默认参数
"""  
If you want 'tcrdistances' with changes over some parameters.
For instance you want to change the gap penalty on CDR3s to 5. 
"""
import pwseqdist as pw
import pandas as pd
from tcrdist.repertoire import TCRrep

df = pd.read_csv("dash.csv")
tr = TCRrep(cell_df = df, 
            organism = 'mouse', 
            chains = ['alpha','beta'], 
            compute_distances = False,
            db_file = 'alphabeta_gammadelta_db.tsv')

tr.kargs_a['cdr3_a_aa']['gap_penalty'] = 5 
tr.kargs_b['cdr3_b_aa']['gap_penalty'] = 5 

tr.compute_distances()

tr.pw_alpha
tr.pw_beta
人为完全控制距离的计算(对代码的水平要求有点高)
"""
If want a 'tcrdistances' AND you want control over EVERY parameter.
"""
import pwseqdist as pw
import pandas as pd
from tcrdist.repertoire import TCRrep

df = pd.read_csv("dash.csv")
tr = TCRrep(cell_df = df, 
            organism = 'mouse', 
            chains = ['alpha','beta'], 
            compute_distances = False,
            db_file = 'alphabeta_gammadelta_db.tsv')

metrics_a = {
    "cdr3_a_aa" : pw.metrics.nb_vector_tcrdist,
    "pmhc_a_aa" : pw.metrics.nb_vector_tcrdist,
    "cdr2_a_aa" : pw.metrics.nb_vector_tcrdist,
    "cdr1_a_aa" : pw.metrics.nb_vector_tcrdist}

metrics_b = {
    "cdr3_b_aa" : pw.metrics.nb_vector_tcrdist,
    "pmhc_b_aa" : pw.metrics.nb_vector_tcrdist,
    "cdr2_b_aa" : pw.metrics.nb_vector_tcrdist,
    "cdr1_b_aa" : pw.metrics.nb_vector_tcrdist }

weights_a= { 
    "cdr3_a_aa" : 3,
    "pmhc_a_aa" : 1,
    "cdr2_a_aa" : 1,
    "cdr1_a_aa" : 1}

weights_b = { 
    "cdr3_b_aa" : 3,
    "pmhc_b_aa" : 1,
    "cdr2_b_aa" : 1,
    "cdr1_b_aa" : 1}

kargs_a = {  
    'cdr3_a_aa' : 
        {'use_numba': True, 
        'distance_matrix': pw.matrices.tcr_nb_distance_matrix, 
        'dist_weight': 1, 
        'gap_penalty':4, 
        'ntrim':3, 
        'ctrim':2, 
        'fixed_gappos': False},
    'pmhc_a_aa' : {
        'use_numba': True,
        'distance_matrix': pw.matrices.tcr_nb_distance_matrix,
        'dist_weight':1,
        'gap_penalty':4,
        'ntrim':0,
        'ctrim':0,
        'fixed_gappos':True},
    'cdr2_a_aa' : {
        'use_numba': True,
        'distance_matrix': pw.matrices.tcr_nb_distance_matrix,
        'dist_weight': 1,
        'gap_penalty':4,
        'ntrim':0,
        'ctrim':0,
        'fixed_gappos':True},
    'cdr1_a_aa' : {
        'use_numba': True,
        'distance_matrix': pw.matrices.tcr_nb_distance_matrix,
        'dist_weight':1,
        'gap_penalty':4,
        'ntrim':0,
        'ctrim':0,
        'fixed_gappos':True}
    }
kargs_b= {  
    'cdr3_b_aa' : 
        {'use_numba': True, 
        'distance_matrix': pw.matrices.tcr_nb_distance_matrix, 
        'dist_weight': 1, 
        'gap_penalty':4, 
        'ntrim':3, 
        'ctrim':2, 
        'fixed_gappos': False},
    'pmhc_b_aa' : {
        'use_numba': True,
        'distance_matrix': pw.matrices.tcr_nb_distance_matrix,
        'dist_weight': 1,
        'gap_penalty':4,
        'ntrim':0,
        'ctrim':0,
        'fixed_gappos':True},
    'cdr2_b_aa' : {
        'use_numba': True,
        'distance_matrix': pw.matrices.tcr_nb_distance_matrix,
        'dist_weight':1,
        'gap_penalty':4,
        'ntrim':0,
        'ctrim':0,
        'fixed_gappos':True},
    'cdr1_b_aa' : {
        'use_numba': True,
        'distance_matrix': pw.matrices.tcr_nb_distance_matrix,
        'dist_weight':1,
        'gap_penalty':4,
        'ntrim':0,
        'ctrim':0,
        'fixed_gappos':True}
    }   

tr.metrics_a = metrics_a
tr.metrics_b = metrics_b

tr.weights_a = weights_a
tr.weights_b = weights_b

tr.kargs_a = kargs_a 
tr.kargs_b = kargs_b
只考虑不匹配的计算
"""
If you want "tcrdistances" using a different metric.

Here we illustrate the use a metric that uses the 
Needleman-Wunsch algorithm to align sequences and then 
calculate the number of mismatching positions (pw.metrics.nw_hamming_metric)

This method doesn't rely on Numba so it can run faster using multiple cpus.
"""
import pwseqdist as pw
import pandas as pd
from tcrdist.repertoire import TCRrep
import multiprocessing

df = pd.read_csv("dash.csv")
df = df.head(100) # for faster testing
tr = TCRrep(cell_df = df, 
            organism = 'mouse', 
            chains = ['alpha','beta'], 
            use_defaults=False,
            compute_distances = False,
            cpus = 1,
            db_file = 'alphabeta_gammadelta_db.tsv')

metrics_a = {
    "cdr3_a_aa" : pw.metrics.nw_hamming_metric ,
    "pmhc_a_aa" : pw.metrics.nw_hamming_metric ,
    "cdr2_a_aa" : pw.metrics.nw_hamming_metric ,
    "cdr1_a_aa" : pw.metrics.nw_hamming_metric }

metrics_b = {
    "cdr3_b_aa" : pw.metrics.nw_hamming_metric ,
    "pmhc_b_aa" : pw.metrics.nw_hamming_metric ,
    "cdr2_b_aa" : pw.metrics.nw_hamming_metric ,
    "cdr1_b_aa" : pw.metrics.nw_hamming_metric  }

weights_a = { 
    "cdr3_a_aa" : 1,
    "pmhc_a_aa" : 1,
    "cdr2_a_aa" : 1,
    "cdr1_a_aa" : 1}

weights_b = { 
    "cdr3_b_aa" : 1,
    "pmhc_b_aa" : 1,
    "cdr2_b_aa" : 1,
    "cdr1_b_aa" : 1}

kargs_a = {  
    'cdr3_a_aa' : 
        {'use_numba': False},
    'pmhc_a_aa' : {
        'use_numba': False},
    'cdr2_a_aa' : {
        'use_numba': False},
    'cdr1_a_aa' : {
        'use_numba': False}
    }
kargs_b = {  
    'cdr3_b_aa' : 
        {'use_numba': False},
    'pmhc_b_aa' : {
        'use_numba': False},
    'cdr2_b_aa' : {
        'use_numba': False},
    'cdr1_b_aa' : {
        'use_numba': False}
    }

tr.metrics_a = metrics_a
tr.metrics_b = metrics_b

tr.weights_a = weights_a
tr.weights_b = weights_b

tr.kargs_a = kargs_a 
tr.kargs_b = kargs_b

tr.compute_distances()

tr.pw_cdr3_b_aa
tr.pw_beta
自定义距离度量
"""
If you want a tcrdistance, but you want to use your own metric. 
(A valid metric takes two strings and returns a numerical distance).  

def my_own_metric(s1,s2):   
    return Levenshtein.distance(s1,s2)
"""
import pwseqdist as pw
import pandas as pd
from tcrdist.repertoire import TCRrep
import multiprocessing

df = pd.read_csv("dash.csv")
df = df.head(100) # for faster testing
tr = TCRrep(cell_df = df, 
            organism = 'mouse', 
            chains = ['alpha','beta'], 
            use_defaults=False,
            compute_distances = False,
            cpus = 1,
            db_file = 'alphabeta_gammadelta_db.tsv')

metrics_a = {
    "cdr3_a_aa" : my_own_metric ,
    "pmhc_a_aa" : my_own_metric ,
    "cdr2_a_aa" : my_own_metric ,
    "cdr1_a_aa" : my_own_metric }

metrics_b = {
    "cdr3_b_aa" : my_own_metric ,
    "pmhc_b_aa" : my_own_metric ,
    "cdr2_b_aa" : my_own_metric,
    "cdr1_b_aa" : my_own_metric }

weights_a = { 
    "cdr3_a_aa" : 1,
    "pmhc_a_aa" : 1,
    "cdr2_a_aa" : 1,
    "cdr1_a_aa" : 1}

weights_b = { 
    "cdr3_b_aa" : 1,
    "pmhc_b_aa" : 1,
    "cdr2_b_aa" : 1,
    "cdr1_b_aa" : 1}

kargs_a = {  
    'cdr3_a_aa' : 
        {'use_numba': False},
    'pmhc_a_aa' : {
        'use_numba': False},
    'cdr2_a_aa' : {
        'use_numba': False},
    'cdr1_a_aa' : {
        'use_numba': False}
    }
kargs_b = {  
    'cdr3_b_aa' : 
        {'use_numba': False},
    'pmhc_b_aa' : {
        'use_numba': False},
    'cdr2_b_aa' : {
        'use_numba': False},
    'cdr1_b_aa' : {
        'use_numba': False}
    }

tr.metrics_a = metrics_a
tr.metrics_b = metrics_b

tr.weights_a = weights_a
tr.weights_b = weights_b

tr.kargs_a = kargs_a 
tr.kargs_b = kargs_b

tr.compute_distances()

tr.pw_cdr3_b_aa
tr.pw_beta
I want tcrdistances, but I hate OOP
"""
If you don't want to use OOP, but you I still want a multi-CDR 
tcrdistances on a single chain, using you own metric 

def my_own_metric(s1,s2):   
    return Levenshtein.distance(s1,s2)    
"""
import multiprocessing
import pandas as pd
from tcrdist.rep_funcs import _pws, _pw

df = pd.read_csv("dash2.csv")

metrics_b = {
    "cdr3_b_aa" : my_own_metric ,
    "pmhc_b_aa" : my_own_metric ,
    "cdr2_b_aa" : my_own_metric ,
    "cdr1_b_aa" : my_own_metric }

weights_b = { 
    "cdr3_b_aa" : 1,
    "pmhc_b_aa" : 1,
    "cdr2_b_aa" : 1,
    "cdr1_b_aa" : 1}

kargs_b = {  
    'cdr3_b_aa' : 
        {'use_numba': False},
    'pmhc_b_aa' : {
        'use_numba': False},
    'cdr2_b_aa' : {
        'use_numba': False},
    'cdr1_b_aa' : {
        'use_numba': False}
    }

dmats =  _pws(df = df , 
            metrics = metrics_b, 
            weights = weights_b, 
            kargs   = kargs_b , 
            cpu     = 1, 
            uniquify= True, 
            store   = True)

print(dmats.keys())
仅考虑CDR3
"""
If you hate object oriented programming, just show me the functions. 
No problem. 

Maybe you only care about the CDR3 on the beta chain.

def my_own_metric(s1,s2):   
    return Levenshtein.distance(s1,s2)
"""  
import multiprocessing
import pandas as pd
from tcrdist.rep_funcs import _pws, _pw

df = pd.read_csv("dash2.csv")

# 
dmat = _pw( metric = my_own_metric,
            seqs1 = df['cdr3_b_aa'].values,
            ncpus=2,
            uniqify=True,
            use_numba=False)
I want tcrdistances but I want to keep my variable names
"""
You want a 'tcrdistance' but you don't want to bother with the tcrdist3 framework. 

Note that the columns names are completely arbitrary under this 
framework, so one can directly compute a tcrdist on a 
AIRR, MIXCR, VDJTools, or other formated file without any
reformatting.
""" 
import multiprocessing
import pandas as pd
import pwseqdist as pw
from tcrdist.rep_funcs import _pws, _pw  

df_airr = pd.read_csv("dash_beta_airr.csv")

# Choose the metrics you want to apply to each CDR
metrics = { 'cdr3_aa' : pw.metrics.nb_vector_tcrdist,
            'cdr2_aa' : pw.metrics.nb_vector_tcrdist,
            'cdr1_aa' : pw.metrics.nb_vector_tcrdist}

# Choose the weights that are right for you.
weights = { 'cdr3_aa' : 3,
            'cdr2_aa' : 1,
            'cdr1_aa' : 1}

# Provide arguments for the distance metrics 
kargs = {   'cdr3_aa' : {'use_numba': True, 'distance_matrix': pw.matrices.tcr_nb_distance_matrix, 'dist_weight': 1, 'gap_penalty':4, 'ntrim':3, 'ctrim':2, 'fixed_gappos':False},
            'cdr2_aa' : {'use_numba': True, 'distance_matrix': pw.matrices.tcr_nb_distance_matrix, 'dist_weight': 1, 'gap_penalty':4, 'ntrim':0, 'ctrim':0, 'fixed_gappos':True},
            'cdr1_aa' : {'use_numba': True, 'distance_matrix': pw.matrices.tcr_nb_distance_matrix, 'dist_weight': 1, 'gap_penalty':4, 'ntrim':0, 'ctrim':0, 'fixed_gappos':True}}
            
# Here are your distance matrices
from tcrdist.rep_funcs import _pws

dmats = _pws(df = df_airr,
         metrics = metrics, 
         weights= weights, 
         kargs=kargs, 
         cpu = 1, 
         store = True)

dmats['tcrdist']
I want to use TCRrep but I want to keep my variable names
"""
If you already have a clones file and want 
to compute 'tcrdistances' on a DataFrame with 
custom columns names.

Set:
1. Assign TCRrep.clone_df
2. set infer_cdrs = False,
3. compute_distances = False
4. deduplicate = False
5. customize the keys for metrics, weights, and kargs with the lambda
    customize = lambda d : {new_cols[k]:v for k,v in d.items()} 
6. call .calculate_distances()
"""
import pwseqdist as pw
import pandas as pd
from tcrdist.repertoire import TCRrep

new_cols = {'cdr3_a_aa':'c3a', 'pmhc_a_aa':'pa', 'cdr2_a_aa':'c2a','cdr1_a_aa':'c1a',
            'cdr3_b_aa':'c3b', 'pmhc_b_aa':'pb', 'cdr2_b_aa':'c2b','cdr1_b_aa':'c1b'}

df = pd.read_csv("dash2.csv").rename(columns = new_cols) 

tr = TCRrep(
        cell_df = df,
        clone_df = df,              #(1)
        organism = 'mouse', 
        chains = ['alpha','beta'],
        infer_all_genes = True, 
        infer_cdrs = False,         #(2)s
        compute_distances = False,  #(3)
        deduplicate=False,          #(4)
        db_file = 'alphabeta_gammadelta_db.tsv')

customize = lambda d : {new_cols[k]:v for k,v in d.items()} #(5)
tr.metrics_a = customize(tr.metrics_a)
tr.metrics_b = customize(tr.metrics_b)
tr.weights_a = customize(tr.weights_a)
tr.weights_b = customize(tr.weights_b)
tr.kargs_a = customize(tr.kargs_a)
tr.kargs_b = customize(tr.kargs_b)

tr.compute_distances() #(6)

# Notice that pairwise results now have custom names 
tr.pw_c3b
tr.pw_c3a
tr.pw_alpha
tr.pw_beta

####### I want distances from 1 TCR to many TCRs

"""
If you just want a 'tcrdistances' of some target seqs against another set.

(1) cell_df is asigned the first 10 cells in dash.csv
(2) compute tcrdistances with default settings.
(3) compute rectangular distance between clone_df and df2.
(4) compute rectangular distance between clone_df and any 
arbtirary df3, which need not be associated with the TCRrep object.
(5) compute rectangular distance with only a subset of the TCRrep.clone_df
"""
import pandas as pd
from tcrdist.repertoire import TCRrep

df = pd.read_csv("dash.csv")
df2 = pd.read_csv("dash2.csv")
df = df.head(10)                        #(1)
tr = TCRrep(cell_df = df,               #(2)
            df2 = df2, 
            organism = 'mouse', 
            chains = ['alpha','beta'], 
            db_file = 'alphabeta_gammadelta_db.tsv')

assert tr.pw_alpha.shape == (10,10) 
assert tr.pw_beta.shape  == (10,10)

tr.compute_rect_distances()             # (3) 
assert tr.rw_alpha.shape == (10,1924) 
assert tr.rw_beta.shape  == (10,1924)

df3 = df2.head(100)

tr.compute_rect_distances(df = tr.clone_df, df2 = df3)  # (4) 
assert tr.rw_alpha.shape == (10,100) 
assert tr.rw_beta.shape  == (10,100)

tr.compute_rect_distances(  df = tr.clone_df.iloc[0:2,], # (5)
                            df2 = df3)  
assert tr.rw_alpha.shape == (2,100) 
assert tr.rw_beta.shape  == (2,100)

个性化程度真的高,也确实很难

生活很好,有你更好,下一篇我们继续分享TCRdist3的分析代码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容