「代码记录」WGCNA篇

Nov 21, 2019更新 => 修正部分function代码,实现了导出gene2module

目的

最近有个项目又准备跑WGCNA,之前跑通过一个,然后很久没碰,都快忘了,这一年多对R又多了一丢丢的理解,干脆给他切分了几个步骤,分别写成了function。这里保存记录下,不过好像只适用于我自己的项目,如果大家有需求的话有些东西还是要改一下的,WGCNA的流程可不是复制一套别人的代码,然后点点点就出来结果的,还是需要可以读懂大部分的代码,脑子清楚自己要干啥。

参考内容

如有侵权,联系我删除!!

这里参考了简书多为大佬的WGCNA教程,之前只是懵懵懂懂ctrl c+v然后套数据,然后跑出个结果,这次折腾了这么久,也看懂了一部分,当然,总体还是ctrl c+v,但是多了一些理解在里面,这里做个记录,以后再有直接拿来用。

  1. 输入数据清洗,样品PCA,热图; 参考:「WGCNA-FAQ」「 生信技能树-这个WGCNA作业终于有学徒完成了!
  2. WGCNA常规流程包括sft,module,module-trait, hubgene: 「WGCNA分析,简单全面的最新教程」「 STEP6:WGCNA相关性分析

代码记录

###########################################
#       Assignment: WGCNA
#       Date: Nov 17,2019   
#       Author: Shawn Wang
###########################################

##=====01.准备========

setwd("/Volumes/FileManage/06.360Drive/02.heterosis_project/08.4133BtGroup/03.WGCNA/")
options(stringsAsFactors = F)
library(edgeR)
library(WGCNA)
library(dplyr)
library(reshape2)
library(stringr)
library(tidyverse)
library(FactoMineR)
library(factoextra)
library(pheatmap)
source("/Volumes/FileManage/06.360Drive/02.heterosis_project/09.code/11.01.PCAandHeatmap.R")
source("/Volumes/FileManage/06.360Drive/02.heterosis_project/09.code/11.02.WGCNA.SFT.R")
source("/Volumes/FileManage/06.360Drive/02.heterosis_project/09.code/11.03.WGCNA.module.R")
source("/Volumes/FileManage/06.360Drive/02.heterosis_project/09.code/11.04.WGCNA.moduleTrait.R")
source("/Volumes/FileManage/06.360Drive/02.heterosis_project/09.code/11.05.WGCNA.HubGene.R")
# 原始count数据
rawCount <- read.table("/Volumes/FileManage/06.360Drive/02.heterosis_project/08.4133BtGroup/03.WGCNA/02.Rawdata/mRNA_readcount.xls",
                       header = T,
                       sep = "\t")

# head(rawCount)
rownames(rawCount) <- rawCount$transcript_id
rawCount <- rawCount[,-1]
bt.raw <- data.frame(row.names = rownames(rawCount),
                     select(rawCount, contains("Bt_")),
                     select(rawCount, contains("J_")),
                     select(rawCount, contains("SY_")))
bt.raw <- select(bt.raw, -contains("Z12"))

# 按照组织分成3份
leaf <- select(bt.raw, contains("_L"))
ovule <- select(bt.raw, contains("_O"))
fiber <- select(bt.raw, contains("_F"))

##======02.方法======================
# 参数设置
type = "unsigned"
corType = "pearson"
corFnc = ifelse(corType=="pearson", cor, bicor)
maxPOutliers = ifelse(corType=="pearson",1,0.05)
robustY = ifelse(corType=="pearson",T,F)
# 组织
tissue = leaf
# 名字
Title = "leaf"

##======02.1 样品PCA和Heatmap============
WGCNA.PCAandHeatmap(tissue = tissue,
                    Title = Title)

##======02.2 软阈值SFT计算===============
# 表达
datExpr = y
WGCNA.SFT(Title = Title,
          datExpr = datExpr)

##======02.3 Module计算===============
# 无向网络在power小于15或有向网络power小于30内,没有一个power值可以使
# 无标度网络图谱结构R^2达到0.8,平均连接度较高如在100以上,可能是由于
# 部分样品与其他样品差别太大。这可能由批次效应、样品异质性或实验条件对
# 表达影响太大等造成。可以通过绘制样品聚类查看分组信息和有无异常样品。
# 如果这确实是由有意义的生物变化引起的,也可以使用下面的经验power值。
if (is.na(power)){
power = ifelse(nSamples<20, ifelse(type == "unsigned", 9, 18),
               ifelse(nSamples<30, ifelse(type == "unsigned", 8, 16),
                      ifelse(nSamples<40, ifelse(type == "unsigned", 7, 14),
                             ifelse(type == "unsigned", 6, 12))
               )
)
}

