从GEO数据下载到limma差异分析,再到topGO富集分析

一个失败案例,不要点进来


GSE2034 topGO

1. DEG

1.1 下载GEO数据

library(GEOquery)
f <- 'GSE2034.Rdata'
if(!file.exists(f)){
  gset <- getGEO('GSE2034',destdir = '.',
                 AnnotGPL = F,
                 getGPL = F)
  save(gset,file=f)
}
load('GSE2034.Rdata')

1.2 表达矩阵

eset <- gset[[1]]
expmat <- exprs(eset)

## 检查数据是否已经经过log转换,并进行log2转换
ex <- expmat
qx <- as.numeric(quantile(ex, c(0., 0.25, 0.5, 0.75, 0.99, 1.0), na.rm=T))

LogC <- (qx[5] > 100) ||
  (qx[6]-qx[1] > 50 && qx[2] > 0) ||
  (qx[2] > 0 && qx[2] < 1 && qx[4] > 1 && qx[4] < 2)

if (LogC) { ex[which(ex <= 0)] <- NaN
expmat <- log2(ex)
print("log2 transform finished")}else{print("log2 transform not needed")}
# [1] "log2 transform finished"

fit <- lmFit(expmat, design)
fit1 <- contrasts.fit(fit, contrast.matrix) 
fit2 <- eBayes(fit, 0.01)

DEGstb <- topTable(fit2, coef=1, n=Inf)
DEGstb <- DEGstb[order(DEGstb[,4], decreasing = F),]

before:

after:

1.3 设计矩阵和对比矩阵

pdata <- pData(eset)
group_list <- pdata$`bone relapses (1=yes, 0=no):ch1`
library(limma)
design <- model.matrix(~0+factor(group_list))
colnames(design) <- c("free", "relapses")
rownames(design) <- colnames(expmat)
contrast.matrix <- makeContrasts('free-relapses',levels = design) 

1.4 差异分析

fit <- lmFit(expmat, design)
fit1 <- contrasts.fit(fit, contrast.matrix) 
fit2 <- eBayes(fit1)
DEGstb <- topTable(fit2, coef=1, n=Inf)
DEGmtx <- na.omit(DEGstb)
head(DEGmtx)
#                  logFC   AveExpr         t      P.Value    adj.P.Val
# 209835_x_at  0.7010262 10.616209  6.251930 1.460149e-09 3.253649e-05
# 212014_x_at  0.7264678 10.372145  5.927511 8.785625e-09 9.788505e-05
# 204490_s_at  0.4928252  9.788234  5.558565 6.206262e-08 4.609804e-04
# 209380_s_at -0.5373120 10.062837 -5.421342 1.253664e-07 6.983850e-04
# 209831_x_at  0.2666780 10.594035  5.228288 3.295703e-07 1.338433e-03
# 204489_s_at  0.5619588 10.302607  5.181790 4.142994e-07 1.338433e-03
#                     B result
# 209835_x_at 11.154503     UP
# 212014_x_at  9.526820     UP
# 204490_s_at  7.757191     UP
# 209380_s_at  7.121927   DOWN
# 209831_x_at  6.249843    NOT
# 204489_s_at  6.043635     UP

1.5 火山图

logFC_cutoff <- with(DEGmtx, mean(abs(logFC)) + 
                       2*sd(abs(logFC)))

DEGdf$result <- ifelse(DEGdf$P.Value < 0.05 &
                                    abs(DEGdf$logFC) > logFC_cutoff,
                                  ifelse(DEGdf$logFC >logFC_cutoff,
                                         "UP", "DOWN"), "NOT")

title <- paste0('Cutoff for logFC is ',round(logFC_cutoff,3), 
                '\nThe number of up gene is ',nrow(DEGmtx[DEGmtx$result =='UP',]) ,
                '\nThe number of down gene is ',nrow(DEGmtx[DEGmtx$result =='DOWN',]))

library(ggplot2)
ggplot(data=DEGmtx, aes(x=logFC, y=-log10(P.Value), color=result)) +
  geom_point(alpha=0.4, size=1.75) +
  theme_set(theme_set(theme_bw(base_size=20))) +
  xlab("log2 fold change") + ylab("-log10 p-value") +
  ggtitle( title ) + theme(plot.title = element_text(size=15,hjust = 0.5)) +
  scale_colour_manual(values = c('blue','black','red'))

