第2章 R编程入门(一):数据集

2.1 R语言

R是一种解释性语言,输入后可直接给出结果。
R功能烤函数实现,函数形式如下:函数(输入数据, 参数=)
如果没有指定,则参数以默认值为准。
每一个函数执行特定的功能,后面紧跟括号,如:平均值mean()、求和sum()、绘图plot()、排序sort()
R的函数又分为“高级”和“低级”函数,高级函数可调用低级函数。
“高级“函数习惯上称为泛型函数。

2.1.1 数据集的概念

在R中,穿件数据集包括以下两步:

  • 选择一种数据结构来存储数据
  • 将数据输入或导入这个数据结构中

数据集通常是由数据构成的一个矩形数组:

  • :观测(observation)、记录(record)、示例(example)
  • :变量(variable)、字段(field)、属性(attribute)

R可以处理的数据类型

  • 数值型(numeric)
> a <- -1
> is.numeric(a)  # 判断a是否为数值型
[1] TRUE
  • 字符型(character)
> b <- "peter"
> nchar(b)  # 输出字符串的长度
[1] 5
  • 日期型(date)
    • 常用的日期型数据类型是Date(仅存储日期)和POSIXct(同时存储日期和时间)
    • 处理日期型数据时,往往需要as.Date()函数或as.POSIXct()函数将读入的数值型转换成日期型
    • 使用as.numeric()函数或as.integer()函数将日期型数据转换成数值型
    • strftime(x, format = "")函数可以定义日期型数据的格式
> t <- as.Date("2020-10-09")
> class(t)   # 输出t的数据类型
[1] "Date"
> ct <- as.POSIXct("2020-10-10 18:32:45")
> class(ct)    # 输出ct的数据类型
[1] "POSIXct" "POSIXt"

> c_year <- as.integer(strftime(t,"%Y"))   # 输出年份
> c_month <- as.integer(strftime(t,"%m"))    # 输出月份
> c_week <- as.integer(strftime(t,"%W"))     # 输出周数
> c_year;c_month;c_week
[1] 2020
[1] 10
[1] 40
  • 逻辑型(logical)TRUEFALSE
  • 复数型
  • 因子型(factor):表示不同的类别。因子是名义型变量或有序型变量

R中用于存储数据的结构

  • 标量:只含有一个元素的向量,如f<-3g<-"US"h<-TRUE,用于保存常量
  • 向量:用于存储数值型字符型逻辑型数据的一维数组
  • 数组
  • 数据框:R中用于存储数据集的一种主要数据结构:列表示变量,行表示观测。
  • 列表

(1)向量(vector)

  • 一系列元素的组合,用于存储数值型字符型逻辑型数据的一维数组
  • 执行组合功能的函数c()可以用来创建向量。
# 数值型向量
a <- c(1, 2, 3, 4, -9, -20)

# 字符型向量
b <- c("a", "b", "c", "b", "a")
c <- c("one", "china", "USA")

# 逻辑型向量
d <- c(TRUE, FALSE, FALSE, TRUE, TRUE)
  • 单个向量中的数据必须拥有相同的类型或模式(数值型、字符型或逻辑型),同一向量中无法混杂不同模式的数据。
  • 可以通过方括号[]中给定元素的索引(index)来访问向量中的元素。
    • 整数型索引:索引是元素的位置;
    • 字符型索引:索引是名称属性;
    • 逻辑型索引:索引的是相同长度的逻辑向量对应的逻辑值为真的元素
  • 可以通过冒号:生成一个数值序列。
> d <- c(1, 3, 5, 6, 7, 10, 21, 17)
> d[2]       # 取第2个元素
[1] 3
> d[-2]      # 删除第2个元素
[1]  1  5  6  7 10 21 17
> d[c(1,4)]   # 取第1个和第4个元素
[1] 1 6
> d[2:5]     # d[2:5]等价于d[c(2:5)],取第2-5号元素
[1] 3, 5, 6. 7
> d[d>2]     # 取大于2的元素
[1]  3  5  6  7 10 21 17
> d[d==1]     # 取等于1的元素
[1] 1
> d[d<=5]      # 取小于等于5的元素
[1] 1 3 5

(2)矩阵(matrix)

  • 二维数组
  • 每个元素都拥有相同的模式(数值型、字符型或逻辑型)
  • 可以通过matrix()函数创建矩阵
    matrix <- matrix ( vector, nrow=, ncol=, byrow=, dimnames=list() )