WGCNA.oneStepNetWork(Title = Title)

##=======表型鉴定========
rownames(datExpr)
phenotype = data.frame(row.names = rownames(datExpr),
                       yield = rep(c(1,0),times = c(3,12)),
                       quality = rep(c(0,1,0,1),times = c(6,3,3,3)),
                       Yield_Quality = rep(c(0,1,0,1,0), times =c(3,3,3,3,3)))
WGCNA.ModuleTrait(Title = Title,
                  phenotype = phenotype)
##=======选择感兴趣的模块分析=========
## design设计
datTraits <- data.frame(row.names = rownames(phenotype),
                        subtype = factor(rep(c("Y","Y_Q","Y","Y_Q","Q"),each = 3),
                                         levels = c("Y","Q","Y_Q")))
design = model.matrix(~0 + datTraits$subtype)
colnames(design) = levels(datTraits$subtype)

## 参数设置
moduleName = "blue"
phenoName = "Y_Q"
cor = 0.6
con = 0.90
## 
WGCNA.HubGene(Title = Title,
              cor = cor,
              con = con,
              moduleName = moduleName,
              phenoName = phenoName)




Function

这里一个写了5个:

  • PCA和heatmap: WGCNA.PCAandHeatmap
  • SFT : WGCNA.SFT
  • One step network: WGCNA.oneStepNetWork
  • 模块-表型: WGCNA.ModuleTrait
  • hubgene:WGCNA.HubGene

PCA和heatmap

WGCNA.PCAandHeatmap <- function(tissue,Title){
  x <- tissue[apply(tissue,1,function(x) sum(x > 10) > (0.9*ncol(tissue))),]
  # 01.1.将readcount转换为logcpm
  y <- log10(edgeR::cpm(x)+1)
  y[1:4,1:4]
  # 01.2.将样品名称去掉生物学重复标记起到分组作用
  z <- gsub("_.*","",colnames(y))
  test <- as.data.frame(t(y))
  # 01.3.将分组标记放到表达矩阵最后一列
  dat <- cbind(test,z)
  # 02.1.准备PCA数据
  dat.pca <- PCA(dat[,-ncol(dat)], graph = F)
  # 02.2.绘制PCA图并保存
  fviz_pca_ind(dat.pca,
               geom.ind = "point",
               col.ind = dat$z,
               palette = c("#9370DB", "#FF82AB", "#87CEFF", "#2E8B57", "#0000FF"),
               addEllipses = T,
               legend.title = "Groups")
  ggsave(paste(Title,"SamplsPCAplot.pdf",sep = "_"), width = 8, height = 8)
  # 将每行表达量最大的前5000个基因拿出来做热图
  cg = names(tail(sort(apply(y, 1, function(x){sum(x)})),5000))
  # pheatmap(pheatmap(y[cg,],show_rownames = F,show_colnames = F),scale = "row")
  n=t(scale(t(y[cg,]))) # 'scale'可以对log-ratio数值进行归一化
  n[n>3]=3
  n[n< -3]= -3
  n[1:4,1:4]
  ac=data.frame(g=z)
  rownames(ac)=colnames(n) #把ac的行名给到n的列名,即对每一个探针标记上分组信息(是'noTNBC'还是'TNBC')
  pheatmap(n,show_colnames =T,show_rownames = F,
           annotation_names_col = F,
           annotation_col=ac,
           filename = paste(Title,'heatmap_top5000.png',sep = "_"),
           clustering_distance_rows = "euclidean")
  assign("y",value = y, envir = globalenv())
}

SFT