1.6 PCA图

pca_data <- prcomp(t(expmat),scale=TRUE)
pcx <- data.frame(pca_data$x)
pcr <- cbind(samples=rownames(pcx),group_list, pcx) 
ggplot(pcr, aes(PC1, PC2)) + geom_point(size=5, aes(color = group_list)) +
  geom_text(aes(label="samples"),hjust=-0.1, vjust=-0.3)

这个数据中的分组:bone relapses: 1=yes, 0=no

分别是加样本名和不加样本名的亚子:

1.7 热图

选择&调整矩阵,设置各种参数:

chosedgene <- rownames(DEGmtx)[1:50]
chosedmatx <- expmat[chosedgene,]      

re <- subset(pdata,pdata[,28]==1)
re <- row.names(re)
fr <- row.names(subset(pdata,pdata[,28]==0))
sampleorder <- as.vector(c(fr,re))

do_mtx <- t(scale(t(chosedmatx)))
do_mtx[do_mtx > 2] = 2
do_mtx[do_mtx < -2] = -2

do_mtx <- do_mtx[,sampleorder]

chosedprob <- row.names(do_mtx)
chosedsymbol <- AnnotationDbi::select(hgu133a.db, chosedprob,
                                      "SYMBOL",
                                      "PROBEID")[,2]
row.names(do_mtx) <- chosedsymbol

anno_col <- data.frame(sample=factor(rep(c("free", "relapses"), c(217, 69))))
row.names(anno_col) <- colnames(do_mtx)
ann_color <- list(sample=c(free = "#3a5fcd", relapses = "#cd0000"))

画图:

png(filename = "GSE2034_hm_nmlmatrix_nocluscol.png", height = 5000, width = 6000,
   res = 500, units = "px")

pheatmap::pheatmap(do_mtx, scale = "none", color = color,
                   annotation_col = anno_col,
                   annotation_colors = ann_color,
                   fontsize = 8, fontsize_row = 8, 
                   show_colnames = F,
                   cluster_cols = F,
                   main = "GSE2034: free vs relapses HEATMAP (delected outliers)",)
dev.off()

2. topGO

2.1 topGO: Predefined list of interesting genes

过表征分析 (over representation analysis, ORA)

Tests based on gene counts. This is the most popular family of tests, given that it only requires the presence of a list of interesting genes and nothing more. Tests like Fisher's exact test, Hypegeometric test and binomial test belong to this family. Draghici et al. (2006) .

2.1.1 构建 topGOdata 对象

构建 geneID2GO list

library(topGO)
library(hgu133a.db)
library(dplyr)

probe_id <- row.names(expmat)
entrez_id <- AnnotationDbi::select(hgu133a.db,
                                   probe_id,
                                   "ENTREZID",
                                   "PROBEID")[,2]

geneID2GOtb <- AnnotationDbi::select(hgu133a.db, entrez_id,
                                     "GO", "ENTREZID")
geneID2GOsb <- geneID2GOtb %>%
  group_by(ENTREZID) %>%
  summarise(GO=list(GO))
geneID2GO <- geneID2GOsb$GO
names(geneID2GO) <- rownames(geneID2GOsb)

构建 geneList

