python数据规整:连接、联合与重塑

分层索引

在轴向上拥有一个或多个索引层级,创建一个分层索引数据:

data = pd.Series(np.random.randn(9),index = [['a','a','a','b','b','c','c','d','d'],
                                             [1,2,3,1,3,1,2,2,3]])
data
a  1   -0.843020
   2    1.161747
   3   -1.530431
b  1   -0.219945
   3   -0.298198
c  1   -0.431312
   2    1.041442
d  2   -0.186717
   3    0.090959
dtype: float64
data.index
MultiIndex(levels=[['a', 'b', 'c', 'd'], [1, 2, 3]],
           codes=[[0, 0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 2, 0, 2, 0, 1, 1, 2]])

这个结果有点意外吧,注意这里是标签索引loc[],返回的是全部第一层级,下第二层级中索引为‘2’的部分

data.loc[:,2]
a    1.161747
c    1.041442
d   -0.186717
dtype: float64

使用unstack方法将数据在DataFrame中重新排列(一般是把最里面的层级作为列标签拿出来)

data.unstack()

    1           2         3
a   -0.843020   1.161747    -1.530431
b   -0.219945   NaN         -0.298198
c   -0.431312   1.041442    NaN
d   NaN         -0.186717   0.090959

unstack的反操作是 stack:

a  1   -0.843020
   2    1.161747
   3   -1.530431
b  1   -0.219945
   3   -0.298198
c  1   -0.431312
   2    1.041442
d  2   -0.186717
   3    0.090959
dtype: float64

Series中只有index轴有分层索引,在 DataFrame中,每个轴都可以有分层索引。索引方法同Series,分层的层级可以有名称,这些名称会在输出中显示。

frame = pd.DataFrame(np.arange(12).reshape((4,3)),
                    index = [['a','a','b','b'],[1,2,1,2]],
                    columns = [['Ohio','Ohio','Colorado'],['Green','Red','Green']])
frame
        Ohio           Colorado
        Green   Red     Green
a   1   0       1       2
    2   3       4       5
b   1   6       7       8
    2   9       10      11
frame.index.names = ['key1','key2']
frame.columns.names = ['states','color']
frame
    states  Ohio            Colorado
    color   Green   Red     Green
key1 key2           
a    1      0       1       2
     2      3       4       5
b    1      6       7       8
     2      9       10      11

一个MultiIndex对象可以使用其自身的构造函数创建并复用,前面带有层级名称的DataFrame的 列 还可以这样创建:

columns=pd.MultiIndex.from_arrays([['Ohio','Ohio','Colorado'],['Green','Red','Green']],names = ['states','color'])
#两种方式等价,上面的复用性强,下面的简单明了
columns = [['Ohio','Ohio','Colorado'],['Green','Red','Green']]

重排序和层级排序

swaplevel接受两个层级序号或层级名称,返回一个进行了层及变更的新对象(但是数据不变)

frame.swaplevel('key1','key2')#表示key1与key2两个索引层级交换位置,数据不变
#里面‘key1’和‘key2参数互换位置输出是一样的,或者传递:
frame.swaplevel(0,1)
#sort_index只能在单一层级上对数据进行排序
frame.sort_index(level = 1)#对key2列排序
frame.sort_index(level = 0)#对key1排序
frame.swaplevel(0,1).sort_index(level = 0)#key1与key2互换后,传递level= 0便是与key2排序了

通过level选项你可以指定你想要在某个特定的轴上进行聚合。

frame.sum(level = 'key2')#默认对行axis=0
frame.sum(level = 'color',axis = 1)#必须指定轴向axis = 1

使用DataFrame的 列 进行索引
DataFrame的set_index函数会生成一个新的DataFrame,新的DataFrame使用一个或多个列作为索引

frame.set_index(['c','d'])
frame.set_index(['c','d'],drop = False)#默认情况下,变为索引的列会被删除,传递drop参数保留这些列
#reset_index是set_index 的反操作,分层索引的索引层级会被移动到列中
frame2.reset_index()

联合与合并数据集

pd.merge根据一个或多个件进行连接
pd.concat使对象在轴向上进行黏合或堆叠
combine_first实例方法允许将重叠数据拼接在一起,以使一个对象中的值填充另一个对象中的值。