WGCNA.SFT <- function(datExpr, Title){
  datExpr <- datExpr
  type = "unsigned"
  corType = "pearson"
  corFnc = ifelse(corType=="pearson", cor, bicor)
  maxPOutliers = ifelse(corType=="pearson",1,0.05)
  robustY = ifelse(corType=="pearson",T,F)
  m.mad <- apply(datExpr,1,mad)
  datExprVar <- datExpr[which(m.mad > 
                                max(quantile(m.mad, probs=seq(0, 1, 0.8))[2],0.01)),]
  dim(datExprVar)
  datExpr <- as.data.frame(t(datExprVar))
  ## 检测缺失值
  gsg = goodSamplesGenes(datExpr, verbose = 3)
  if (!gsg$allOK){
    # Optionally, print the gene and sample names that were removed:
    if (sum(!gsg$goodGenes)>0) 
      printFlush(paste("Removing genes:", 
                       paste(names(dataExpr)[!gsg$goodGenes], collapse = ",")));
    if (sum(!gsg$goodSamples)>0) 
      printFlush(paste("Removing samples:", 
                       paste(rownames(dataExpr)[!gsg$goodSamples], collapse = ",")));
    # Remove the offending genes and samples from the data:
    dataExpr = dataExpr[gsg$goodSamples, gsg$goodGenes]
  }
  
  nGenes = ncol(datExpr)
  nSamples = nrow(datExpr)
  assign("nGenes",value = nGenes, envir = globalenv())
  assign("nSamples",value = nSamples, envir = globalenv())
  dim(datExpr)
  head(datExpr)[,1:8]
  
  sampleTree = hclust(dist(datExpr), method = "average")

  pdf(file = paste(Title,"Sample_clustering.pdf",sep = "."),width = 8,height = 5)
  plot(sampleTree, main = "Sample clustering to detect outliers", sub="", xlab="")
  dev.off();
  
  powers = c(c(1:10), seq(from = 12, to=30, by=2))
  sft = pickSoftThreshold(datExpr, powerVector=powers, 
                          networkType=type, verbose=5)
  
  pdf(file = paste(Title,"SFTPlot.pdf",sep = "."),width = 10,height = 7)
  par(mfrow = c(1,2))
  cex1 = 0.9
  # 横轴是Soft threshold (power),纵轴是无标度网络的评估参数,数值越高,
  # 网络越符合无标度特征 (non-scale)
  {plot(sft$fitIndices[,1], -sign(sft$fitIndices[,3])*sft$fitIndices[,2],
         xlab="Soft Threshold (power)",
         ylab="Scale Free Topology Model Fit,signed R^2",type="n",
         main = paste("Scale independence"))
    text(sft$fitIndices[,1], -sign(sft$fitIndices[,3])*sft$fitIndices[,2],
         labels=powers,cex=cex1,col="red")
    # 筛选标准。R-square=0.85
    abline(h=0.90,col="red")
    abline(h=0.85,col="green")
    # Soft threshold与平均连通性
    plot(sft$fitIndices[,1], sft$fitIndices[,5],
         xlab="Soft Threshold (power)",ylab="Mean Connectivity", type="n",
         main = paste("Mean connectivity"))
    text(sft$fitIndices[,1], sft$fitIndices[,5], labels=powers, 
         cex=cex1, col="red")
  }
  dev.off();
  power = sft$powerEstimate
  power
  assign("datExpr",value = datExpr, envir = globalenv())
  assign("power",value = power, envir = globalenv())
}

One step network

WGCNA.oneStepNetWork <- function(Title){
  #######============一步法网络构建===========
  # power: 上一步计算的软阈值
  # maxBlockSize: 计算机能处理的最大模块的基因数量 (默认5000);
  #  4G内存电脑可处理8000-10000个,16G内存电脑可以处理2万个,32G内存电脑可
  #  以处理3万个
  #  计算资源允许的情况下最好放在一个block里面。
  # corType: pearson or bicor
  # numericLabels: 返回数字而不是颜色作为模块的名字,后面可以再转换为颜色
  # saveTOMs:最耗费时间的计算,存储起来,供后续使用
  # mergeCutHeight: 合并模块的阈值,越大模块越少
  net = blockwiseModules(datExpr, power = power, maxBlockSize = nGenes,
                         TOMType = type, minModuleSize = 30,
                         reassignThreshold = 0, mergeCutHeight = 0.25,
                         numericLabels = TRUE, pamRespectsDendro = FALSE,
                         saveTOMs=TRUE, corType = corType, 
                         maxPOutliers=maxPOutliers, loadTOMs=TRUE,
                         #saveTOMFileBase = paste(Title,".tom",sep = ""),
                         verbose = 3)
  table(net$colors)
  assign("net",value = net, envir = globalenv())
  ####============modular construction=================
  ## 灰色的为**未分类**到模块的基因。
  # Convert labels to colors for plotting
  moduleLabels = net$colors
  assign("moduleLabels",value = moduleLabels, envir = globalenv())
  moduleColors = labels2colors(moduleLabels)
  assign("moduleColors",value = moduleColors, envir = globalenv())
  # Plot the dendrogram and the module colors underneath
  # 如果对结果不满意,还可以recutBlockwiseTrees,节省计算时间
  pdf(file = paste(Title,"Module.pdf",sep = "."),width = 6,height = 6)
  plotDendroAndColors(net$dendrograms[[1]], moduleColors[net$blockGenes[[1]]],
                      "Module colors",
                      dendroLabels = FALSE, hang = 0.03,
                      addGuide = TRUE, guideHang = 0.05)
  dev.off()
  # module eigengene, 可以绘制线图,作为每个模块的基因表达趋势的展示
  MEs = net$MEs
  ### 不需要重新计算,改下列名字就好
  ### 官方教程是重新计算的,起始可以不用这么麻烦
  MEs_col = MEs
  colnames(MEs_col) = paste0("ME", labels2colors(
    as.numeric(str_replace_all(colnames(MEs),"ME",""))))
  MEs_col = orderMEs(MEs_col)
  assign("MEs",value = MEs, envir = globalenv())
  assign("MEs_col",value = MEs_col, envir = globalenv())
  pdf(file = paste(Title,"Eigeng_adja_heatmap.pdf",sep = "."),width = 7,height = 10)
  # 根据基因间表达量进行聚类所得到的各模块间的相关性图
  # marDendro/marHeatmap 设置下、左、上、右的边距
  plotEigengeneNetworks(MEs_col, "Eigengene adjacency heatmap", 
                        marDendro = c(3,3,2,4),
                        marHeatmap = c(3,4,2,2), plotDendrograms = T, 
                        xLabelsAngle = 90)
  dev.off()
  Gene2module <- data.frame(GID = colnames(datExpr),
                            Module = moduleColors)
  write.table(Gene2module,file = paste(Title,"Gene2module.xls",sep = "_"),
              row.names = F,
              quote = F,
              sep = "\t")
}