vector包含了矩阵的元素;
nrowncol用以指定行和列的维数
dimnames包含了可选的以字符型向量表示的行名和列名
byrow用以指明矩阵应当按行填充(byrow = TRUE)还是按列填充(byrow = FALSE)默认情况下按列填充

  • 可以使用下标和方括号来选择矩阵中的行,列或者元素。

X[i,]指矩阵X中的第i
X[,j]指矩阵X中的第j
X[i, j]指第i行第j个元素
选择多行或者多列时,下标ij可以是数值型向量。

# 创建一个4行5列的矩阵
> x = matrix(21:40,nrow = 4, ncol = 5, byrow = FALSE)
> x
     [,1] [,2] [,3] [,4] [,5]
[1,]   21   25   29   33   37
[2,]   22   26   30   34   38
[3,]   23   27   31   35   39
[4,]   24   28   32   36   40

# 创建一个2行3列,含有行列标签,按行填充的矩阵
> fourcell = c(1,2,3,10,20,30)
> rnames = c("R1","R2")
> cnames = c("C1","C2", "C3")
> matrix = matrix(fourcell, nrow = 2, ncol = 3, byrow = TRUE, dimnames = list(rnames, cnames))
> matrix
   C1 C2 C3
R1  1  2  3
R2 10 20 30
# 创建一个2行3列,含有行列标签,按列填充的矩阵
> matrix = matrix(fourcell, nrow = 2, ncol = 3, byrow = FALSE, dimnames = list(rnames, cnames))
> matrix
   C1 C2 C3
R1  1  3 20
R2  2 10 30

# 创建一个内容为数字1~20的5行的矩阵,默认情况下案列填充
> aa = matrix(1:20, nrow=5)
> aa
     [,1] [,2] [,3] [,4]
[1,]    1    6   11   16
[2,]    2    7   12   17
[3,]    3    8   13   18
[4,]    4    9   14   19
[5,]    5   10   15   20
# 选择第3行的元素
> aa[3,]
[1]  3  8 13 18
# 选择第3列的元素
> aa[,3]
[1] 11 12 13 14 15
# 选择第3行第3列的原色
> aa[3,3]
[1] 13
# 选择第3行第3、第4列的元素
> aa[3,c(3,4)]
[1] 13 18
# 原则第3列和第4列的元素
> aa[,c(3,4)]
     [,1] [,2]
[1,]   11   16
[2,]   12   17
[3,]   13   18
[4,]   14   19
[5,]   15   20

(3)数组(array)

  • 与矩阵类似,但是维度可以大于2
  • 可以通过array()函数创建
    array <- array ( vector, dimensions, dimnames )

vector, 包含了数组中的数据;
dimensions, 数值型向量,给出各维度下标的最大值
dimnames, 可选的,各维度名称标签的列表

  • 数组中的数据也只能拥有一种模式
  • 从数组中取元素的方式与矩阵相同
> dim1 <- c("X1", "X2")
> dim2 <- c("Y1", "Y2", "Y3")
> dim3 <- c("Z1", "Z2", "Z3", "Z4")
> xyz <- array(1:24, c(2, 3, 4), dimnames=list(dim1, dim2, dim3))
> xyz
, , Z1

   Y1 Y2 Y3
X1  1  3  5
X2  2  4  6

, , Z2

   Y1 Y2 Y3
X1  7  9 11
X2  8 10 12

, , Z3

   Y1 Y2 Y3
X1 13 15 17
X2 14 16 18

, , Z4

   Y1 Y2 Y3
X1 19 21 23
X2 20 22 24

> xyz[1,2,]
Z1 Z2 Z3 Z4 
 3  9 15 21 
> xyz[1,3,]
Z1 Z2 Z3 Z4 
 5 11 17 23 
> xyz[,2,2]
X1 X2 
 9 10 
> xyz[2,,4]
Y1 Y2 Y3 
20 22 24 
> xyz[1,2,3]
[1] 15

(4)数据框(data.frame)

  • 与通常在SAS、SPSS和STATA中看到的数据集类似,不同的列可以包含不同模式的数据(数值型、字符型等)
  • 数据框可以通过data.frame()创建
    mydata = data.frame(col1, col2, col3, ···)