df1 = pd.DataFrame({'key':['b','b','a','c','a','a','b'], 'data1':range(7)})
df2 = pd.DataFrame({'key':['a','b','d'],"data2":range(3)})
#调用merge处理我们获得的对象呢
pd.merge(df1,df2)#没有指定在那一列上连接时,自动将重叠列作为连接的键(内连接,取交集),这里重叠列为 ‘key’列
#等价于df1.merge(df2)
pd.merge(df1,df2,on = 'key',how = 'outer')#类似sql中的外连接,取并集

how参数选项


如果每个DataFrame对象的列名是不一样的:

pd.merge(df3,df4,left_on='lkey',right_on='rkey')#分别指定左键和右键,结果取交集
pd.merge(df1,df2,on = 'key',how = 'left')#对所有左表的键进行联合
pd.merge(df1,df2,how = 'inner')#只对两张表都有的键进行联合

当有多个重复列时,on参数仅仅指定其中一个重复列,其他列会自动添加后缀用以区分,当然你也可以指定后缀名suffixes = ('_left','_right')

left = DataFrame({'key1':['foo','foo','bar'],'key2':['one','two','one'],'lval':[1,2,3]})
right = pd.DataFrame({'key1':['foo','foo','bar','bar'],'key2':['one','one','one','two'],'rval':[4,5,6,7]})
pd.merge(left,right,on ='key1')#可以看到重复列有key1和key2,但只指定了key1
pd.merge(left,right,on = 'key2',suffixes = ('_left','_right'))

根据索引合并,你可以传递left_index = True 或 right_index = True来表示索引需要用来作为合并的键:

left1 = pd.DataFrame({'key':['a','b','a','a','b','c'],'value':range(6)})
right1 = pd.DataFrame({'group_val':[3.5,7]},index = ['a','b'])
pd.merge(left1,right1,left_on='key',right_index=True)
#默认的合并方法是取交集(内连接),你也可以使用外连接
pd.merge(left1,right1,left_on = 'key',right_index = True,how = 'outer')

多层索引数据的情况下,事情会更复杂,在索引上连接是一个隐式。
pd.merge(lefth,right,left_on = ['key1','key2'],right_index=True,how = 'outer')
使用两边的索引合并也是可以的
pd.merge(lefth2,right2,left_index=True,right_index=True,how = 'outer')
pd.merge(lefth2,right2,left_index=True,right_index=True)
merge函数的参数

DataFrame 有一个join示例方法,用于按照 索引 合并。该方法也可以用于 合并多个索引相同或相似但没有重叠列 的DataFrame对象。

lefth2.join(right2,how = 'outer')

DataFrame上的join方法进行连接键 上的左连接,完全保留左边DataFrame的 行索引,在调用该DataFrame的某一列(on参数指定的列)上,连接传递的DataFrame的索引:

left1.join(right1,on = 'key')

也可以向join方法中传入一个DataFrame列表,注意,始终是根据索引进行连接,且默认内连接,除非指定how参数为‘outer’

lefth2.join([right2,another],how = 'outer')
Numpy的concatenate函数可以实现在numpy数组上的 拼接、绑定或堆叠

arr = np.arange(12).reshape((3,4))
np.concatenate([arr,arr],axis = 1)
array([[ 0,  1,  2,  3,  0,  1,  2,  3],
       [ 4,  5,  6,  7,  4,  5,  6,  7],
       [ 8,  9, 10, 11,  8,  9, 10, 11]])

默认情况下,pd.concat方法时沿着axis = 0的轴向生效的,生成一个Series,如果你传递axis = 1(根据列),返回的结果则是一个DataFrame。

s1 = pd.Series([0,1],index = ['a','b'])
s2 = pd.Series([2,3,4],index = ['c','d','e'])
s3 = pd.Series([5,6],index = ['f','g'])
pd.concat([s1,s2,s3])
a    0
b    1
c    2
d    3
e    4
f    5
g    6
dtype: int64
pd.concat([s1,s2,s3],axis = 1)
    0       1       2
a   0.0     NaN     NaN
b   1.0     NaN     NaN
c   NaN     2.0     NaN
d   NaN     3.0     NaN
e   NaN     4.0     NaN
f   NaN     NaN     5.0
g   NaN     NaN     6.0
s4 = pd.concat([s1,s3])
s4
a    0
b    1
f    5
g    6
dtype: int64
pd.concat([s1,s4],axis = 1,sort=False)
    0   1