模块-表型

WGCNA.ModuleTrait <- function(Title,phenotype){
  traitData <- phenotype
  dim(traitData)
  ### 模块与表型数据关联
  if (corType=="pearson") {
    modTraitCor = cor(MEs_col, traitData, use = "p")
    modTraitP = corPvalueStudent(modTraitCor, nSamples)
  } else {
    modTraitCorP = bicorAndPvalue(MEs_col, traitData, robustY=robustY)
    modTraitCor = modTraitCorP$bicor
    modTraitP   = modTraitCorP$p
  }
  
  ## Warning in bicor(x, y, use = use, ...): bicor: zero MAD in variable 'y'.
  ## Pearson correlation was used for individual columns with zero (or missing)
  ## MAD.
  
  # signif表示保留几位小数
  textMatrix = paste(signif(modTraitCor, 2), "\n(", signif(modTraitP, 1), ")", sep = "")
  dim(textMatrix) = dim(modTraitCor)
  pdf(file = paste(Title,"Module_trait.pdf",sep = "."),width = 8,height = 10)
  labeledHeatmap(Matrix = modTraitCor, xLabels = colnames(traitData), 
                 yLabels = colnames(MEs_col), 
                 cex.lab = 0.7, xLabelsAngle = 0, xLabelsAdj = 0.5,
                 ySymbols = substr(colnames(MEs_col),3,1000), colorLabels = FALSE, 
                 colors = blueWhiteRed(50), 
                 textMatrix = textMatrix, setStdMargins = FALSE, 
                 cex.text = 0.6, zlim = c(-1,1),
                 main = paste("Module-trait relationships"))
  dev.off()
}

Hubgene 提取

WGCNA.HubGene <- function(Title,cor,con,moduleName,phenoName){
  ## 联通性计算
  # (1) Intramodular connectivity
  connet=abs(cor(datExpr,use="p"))^6
  Alldegrees1=intramodularConnectivity(connet, moduleColors)
  ###(3) Generalizing intramodular connectivity for all genes on the array
  datKME=signedKME(datExpr, MEs_col, outputColumnName="MM.")
  write.table(datKME, paste(Title,"Conectivity_of_each_modular.xls",sep = "."),
              sep = "\t",
              row.names = T,
              quote = F)
  # Display the first few rows of the data frame
  ##Finding genes with high gene significance and high intramodular connectivity in interesting modules
  PheName <- as.data.frame(design[,grep(phenoName,colnames(design))])
  names(PheName) = phenoName
  GS1 = as.numeric(cor(PheName,datExpr, use = "p"))
  # abs(GS1)模块和基因的关联性
  # abs(datKME$MM.green) 基因的连通性
  num <- grep(moduleName,colnames(datKME))
  FilterGenes= abs(GS1)> cor & abs(datKME[,num])>con
  
  hubGenes_raw = data.frame(ID = rownames(datKME),
                            TORF = FilterGenes)
  hubGenes = filter(hubGenes_raw, TORF == "TRUE")
  table(hubGenes)
  
  write.table(hubGenes,file = paste(moduleName,phenoName,"hubGene.xls",
                                    sep = "_"),
              sep = "\t",
              row.names = F,
              quote = F)
}