列向量col1col2col3……可为任何类型(如字符型、数值型或者逻辑型)。
每一列数据的模式必须唯一,但是不同列的数据模式可以不同。
每一列的名称可由函数names(df) <- c(col1_name, col2_name, col3_name, ...)设定。
每一行的实例标识符(case identifier)可以通过参数row.names =指定,或者通过函数rownames(df) <- c()设定。

> IDnumber <- c(101, 102, 103, 104)
> age <- c(24, 78, 56, 45)
> hypertention <- c("yes","no", "no", "yes")
> severity <- c("high", "middle", "low", "middle")
> patientdata <- data.frame(IDnumber, age, hypertention, severity)
> patientdata
  IDnumber age hypertention severity
1      101  24          yes     high
2      102  78           no   middle
3      103  56           no      low
4      104  45          yes   middle

> patientdata <- data.frame(IDnumber, age, hypertention, severity, row.names = IDnumber)
> patientdata
    IDnumber age hypertention severity
101      101  24          yes     high
102      102  78           no   middle
103      103  56           no      low
104      104  45          yes   middle
> rownames(patientdata) <- c("patient1", "patient2", "patient3", "patient4")
> patientdata
         IDnumber age hypertention severity
patient1      101  24          yes     high
patient2      102  78           no   middle
patient3      103  56           no      low
patient4      104  45          yes   middle
> names(patientdata) <- c("ID", "Nianling", "yes or no", "Yanzhongxing")
> patientdata
          ID Nianling yes or no Yanzhongxing
patient1 101       24       yes         high
patient2 102       78        no       middle
patient3 103       56        no          low
patient4 104       45       yes       middle
  • 数据框中元素的获取方式,可以使用下标记号,也可以直接指定列名
  • 也可以使用双方括号[[列序号]][[列名称]]
  • $可以选取一个给定数据框中的某个特定变量,数据框名称$变量名称(列名称)
# 读取第1列元素
> patientdata[1]    
  IDnumber
1      101
2      102
3      103

# 读取第1列的元素
> patientdata[,1]     # 等价于patientdata[[1]]
[1] 101 102 103 104
> patientdata[[1]]
[1] 101 102 103 104
> patientdata[["IDnumber"]]
[1] 101 102 103 104

# 读取第2行元素
> patientdata[2,]
  IDnumber age hypertention severity
2      102  78           no   middle

# 读取第2到第4行的元素
> patientdata[2:4,]
  IDnumber age hypertention severity
2      102  78           no   middle
3      103  56           no      low
4      104  45          yes   middle

# 读取第1到3列的元素
> patientdata[1:3]    # 等价于 patientdata[,1:3]
  IDnumber age hypertention
1      101  24          yes
2      102  78           no
3      103  56           no
4      104  45          yes

# 第2行第4列的元素
> patientdata[2,4]
[1] "middle"

# 读取“age”列的元素
> patientdata["age"]
  age
1  24
2  78
3  56
4  45
> patientdata[["age"]]
[1] 24 78 56 45

# 读取“age”列和“severity”列的元素
> patientdata[c("age","severity")]
  age severity
1  24     high
2  78   middle
3  56      low
4  45   middle

# 获取age变量的数据
> patientdata$age
[1] 24 78 56 45
  • 获取数据框的行数、列数和维数:nrow()ncol()dim()
  • 获取数据框的列名或行名:names()colnames()rownames()。也可以重新定义列名names(df) <- c(col1_name, col2_name, col3_name, ...)
  • 观察数据框的内容:View(df)head(df, n=3)tail(df, n=1)
> nrow(patientdata)      # 行数
[1] 4
> ncol(patientdata)       # 列数
[1] 4
> dim(patientdata)        # 维数
[1] 4 4

> names(patientdata)       # 列名
[1] "IDnumber"     "age"          "hypertention" "severity"    
> rownames(patientdata)    # 行名
[1] "1" "2" "3" "4"
> colnames(patientdata)     # 列名
[1] "IDnumber"     "age"          "hypertention" "severity"  

> head(patientdata,n=3)
  IDnumber age hypertention severity
1      101  24          yes     high
2      102  78           no   middle
3      103  56           no      low
> tail(patientdata)
  IDnumber age hypertention severity
1      101  24          yes     high
2      102  78           no   middle
3      103  56           no      low
4      104  45          yes   middle
> tail(patientdata,n=1)
  IDnumber age hypertention severity
4      104  45          yes   middle