a   0.0     0
b   1.0     1
f   NaN     5
g   NaN     6
#只有在轴向为axis=1时传入join参数才有意义
pd.concat([s1,s4],axis = 1,join ='inner')
    0   1
a   0   0
b   1   1
#可以使用jreindex方法来指明用于连接其他轴向的轴,join_axes已被弃用
pd.concat([s1,s4],axis = 1).reindex(['a','c','b','e'])

    0       1
a   0.0     0.0
c   NaN     NaN
b   1.0     1.0
e   NaN     NaN
#假设你想在轴上创建一个多层索引 ,可以使用keys参数来实现:
result = pd.concat([s1,s3,s4],keys = ['one','two','three'])
result
one    a    0
       b    1
two    f    5
       g    6
three  a    0
       b    1
       f    5
       g    6
dtype: int64

将相同逻辑拓展到DataFrame对象:

df1 = DataFrame(np.arange(6).reshape((3,2)),index = ['a','b','c'],
               columns = ['one','two'])
df2 = DataFrame(5+np.arange(4).reshape(2,2),index = ['a','c'],
               columns = ['three','four'])
pd.concat([df1,df2],sort=False)
    four    one     three   two
a   NaN     0.0     NaN     1.0
b   NaN     2.0     NaN     3.0
c   NaN     4.0     NaN     5.0
a   6.0     NaN     5.0     NaN
c   8.0     NaN     7.0     NaN
pd.concat([df1,df2],axis = 1,keys= ['level1','level2'],sort=False)
    level1          level2
    one     two     three   four
a   0   1   5.0     6.0
b   2   3   NaN     NaN
c   4   5   7.0     8.0
#如果你传递的是对象的字典而不是列表的话,则字典的键会用于keys选项:
pd.concat({'level1':df1,'level2':df2},axis = 1,sort=False)
    level1          level2
    one     two     three   four
a   0   1   5.0     6.0
b   2   3   NaN     NaN
c   4   5   7.0     8.0
#我们使用names参数命名生成的轴层级
pd.concat([df1,df2],axis = 1,keys = ['level1','level2'],names = ['upper','lower'],sort=False)
upper   level1  level2
lower   one     two     three   four
a       0       1       5.0     6.0
b       2       3       NaN     NaN
c       4       5       7.0     8.0

最需要考虑的是行索引中不包含任何数据的DataFrame:

df1 = pd.DataFrame(np.random.randn(3,4),columns = ['a','b','c','d'])
df2 = pd.DataFrame(np.random.randn(2,3),columns = ['b','d','a'])
pd.concat([df1,df2],sort = False)
    a           b           c           d
0   -1.410499   0.539828    0.403357    -0.207983
1   0.222250    -0.001390   1.897430    0.742536
2   -0.320451   1.282065    1.041709    0.764900
0   1.311377    2.130122    NaN         0.494659
1   -0.046373   1.549453    NaN         -0.410902
#我们传入ignore_index = True
pd.concat([df1,df2],ignore_index=True,sort=False)
    a           b           c           d
0   -1.410499   0.539828    0.403357    -0.207983
1   0.222250    -0.001390   1.897430    0.742536
2   -0.320451   1.282065    1.041709    0.764900
3   1.311377    2.130122    NaN         0.494659
4   -0.046373   1.549453    NaN         -0.410902

concat函数的参数

联合重叠数据
有两个数据集,索引全部或部分重叠。考虑numpy的where函数,这个函数可以进行面向数组的if-else等价操作:

a = pd.Series([np.nan,2.5,0.0,3.5,4.5,np.nan],index = ['f','e','d','c','b','a'])
b= pd.Series([0.,np.nan,2.,np.nan,np.nan,5.],index = ['a','b','c','d','e','f'])
np.where(pd.isnull(a),b,a)#a为NAN就用b的值(但是修补时索引没有对齐)
array([0. , 2.5, 0. , 3.5, 4.5, 5. ])
#Series中有一个combine_first方法:
b.combine_first(a)#根据传入的a 的值来修补b的值
a    0.0
b    4.5
c    2.0
d    0.0
e    2.5
f    5.0
dtype: float64

在DataFrame中,combine_first逐 列做相同的操作:

df1 = pd.DataFrame({'a':[1.,np.nan,5.,np.nan],'b':[np.nan,2.,np.nan,6.], 'c':range(2,18,4)})
df2 = pd.DataFrame({'a':[5.,4.,np.nan,3.,7.], 'b':[np.nan,3.,4.,6.,8.]})
df1.combine_first(df2)
    a       b       c
0   1.0     NaN     2.0
1   4.0     2.0     6.0
2   5.0     4.0     10.0
3   3.0     6.0     14.0
4   7.0     8.0     NaN

重塑和透视

适用多层索引进行重塑
stack(堆叠):将列的数据透视到行(旋转)
unstack(拆堆):将行中的数据透视到列

data = pd.DataFrame(np.arange(6).reshape((2,3)),
                   index = pd.Index(['Ohio','Colorado'],name = 'state'),
                   columns = pd.Index(['one','two','three'],name = 'number'))
data
number   one   two  three
state           
Ohio     0     1    2
Colorado 3     4    5
result = data.stack()
result
state     number
Ohio      one       0
          two       1
          three     2
Colorado  one       3
          two       4
          three     5
dtype: int32
data.stack().unstack()

number   one    two     three
state           
Ohio     0      1       2
Colorado 3      4       5
# 你也可以传入一个层级序号(这里传入的是0) 或 名称(对应0的就是‘state’) 来拆分一个不同的层级;
#当然如果传入1或者‘number’返回的就是同上面一样默认情况下的出现结果
data.stack().unstack(0)
state   Ohio    Colorado
number      
one     0       3
two     1       4
three   2       5

由于层级中的所有值并未包含于每个子分组中时,拆分可能会引入缺失值

s1 = pd.Series([0,1,2,3],index = ['a','b','c','d'])
s2 = pd.Series([4,5,6],index = ['c','d','e'])
data2 = pd.concat([s1,s2], keys = ['one','two'])
data2
one  a    0
     b    1
     c    2
     d    3
two  c    4
     d    5
     e    6
dtype: int64
data2.unstack()#永远默认从最内侧拆分

    a   b   c   d   e
one     0.0     1.0     2.0     3.0     NaN
two     NaN     NaN     4.0     5.0     6.0
data2.unstack(0)

    one     two
a   0.0     NaN
b   1.0     NaN
c   2.0     4.0
d   3.0     5.0
e   NaN     6.0
data2.unstack().stack()#回到原来的样子
data2.unstack().stack(dropna = False)#保留不同层级中的缺失值
one  a    0.0
     b    1.0
     c    2.0
     d    3.0
     e    NaN
two  a    NaN
     b    NaN
     c    4.0
     d    5.0
     e    6.0
dtype: float64

在DataFrame中拆堆时,被拆堆的层级会变为结果中最低的层级:

df = pd.DataFrame({'left':result,'right':result+5},
                 columns = pd.Index(['left','right'],name = 'side'))
df
    side      left  right
state    number         
Ohio     one    0   5
         two    1   6
         three  2   7
Colorado one    3   8
         two    4   9
         three  5   10
df.unstack()#可以看到最内侧(最低)层级的number行索引变成了行标签中最内侧(最低)的层级,等价于传入1或者‘number’
side    left    right
number  one     two     three   one     two     three
state                       
Ohio    0   1   2   5   6   7
Colorado    3   4   5   8   9   10
df.unstack('state')#等价于传入0
side    left                right
state   Ohio    Colorado    Ohio    Colorado
number              
one     0       3           5       8
two     1       4           6       9
three   2       5           7       10
#来更多花里胡哨的变化
df.unstack('state').stack()
    side    left    right
number  state       
one     Ohio    0   5
      Colorado  3   8
two     Ohio    1   6
      Colorado  4   9
three   Ohio    2   7
      Colorado  5   10
df.unstack('state').stack('side')#等价于把‘side’替换成0,若传入1等价于替换为‘state’
    state   Colorado    Ohio
number  side        
one     left    3   0
right   8   5
two     left    4   1
right   9   6
three   left    5   2
right   10  7
df.unstack('state').stack(['state','side'])#传入一个列表时,我裂开了
number  state     side 
one     Ohio      left      0
                  right     5
        Colorado  left      3
                  right     8
two     Ohio      left      1
                  right     6
        Colorado  left      4
                  right     9
three   Ohio      left      2
                  right     7
        Colorado  left      5
                  right    10
dtype: int32