myInterestingDEG <- subset(DEGdf, abs(DEGdf$logFC) > 1)
myInterestingDEG <- subset(myInterestingDEG, myInterestingDEG$adj.P.Val < 0.05)
logFC AveExpr t P.Value adj.P.Val B result
214777_at 1.621019 7.578387 4.723681 3.63E-06 0.005771 4.093824 UP
216365_x_at 1.340734 5.631291 4.599642 6.35E-06 0.007075 3.592027 UP
217157_x_at 1.02288 8.543867 4.564419 7.43E-06 0.007348 3.45161 UP
216984_x_at 1.482014 7.826338 4.56344 7.46E-06 0.007348 3.447722 UP
217378_x_at 1.740957 8.055472 4.500623 9.85E-06 0.008778 3.199637 UP
217258_x_at 1.217204 7.180579 4.485677 1.05E-05 0.009013 3.14105 UP
217148_x_at 1.236926 9.662857 4.468183 1.14E-05 0.009236 3.072682 UP
211798_x_at 1.129989 7.765948 4.411513 1.45E-05 0.009236 2.852795 UP
214669_x_at 1.146124 11.22506 4.382578 1.65E-05 0.009236 2.741463 UP
211644_x_at 1.51252 8.895729 4.353739 1.86E-05 0.00958 2.631127 UP
215176_x_at 1.554616 9.781743 4.350516 1.89E-05 0.00958 2.618838 UP
211637_x_at 1.319264 6.128292 4.291975 2.42E-05 0.01036 2.396959 UP
215121_x_at 1.279395 12.98246 4.247872 2.92E-05 0.011018 2.231533 UP
216576_x_at 1.355247 8.468197 4.198257 3.59E-05 0.011951 2.047218 UP
204540_at -1.06758 8.610066 -4.19044 3.71E-05 0.011982 2.018367 DOWN
216401_x_at 1.475812 7.711042 4.180749 3.86E-05 0.011983 1.982631 UP
207430_s_at -1.3399 6.097727 -4.15096 4.37E-05 0.012368 1.873294 DOWN
213201_s_at -1.27114 6.641464 -4.13499 4.66E-05 0.01267 1.814941 DOWN
215214_at 1.239997 7.376844 4.125793 4.84E-05 0.012763 1.781441 UP
204933_s_at 1.073296 4.540879 4.031533 7.10E-05 0.015467 1.441822 UP
217281_x_at 1.355866 5.417214 4.015399 7.58E-05 0.015784 1.384391 UP
214858_at -1.08985 6.455748 -4.00976 7.75E-05 0.015861 1.36435 DOWN
216510_x_at 1.132241 6.304655 3.99392 8.26E-05 0.016195 1.308245 UP
215379_x_at 1.123251 11.77926 3.943786 0.000101 0.016689 1.131931 UP
211634_x_at 1.014891 6.92074 3.93688 0.000104 0.016689 1.107799 UP
210297_s_at -1.201 6.705364 -3.93263 0.000105 0.016689 1.092959 DOWN
209138_x_at 1.136452 13.28617 3.9291 0.000107 0.016689 1.080658 UP
211645_x_at 1.244501 9.308405 3.889339 0.000125 0.017262 0.942694 UP
216491_x_at 1.292163 5.893802 3.845647 0.000148 0.018642 0.79254 UP
216560_x_at 1.278329 5.810675 3.778756 0.000192 0.020615 0.565602 UP
221286_s_at 1.10116 7.542805 3.777608 0.000192 0.020615 0.561739 UP
206391_at 1.082072 6.946425 3.505906 0.000528 0.031189 -0.32275 UP
205509_at -1.20626 8.869439 -3.48369 0.000572 0.032322 -0.39241 DOWN
209374_s_at 1.034312 10.40725 3.443591 0.000659 0.034236 -0.51714 UP
204623_at -1.04503 11.21622 -3.25463 0.001271 0.045188 -1.08699 DOWN
geneNames <- names(geneID2GO) 
myInterestingGenestb <- AnnotationDbi::select(hgu133a.db, row.names(myInterestingDEG),
                                            "ENTREZID", "PROBEID")

myInterestingGenes <- geneNames[match(myInterestingGenestb[,2], 
                                      geneNames)]
myInterestingGenes <- na.omit(myInterestingGenes)

ORAgeneList <- factor(as.integer(geneNames %in% myInterestingGenes)) 
names(ORAgeneList) <- geneNames 
str(ORAgeneList) 
#  Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
#  - attr(*, "names")= chr [1:13755] "10" "100" "1000" "10000" ...

构建 topGOdata 对象:

ORAGOdata <- new("topGOdata",
              ontology = "BP",
              allGenes = ORAgeneList,
              annot = annFUN.gene2GO,
              gene2GO = geneID2GO)
# 
# Building most specific GOs .....
#   ( 11302 GO terms found. )
# 
# Build GO DAG topology ..........
#   ( 15326 GO terms and 36394 relations. )
# 
# Annotating nodes ...............
#   ( 11973 genes annotated to the GO terms. )
ORAGOdata
# 
# ------------------------- topGOdata object -------------------------
# 
#  Description:
#    -   
# 
#  Ontology:
#    -  BP 
# 
#  13755 available genes (all genes from the array):
#    - symbol:  10 100 1000 10000 100008586  ...
#    - 29  significant genes. 
# 
#  11973 feasible genes (genes that can be used in the analysis):
#    - symbol:  10 100 1000 10000 10001  ...
#    - 21  significant genes. 
# 
#  GO graph (nodes with at least  1  genes):
#    - a graph with directed edges
#    - number of nodes = 15326 
#    - number of edges = 36394 
# 
# ------------------------- topGOdata object -------------------------
# 