attach()detach()函数

  • attach()可以将数据框添加到R的搜索路径中。R在遇到一个变量名以后,将检查搜索路径中的数据框,以定位到这个变量。
  • detach()将数据框从搜索路径中移除,detach()并不会对数据框本身做任何处理。
  • 最好在分析一个单独的数据框,并且不太可能有多个同名对象时使用。
# 创建一个数据框
> systolic <- c(120,130,140,150,160)
> age <- c(20,30, 40, 50, 55)
> hypertension <- data.frame(systolic, age)
> hypertension
  systolic age
1      120  20
2      130  30
3      140  40
4      150  50
5      160  55

> summary(hypertension$systolic)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    120     130     140     140     150     160 
> summary(hypertension$age)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
     20      30      40      39      50      55 
> plot(hypertension$systolic, hypertension$age)

上述代码也可以写成:

> attach(hypertension)
> summary(age)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
     20      30      40      39      50      55 
> summary(systolic)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    120     130     140     140     150     160 
> plot(systolic,age)
> detach(hypertension)

with()函数

  • with ( 数据框名称, {···} )
  • 大括号{}之间的语句都是针对括号{}前的数据框名称执行的
  • 如果只有一条语句,{}可以省略
    上面的代码可以重写为:
> with(hypertension,{
+     summary(age)
+     summary(systolic)
+     plot(systolic,age)
+ })
  • 局限性:赋值仅在with()函数的括号内生效。
  • 如果要创建在with()结构以外的对象,需使用特殊赋值符号<<-代替标准赋值符号<-<<-可以将对象保存到with()之外的全局环境中。
    上面的代码可以重写为:
> with(hypertension, {stat <- summary(age)
+ stat})
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
     20      30      40      39      50      55

> with(hypertension, {nonstat <- summary(systolic)
+ stat <<- summary(systolic)})
> nonstat
错误: 找不到对象'nonstat'
> stat
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    120     130     140     140     150     160 

(5)因子(factor)

变量可分为:

  • 名义型变量:没有顺序之分的类别变量;
  • 有序型变量:表示一种顺序关系,而非数量关系;
  • 连续型变量:可以呈现为某个范围内的任意值并同时表示了顺序和数量。

类别(名义型)变量有序类别(有序型)变量在R中称为因子(factor)。
在R中,因子决定了数据的分析方式以及如何进行结果展示。
因子的设计思想来源于统计学中的名义变量分类变量,这些变量本质上不是数字,而是对应分类。
在R中,因子可以简单看做一个附加更多信息的向量(尽管他们内部机理是不同的)。这些额外的信息包括向量中不同值的记录,称为“水平”(level)。

  • 函数factor()一个整数向量的形式存储类别值,将一个由字符串(原始值)组成的内部向量映射到这些整数上。
  • 数值型的因子转换成数值型向量,需要使用as.numeric(as.character())组合函数。其中as.character()将向量转换成字符型,as.numeric()函数将向量转换成数值型。
> hypertention <- c("yes", "no", "no", "yes")
> hypertention <- factor(hypertention)
> hypertention
[1] yes no  no  yes
Levels: no yes
> str(hypertention)
 Factor w/ 2 levels "no","yes": 2 1 1 2

因子的长度定义为数据的长度,而不是水平的个数。

> x <- c(5, 12, 13,12)
> x <- factor(x)
> x
[1] 5  12 13 12
Levels: 5 12 13
# x中的不同数值(5,12,13)就是水平
# x的核心是(1,2,3,2),意味着数据是由水平1、水平2和水平3的值构成的,原始数据已经重新编码为水平
> str(x)
 Factor w/ 3 levels "5","12","13": 1 2 3 2
> length(x)
[1] 4

> class(x)
[1] "factor"
> x <- as.numeric(x)     # 实际效果是将因子水平转换成了数值向量
> x
[1] 1 2 3 2
> x <- as.numeric(as.character(x))     # 将数值型的因子转换成数值型向量
> x
[1]  5 12 13 12
> class(x)
[1] "numeric"

要表示有序变量,需要为函数factor()指定参数ordered = TRUE

> severity <- c("high", "middle", "low", "middle")
> severity <- factor(severity, ordered = TRUE)
# 以上语句将向量编码为(1,3,2,3)
# 并在内部将这些值关联为1=high,2=low以及3=middle
> severity
[1] high   middle low    middle
Levels: high < low < middle
> str(severity)
 Ord.factor w/ 3 levels "high"<"low"<"middle": 1 3 2 3