将“长”数据透视为“宽”数据

data = pd.read_csv('macrodata.csv')
data.head()
periods = pd.PeriodIndex(year=data.year,quarter = data.quarter,name = 'date')
periods#PeriodIndex将year和quarter等列进行联合生成了一种时间间隔索引
PeriodIndex(['1959Q1', '1959Q2', '1959Q3', '1959Q4', '1960Q1', '1960Q2',
             '1960Q3', '1960Q4', '1961Q1', '1961Q2',
             ...
             '2007Q2', '2007Q3', '2007Q4', '2008Q1', '2008Q2', '2008Q3',
             '2008Q4', '2009Q1', '2009Q2', '2009Q3'],
            dtype='period[Q-DEC]', name='date', length=203, freq='Q-DEC')
columns = pd.Index(['realgdp','infl','unemp'],name = 'item')
columns
Index(['realgdp', 'infl', 'unemp'], dtype='object', name='item')
data = data.reindex(columns = columns)
data.head()
item    realgdp     infl    unemp
0      2710.349     0.00    5.8
1      2778.801     2.34    5.1
2      2775.488     2.74    5.3
3      2785.204     0.27    5.6
4      2847.699     2.31    5.2
data.index = periods.to_timestamp('D','end')
data.head()#此时数据索引变为
item                            realgdp     infl    unemp
date            
1959-03-31 23:59:59.999999999   2710.349    0.00    5.8
1959-06-30 23:59:59.999999999   2778.801    2.34    5.1
1959-09-30 23:59:59.999999999   2775.488    2.74    5.3
1959-12-31 23:59:59.999999999   2785.204    0.27    5.6
1960-03-31 23:59:59.999999999   2847.699    2.31    5.2
ldata = data.stack().reset_index().rename(columns = {0:'value'})
ldata[:10]#可以看到数据堆叠为了一列value
    date                            item    value
0   1959-03-31 23:59:59.999999999   realgdp 2710.349
1   1959-03-31 23:59:59.999999999   infl    0.000
2   1959-03-31 23:59:59.999999999   unemp   5.800
3   1959-06-30 23:59:59.999999999   realgdp 2778.801
4   1959-06-30 23:59:59.999999999   infl    2.340
5   1959-06-30 23:59:59.999999999   unemp   5.100
6   1959-09-30 23:59:59.999999999   realgdp 2775.488
7   1959-09-30 23:59:59.999999999   infl    2.740
8   1959-09-30 23:59:59.999999999   unemp   5.300
9   1959-12-31 23:59:59.999999999   realgdp 2785.204

这里的reindex()在原有索引的基础上创建新的索引,相当于在原数据中取对应列的数据。(有点切片的意思)。
stack(),堆叠数据,这里直接将三列数据堆叠为一列,返回的是一个Series
reset_index,重建索引,返回一个DataFrame
rename(),是对列进行命名,这里将使用字典键指定索引值0对应堆叠出来的一列,命名为value。

要是突然又想变回去了怎么办,毕竟这样就一列values的直观效果不好,考虑DataFrame的pivot方法,传递的前两个值是用作行和列索引的列,然后是可选的values列以填充DataFrame。

ldata.pivot('date','item','value')

假设你有两个数值列
如果不指定第三个也就是values参数,会得到一个含有分层索引的DataFrame

ldata['value2'] = np.random.randn(len(ldata))
ldata.pivot('date','item')

这里的pivot方法等价于使用set_index(['date','item']),然后调用unstack方法拆堆

ldata.set_index(['date','item']).unstack()

将‘宽’透视为‘长’

pivot方法的反操作是pd.melt。将多列合并为一列,产生一个新的DataFrame,其长度比输入更长。

df = pd.DataFrame({'key':['foo','bar','baz'],'A':[1,2,3],'B':[4,5,6],'C':[7,8,9]})
    key     A   B   C
0   foo     1   4   7
1   bar     2   5   8
2   baz     3   6   9
#使用melt方法必须指明分组指标(如果有的话)
melted = pd.melt(df,['key'])
    key     variable  value
0   foo     A         1
1   bar     A         2
2   baz     A         3
3   foo     B         4
4   bar     B         5
5   baz     B         6
6   foo     C         7
7   bar     C         8
8   baz     C         9
reshaped = melted.pivot('key','variable','value')

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

推荐阅读更多精彩内容