结果

  • 做完后在working dir下面应该会有以下几个文件,关于以下图片及文件的意义,这个看我参考的那几篇文章理解就好;


    jianshu.jpg
  • 还会发现我基本没有做基因和模块的相关,这部分如果有需求,也可以看参考的文章实现。

一些问题总结

  • 正确的输入数据格式
    基本教程,FAQ中提到的我都尝试了一下,不外乎以下几种:

    1. FPKM或者RPKM。
    2. log(fpkm+1)
    3. z-scored fpkm
    4. log(tpm +1)
      使用不同类型的输入数据会发现分出的module数量也不一样,甚至sft都不尽相同,但总体来看,差异不大,用MAD筛选过后留下的基因中,除了直接用fpkm之外,其他几种方法差异不是很大,我最终选择了用log(tpm+1), 而且在转换前先用90%样本中readcount > 10这个阈值去一下背景。个人习惯罢了。
  • 无法找到合适的Power值:
    这些在「WGCNA-FAQ」中作者已经解释,这里着重说下我遇到的问题。
    首先,有一次我尝试用大量不同品种的不同组织的转录组(17个品种,3个组织,3个重复共153个sample)做WGCNA,发现一个特别有趣的结果:在sft阶段发现R2从1开始为明显的负数值,随着power的增大R2逐渐变为正数。刚开始百思不得其解,后来找到了答案:

Data heterogeneity may affect any statistical analysis, and even more so an unsupervised one such as WGCNA. What, if any, modifications should be made to the analysis depends crucially on whether the heterogeneity (or its underlying driver) is considered "interesting" for the question the analyst is trying to answer, or not. If one is lucky, the main driver of sample differences is the treatment/condition one studies, in which case WGCNA can be applied to the data as is. Unfortunately, often the heterogeneity drivers are uninteresting and should be adjusted for. Such factors can be technical (batch effects, technical variables such as post-mortem interval etc.) or biological (e.g., sex, tissue, or species differences).

原因就是样品异质性(heterogeneity)! 由于样本中总体分成了3个组织,由于基因表达的时空特异性,组织间的差异特别大,所以会导致组织内连通性超级高,而组织间连通性特别低。具体解释见:Question about WGCNA soft thresholding value

The negative "signed R^2" is negative when your network has more genes with high connectivity than ones with low connectivity (i.e., the regression line for the fit log(n(k))~log(k) has a positive slope). It means your network shows a topology in some ways opposite (more high connectivity than low connectivity genes) to what is normally expected (a lot of low-connetivity gens and fewer high connectivity genes).Usually, a lot of high-connectivity genes means there is a strong global driver (e.g., you have samples from different tissues or a strong batch effect). Make sure your sample tree doesn't show very strong branches. Also, see WGCNA FAQ (https://labs.genetics.ucla.edu/horvath/CoexpressionNetwork/Rpackages/WGCNA/faq.html) for some comments about heterogeneous data sets and lack of SFT.

这会导致一个结果,就是无法构成一个无尺度网络;
这也不代表WGCNA往下走没有意义了! 不要轻易的怀疑自己的结果,不是为了做WGCNA而做WGCNA,这样就失去了分析的意义,如果有一定的生物学问题,那么他就是有意义的!
上面说了虽然无法构成一个无尺度的网络,我们后期可能无法取到合适的Hubgene,但是这样想,如果我想从这个结果找到与组织特异性强的模块,这个后续分析就是有意义的,我们用经验的power值继续构建module,然后用tissue对sample进行分类,最后我们可以得到一个module-tissue的相关性结果,后续通过对相应module的基因进行富集分析,证实了我的想法,确实叶片的结果大多富集到生物节律,光合作用,叶绿体,细胞循环等,胚珠中富集到花发育,细胞壁,细胞膜,激素等通路...grey模块中富集的较少,但不难看出有许多看家基因。虽然说生物学意义不是很大,但是说明了这条路应该没有错,下一步可以通过对ncRNA的WGCNA找到组织特异性表达的lncRNA,circRNA等等。这些module就会很有意义。

应该是未完待续~~

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

推荐阅读更多精彩内容