对于字符型向量,因子的水平默认依字母顺序创建。可以通过指定levels选项来覆盖默认排序。

需要确保指定的水平与数据中的真实值相匹配,否则,任何在数据中出现而未在参数中列举的数据都将被设为缺失值

> severity <- factor(severity, ordered = TRUE, levels = c("low", "middle", "high"))
> severity
[1] high   middle low    middle
Levels: low < middle < high
> str(severity)
 Ord.factor w/ 3 levels "low"<"middle"<..: 3 2 1 2

数值型变量可以用levelslabels参数编码成因子。

注意标签的顺序必须和水平的顺讯向一致。

sex <- factor(sex, levels = c(1, 2), labels = c("Male", "Female"))

此例中,性别变量被转换成一个无需因子,男性被编码成1,女性被编码成2,标签“Male”和“Famle”将替代1和2在结果中输出。

示例:

> IDnumber <- c(101, 102, 103, 104)
> age <- c(24, 78, 56, 45)
> hypertention <- c("yes", "no", "no", "yes")
> severity <- c("high", "middle", "low", "middle")
> hypertention <- factor(hypertention)
> severity <- factor(severity, ordered = TRUE)
> patientdata <- data.frame(IDnumber, age, hypertention, severity)
> str(patientdata)
'data.frame':   4 obs. of  4 variables:
 $ IDnumber    : num  101 102 103 104
 $ age         : num  24 78 56 45
 $ hypertention: Factor w/ 2 levels "no","yes": 2 1 1 2
 $ severity    : Ord.factor w/ 3 levels "high"<"low"<"middle": 1 3 2 3
> summary(patientdata)
    IDnumber          age        hypertention   severity
 Min.   :101.0   Min.   :24.00   no :2        high  :1  
 1st Qu.:101.8   1st Qu.:39.75   yes:2        low   :1  
 Median :102.5   Median :50.50                middle:2  
 Mean   :102.5   Mean   :50.75                          
 3rd Qu.:103.2   3rd Qu.:61.50                          
 Max.   :104.0   Max.   :78.00 

(6)列表

  • 列表就是一些对象(或成分)的有序集合,是R数据类型中最为复杂的一种。
  • 列表允许整合若干(可能无关的)对象到单个对象名下。
  • 可以使用函数list()创建列表。
    Mylist <- list ( obj1, obj2, ··· )
    也可以为列表中的对象命名:
    Mylist <- list ( name1=obj1, name2=obj2, ··· )
  • 可以通过在双重方括号中指明代表某个成分的数字或名称来访问列表中的元素。
  • 许多R函数的运行结果都以列表的形式返回
> a <- "list example"
> x <- c(1, 2, 3, 4,5)
> matrix <- matrix(1:20, nrow=5, byrow=FALSE)
> k <- c("one", "two", "four")
> mylist <- list(a,x,matrix, k)
> mylist
[[1]]
[1] "list example"

[[2]]
[1] 1 2 3 4 5

[[3]]
     [,1] [,2] [,3] [,4]
[1,]    1    6   11   16
[2,]    2    7   12   17
[3,]    3    8   13   18
[4,]    4    9   14   19
[5,]    5   10   15   20

[[4]]
[1] "one"  "two"  "four"

> mylist[[3]]
     [,1] [,2] [,3] [,4]
[1,]    1    6   11   16
[2,]    2    7   12   17
[3,]    3    8   13   18
[4,]    4    9   14   19
[5,]    5   10   15   20

注意事项

  • 对象名称中的句点(.)没有特殊意义。

  • 但是美元符号($)有着和其它语言中的句点类似的功能,即指定一个数据框或者列表中的某些数据

  • R不提供多行注释或块注释,必须以#作为多行注释中每一行的开始。

  • 调试时,可以把想忽略的代码放到语句if (FALSE) { ··· }中,将FALSE改为TRUE即允许这块代码执行。

  • 将一个值赋值给某个向量、矩阵、数组或列表中一个不存在的元素时,R自动扩展这个数据结构以容纳新值。

c <- c(1, 15, 10, 8, 20)
c <- c(1,15,10)
c
[1] 1 15 10
c[5] <- 21
c
[1] 1 15 10 NA 21
c[8] <- 31
c
[1] 1 15 10 NA 21 NA NA 31
c <- c[1:3]
c
[1] 1 15 10

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