2.1.2 富集分析

we perform a classical enrichment analysis by testing the over-representation of GO terms within the group of di erentially expressed genes.

ORAresultFisher <- runTest(ORAGOdata, algorithm = "classic", statistic = "fisher") 
# 
#            -- Classic Algorithm -- 
# 
#        the algorithm is scoring 449 nontrivial nodes
#        parameters: 
#            test statistic: fisher
# 
ORAresultFisher
# 
# Description:  
# Ontology: BP 
# 'classic' algorithm with the 'fisher' test
# 15326 GO terms scored: 86 terms with p < 0.01
# Annotation data:
#     Annotated genes: 11973 
#     Significant genes: 21 
#     Min. no. of genes annotated to a GO: 1 
#     Nontrivial nodes: 449 

使用 topGO 包时,所构建的 topGOdata 包含了 gene universe (ORAGOdata) 和 p-value (GSEAGOdata), ORA 和 GSEA 对应的统计方法可以同时应用于同一个 topGOdata 对象。所以区别只是基于不同的已知信息,构建 topGOdata 的方法不同。(maybe?

基于 classicelim 算法,对 ORAdata 进行 Kolmogorov-Smirnov test:

ORAresultKS <- runTest(ORAGOdata, algorithm = "classic", statistic = "ks") 
# 
#            -- Classic Algorithm -- 
# 
#        the algorithm is scoring 15326 nontrivial nodes
#        parameters: 
#            test statistic: ks
#            score order: increasing
ORAresultKS
# 
# Description:  
# Ontology: BP 
# 'classic' algorithm with the 'ks' test
# 15326 GO terms scored: 903 terms with p < 0.01
# Annotation data:
#     Annotated genes: 11973 
#     Significant genes: 21 
#     Min. no. of genes annotated to a GO: 1 
#     Nontrivial nodes: 15326 
ORAresult.elim <- runTest(ORAGOdata, algorithm = "elim", statistic = "ks") 
# 
#            -- Elim Algorithm -- 
# 
#        the algorithm is scoring 15326 nontrivial nodes
#        parameters: 
#            test statistic: ks
#            cutOff: 0.01
#            score order: increasing
# ...
ORAresult.elim
# 
# Description:  
# Ontology: BP 
# 'elim' algorithm with the 'ks : 0.01' test
# 15326 GO terms scored: 291 terms with p < 0.01
# Annotation data:
#     Annotated genes: 11973 
#     Significant genes: 21 
#     Min. no. of genes annotated to a GO: 1 
#     Nontrivial nodes: 15326 

2.1.3 Summary

ORAallRes <- GenTable(ORAGOdata,classicFisher = ORAresultFisher,
                   classicKS = ORAresultKS,
                   elimKS = ORAresult.elim,
                   orderBy = "elimKS",
                   ranksOf = "classicFisher",
                   topNodes = 20)
GO.ID Term Annotated Significant Expected Rank in classicFisher classicFisher classicKS elimKS
1 GO:0030198 extracellular matrix organization 303 1 0.53 233 0.42 2.20E-11 4.10E-08
2 GO:0019886 antigen processing and presentation of e... 88 0 0.15 449 1 5.50E-08 5.50E-08
3 GO:0098664 G protein-coupled serotonin receptor sig... 23 0 0.04 450 1 7.60E-08 7.60E-08
4 GO:0006120 mitochondrial electron transport, NADH t... 42 0 0.07 451 1 2.10E-07 2.10E-07
5 GO:0007190 activation of adenylate cyclase activity 29 0 0.05 452 1 5.10E-07 5.10E-07
6 GO:0060337 type I interferon signaling pathway 87 0 0.15 453 1 3.70E-06 3.70E-06
7 GO:0007193 adenylate cyclase-inhibiting G protein-c... 63 0 0.11 454 1 1.70E-08 4.80E-06
8 GO:0006069 ethanol oxidation 9 0 0.02 455 1 4.90E-06 4.90E-06
9 GO:0060078 regulation of postsynaptic membrane pote... 107 0 0.19 456 1 5.60E-07 5.70E-06
10 GO:0007155 cell adhesion 1134 0 1.99 457 1 1.30E-06 6.20E-06
11 GO:0042531 positive regulation of tyrosine phosphor... 54 0 0.09 458 1 6.60E-06 6.60E-06
12 GO:0000184 nuclear-transcribed mRNA catabolic proce... 114 0 0.2 459 1 9.90E-06 9.90E-06
13 GO:0048013 ephrin receptor signaling pathway 81 0 0.14 460 1 1.10E-05 1.10E-05
14 GO:0006749 glutathione metabolic process 43 0 0.08 461 1 1.40E-05 1.40E-05
15 GO:0006123 mitochondrial electron transport, cytoch... 13 0 0.02 462 1 1.40E-05 1.40E-05
16 GO:0009636 response to toxic substance 446 0 0.78 463 1 1.00E-08 1.70E-05
17 GO:1901687 glutathione derivative biosynthetic proc... 19 0 0.03 464 1 2.70E-05 2.70E-05
18 GO:1901701 cellular response to oxygen-containing c... 924 1 1.62 335 0.82 3.30E-06 3.20E-05
19 GO:1904322 cellular response to forskolin 10 0 0.02 465 1 3.20E-05 3.20E-05
20 GO:0097267 omega-hydroxylase P450 pathway 9 0 0.02 466 1 3.50E-05 3.50E-05

2.2 topGO: Using the genes score

基因富集分析 (gene set enrichment analysis, GSEA)

Tests based on gene scores or gene ranks. It includes Kolmogorov-Smirnov like tests (also known as GSEA), Gentleman's Category, t-test, etc. Ackermann and Strimmer (2009).

2.2.1 构建 topGOdata 对象

构建 geneList

GSEAgeneList <- subset(DEGdf, select = "adj.P.Val")
GSEAgeneList <- as.numeric(GSEAgeneList[,1])
names(GSEAgeneList) <- row.names(DEGdf)
str(GSEAgeneList)
#  Named num [1:22283] 3.25e-05 9.79e-05 4.61e-04 6.98e-04 1.34e-03 ...
#  - attr(*, "names")= chr [1:22283] "209835_x_at" "212014_x_at" "204490_s_at" "209380_s_at" ...

通过函数 topDiffGenes() 筛选q值小于0.01的基因。

topDiffGenes <- function(allScore) {
  return(allScore < 0.01)
}
length(GSEAgeneList)
# [1] 22283
sum(topDiffGenes(GSEAgeneList))
# [1] 50

构建 topGOdata 对象:

GSEAGOdata <- new("topGOdata",
                  description = "GO analysis of GSE2034 data; free vs relapses",
                  ontology = "BP",
                  allGenes = GSEAgeneList,
                  geneSel = topDiffGenes,
                  nodeSize = 5,
                  annot = annFUN.db,
                  affyLib = "hgu133a.db")
# 
# Building most specific GOs .....
#   ( 11302 GO terms found. )
# 
# Build GO DAG topology ..........
#   ( 15326 GO terms and 36394 relations. )
# 
# Annotating nodes ...............
#   ( 19375 genes annotated to the GO terms. )

GSEAGOdata
# ------------------------- topGOdata object -------------------------
# 
#  Description:
#    -  GO analysis of GSE2034 data; free vs relapses 
# 
#  Ontology:
#    -  BP 
# 
#  22283 available genes (all genes from the array):
#    - symbol:  209835_x_at 212014_x_at 204490_s_at 209380_s_at 209831_x_at  ...
#    - score :  3.25364921e-05 9.78850462e-05 0.000460980422 0.000698385002 0.0013384333  ...
#    - 50  significant genes. 
# 
#  19375 feasible genes (genes that can be used in the analysis):
#    - symbol:  209835_x_at 212014_x_at 204490_s_at 209380_s_at 209831_x_at  ...
#    - score :  3.25364921e-05 9.78850462e-05 0.000460980422 0.000698385002 0.0013384333  ...
#    - 47  significant genes. 
# 
#  GO graph (nodes with at least  5  genes):
#    - a graph with directed edges
#    - number of nodes = 10532 
#    - number of edges = 24696 
# 
# ------------------------- topGOdata object -------------------------
# 

2.2.2 富集分析

2.2.3 Summary

GenTable 导出表格

未完不更

References


最后,向大家隆重推荐生信技能树的一系列干货!

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

推荐阅读更多精彩内容