12-18沼琉、19 第07章 數(shù)據(jù)清洗和準(zhǔn)備

其實(shí)數(shù)據(jù)分析中80%的時(shí)間都是在數(shù)據(jù)清理部分娃承,loading, clearning, transforming, rearranging奏夫。而pandas非常適合用來執(zhí)行這些任務(wù)。

7.1 Handling Missing Data

在pandas中历筝,missing data呈現(xiàn)的方式有些缺點(diǎn)的酗昼,但對(duì)大部分用戶能起到足夠的效果。對(duì)于數(shù)值型數(shù)據(jù)梳猪,pandas用浮點(diǎn)值Nan(Not a Number)來表示缺失值麻削。我們稱之為識(shí)別符(sentinel value),這種值能被輕易檢測(cè)到:

import pandas as pd
import numpy as np
In [10]: string_data = pd.Series(['aardvark', 'artichoke', np.nan, 'avocado'])

In [11]: string_data
Out[11]:
0     aardvark
1    artichoke
2          NaN
3      avocado
dtype: object

In [12]: string_data.isnull()
Out[12]: 
0    False
1    False
2     True
3    False
dtype: bool

在pandas中春弥,我們使用了R語言中的一些傳統(tǒng)呛哟,把缺失值表示為NA(not available)。在統(tǒng)計(jì)應(yīng)用里匿沛,NA數(shù)據(jù)別是要么是數(shù)據(jù)不存在扫责,要么是存在但不能被檢測(cè)到。做數(shù)據(jù)清理的時(shí)候逃呼,對(duì)缺失值做分析是很重要的鳖孤,我們要確定是否是數(shù)據(jù)收集的問題,或者缺失值是否會(huì)帶來潛在的偏見抡笼。
內(nèi)建的Python None值也被當(dāng)做NA:

In [13]: string_data[0] = None

In [14]: string_data.isnull()
Out[14]: 
0     True
1    False
2     True
3    False
dtype: bool

pandas項(xiàng)目中還在不斷優(yōu)化內(nèi)部細(xì)節(jié)以更好處理缺失數(shù)據(jù)苏揣,像用戶API功能,例如pandas.isnull蔫缸,去除了許多惱人的細(xì)節(jié)腿准。表7-1列出了一些關(guān)于缺失數(shù)據(jù)處理的函數(shù)唯蝶。


表7-1 NA處理方法

1 Filtering Out Missing Data(過濾缺失值)

有一些方法來過濾缺失值翅溺。可以使用pandas.isnull和boolean indexing, 配合使用dropna糜俗。對(duì)于series校翔,只會(huì)返回non-null數(shù)據(jù)和index values:

In [15]: from numpy import nan as NA

In [16]: data = pd.Series([1, NA, 3.5, NA, 7])

In [17]: data.dropna()
Out[17]: 
0    1.0
2    3.5
4    7.0
dtype: float64

這等價(jià)于:

In [18]: data[data.notnull()]
Out[18]: 
0    1.0
2    3.5
4    7.0
dtype: float64

對(duì)于DataFrame弟跑,會(huì)復(fù)雜一些。你可能想要?jiǎng)h除包含有NA的row和column防症。dropna默認(rèn)會(huì)刪除包含有缺失值的row:

In [19]: data = pd.DataFrame([[1., 6.5, 3.], [1., NA, NA],
   ....:                      [NA, NA, NA], [NA, 6.5, 3.]])

In [20]: cleaned = data.dropna()

In [21]: data
Out[21]: 
     0    1    2
0  1.0  6.5  3.0
1  1.0  NaN  NaN
2  NaN  NaN  NaN
3  NaN  6.5  3.0

In [22]: cleaned
Out[22]: 
     0    1    2
0  1.0  6.5  3.0

設(shè)定how=all只會(huì)刪除那些全是NA的行:

In [23]: data.dropna(how='all')
Out[23]: 
     0    1    2
0  1.0  6.5  3.0
1  1.0  NaN  NaN
3  NaN  6.5  3.0

刪除列也一樣孟辑,設(shè)置axis=1:

In [24]: data[4] = NA

In [25]: data
Out[25]: 
     0    1    2   4
0  1.0  6.5  3.0 NaN
1  1.0  NaN  NaN NaN
2  NaN  NaN  NaN NaN
3  NaN  6.5  3.0 NaN

In [26]: data.dropna(axis=1, how='all')
Out[26]: 
     0    1    2
0  1.0  6.5  3.0
1  1.0  NaN  NaN
2  NaN  NaN  NaN
3  NaN  6.5  3.0

一種刪除DataFrame row的相關(guān)應(yīng)用是是time series data哎甲。假設(shè)你想要保留有特定數(shù)字的觀測(cè)結(jié)果,可以使用thresh參數(shù):

In [38]: from numpy import nan as NA
In [39]: df = pd.DataFrame(np.random.randn(7, 3))
In [40]: df.iloc[:4, 1] = NA
In [41]: df.iloc[:2, 2] = NA
    ...: df
Out[41]:
          0         1         2
0 -1.246074       NaN       NaN
1  0.778845       NaN       NaN
2 -1.898484       NaN  0.425853
3  1.077799       NaN -1.412578
4 -1.035750 -2.607838  0.192172
5 -0.112143  0.204284  1.039244
6  0.749974 -2.872124 -0.376451

In [42]: df.dropna(thresh=2) #留下的一行至少有兩個(gè)數(shù)不是缺失值
Out[42]:
          0         1         2
2 -1.898484       NaN  0.425853
3  1.077799       NaN -1.412578
4 -1.035750 -2.607838  0.192172
5 -0.112143  0.204284  1.039244
6  0.749974 -2.872124 -0.376451

In [43]: df.dropna(thresh=3)
Out[43]:
          0         1         2
4 -1.035750 -2.607838  0.192172
5 -0.112143  0.204284  1.039244
6  0.749974 -2.872124 -0.376451

2 Filling In Missing Data(填補(bǔ)缺失值)

不是刪除缺失值饲嗽,而是用一些數(shù)字填補(bǔ)炭玫。對(duì)于大部分目的,fillna是可以用的貌虾。調(diào)用fillna的時(shí)候設(shè)置好一個(gè)常用用來替換缺失值:

In [33]: df.fillna(0)
Out[33]: 
          0         1         2
0 -0.204708  0.000000  0.000000
1 -0.555730  0.000000  0.000000
2  0.092908  0.000000  0.769023
3  1.246435  0.000000 -1.296221
4  0.274992  0.228913  1.352917
5  0.886429 -2.001637 -0.371843
6  1.669025 -0.438570 -0.539741

給fillna傳入一個(gè)dict吞加,可以給不同列替換不同的值:

In [34]: df.fillna({1: 0.5, 2: 0})
Out[34]: 
          0         1         2
0 -0.204708  0.500000  0.000000
1 -0.555730  0.500000  0.000000
2  0.092908  0.500000  0.769023
3  1.246435  0.500000 -1.296221
4  0.274992  0.228913  1.352917
5  0.886429 -2.001637 -0.371843
6  1.669025 -0.438570 -0.539741

fillna返回一個(gè)新對(duì)象,但你可以使用in-place來直接更改原有的數(shù)據(jù):

In [35]: _ = df.fillna(0, inplace=True)

In [36]: df
Out[36]: 
          0         1         2
0 -0.204708  0.000000  0.000000
1 -0.555730  0.000000  0.000000
2  0.092908  0.000000  0.769023
3  1.246435  0.000000 -1.296221
4  0.274992  0.228913  1.352917
5  0.886429 -2.001637 -0.371843
6  1.669025 -0.438570 -0.539741

在使用fillna的時(shí)候尽狠,這種插入法同樣能用于reindexing:

In [37]: df = pd.DataFrame(np.random.randn(6, 3))

In [38]: df.iloc[2:, 1] = NA

In [39]: df.iloc[4:, 2] = NA

In [40]: df
Out[40]: 
          0         1         2
0  0.476985  3.248944 -1.021228
1 -0.577087  0.124121  0.302614
2  0.523772       NaN  1.343810
3 -0.713544       NaN -2.370232
4 -1.860761       NaN       NaN
5 -1.265934       NaN       NaN

In [41]: df.fillna(method='ffill')
Out[41]: 
          0         1         2
0  0.476985  3.248944 -1.021228
1 -0.577087  0.124121  0.302614
2  0.523772  0.124121  1.343810
3 -0.713544  0.124121 -2.370232
4 -1.860761  0.124121 -2.370232
5 -1.265934  0.124121 -2.370232

In [42]: df.fillna(method='ffill', limit=2) #可以連續(xù)填充的最大量是2
Out[42]: 
          0         1         2
0  0.476985  3.248944 -1.021228
1 -0.577087  0.124121  0.302614
2  0.523772  0.124121  1.343810
3 -0.713544  0.124121 -2.370232
4 -1.860761       NaN -2.370232
5 -1.265934       NaN -2.370232

只要有些創(chuàng)新衔憨,你就可以利用fillna實(shí)現(xiàn)許多別的功能。比如說袄膏,你可以傳入Series的平均值或中位數(shù):

In [43]: data = pd.Series([1., NA, 3.5, NA, 7])

In [44]: data.fillna(data.mean())
Out[44]: 
0    1.000000
1    3.833333
2    3.500000
3    3.833333
4    7.000000
dtype: float64
fillna函數(shù)參數(shù)

7.2 數(shù)據(jù)轉(zhuǎn)換

本章到目前為止介紹的都是數(shù)據(jù)的重排践图。另一類重要操作則是過濾、清理以及其他的轉(zhuǎn)換工作沉馆。

1 刪除重復(fù)值

DataFrame中出現(xiàn)重復(fù)行有多種原因码党。下面就是一個(gè)例子:

In [45]: data = pd.DataFrame({'k1': ['one', 'two'] * 3 + ['two'],
   ....:                      'k2': [1, 1, 2, 3, 3, 4, 4]})

In [46]: data
Out[46]: 
    k1  k2
0  one   1
1  two   1
2  one   2
3  two   3
4  one   3
5  two   4
6  two   4

DataFrame方法duplicated返回的是一個(gè)布爾 Series,表示一個(gè)row是否是重復(fù)的(根據(jù)前一行來判斷):

In [47]: data.duplicated()
Out[47]: 
0    False
1    False
2    False
3    False
4    False
5    False
6     True
dtype: bool

drop_duplicateds返回一個(gè)DataFrame悍及,會(huì)刪除重復(fù)的部分:

In [48]: data.drop_duplicates()
Out[48]: 
    k1  k2
0  one   1
1  two   1
2  one   2
3  two   3
4  one   3
5  two   4

上面兩種方法都默認(rèn)考慮所有列闽瓢;另外,我們可以指定一部分來檢測(cè)重復(fù)值心赶。假設(shè)我們只想檢測(cè)'k1'列的重復(fù)值:

In [49]: data['v1'] = range(7)

In [50]: data.drop_duplicates(['k1'])
Out[50]: 
    k1  k2  v1
0  one   1   0
1  two   1   1

duplicated和drop_duplicated默認(rèn)保留第一次觀測(cè)到的數(shù)值組合扣讼。設(shè)置keep='last'能返回最后一個(gè):

In [51]: data.drop_duplicates(['k1', 'k2'], keep='last')
Out[51]: 
    k1  k2  v1
0  one   1   0
1  two   1   1
2  one   2   2
3  two   3   3
4  one   3   4
6  two   4   6

2 Transforming Data Using a Function or Mapping(用函數(shù)和映射來轉(zhuǎn)換數(shù)據(jù))

有時(shí)候我們可能希望做一些數(shù)據(jù)轉(zhuǎn)換。比如下面一個(gè)例子缨叫,有不同種類的肉:

In [52]: data = pd.DataFrame({'food': ['bacon', 'pulled pork', 'bacon',
   ....:                               'Pastrami', 'corned beef', 'Bacon',
   ....:                               'pastrami', 'honey ham', 'nova lox'],
   ....:                      'ounces': [4, 3, 12, 6, 7.5, 8, 3, 5, 6]})

In [53]: data
Out[53]: 
          food  ounces
0        bacon     4.0
1  pulled pork     3.0
2        bacon    12.0
3     Pastrami     6.0
4  corned beef     7.5
5        Bacon     8.0
6     pastrami     3.0
7    honey ham     5.0
8     nova lox     6.0

假設(shè)你想加一列椭符,表明每種肉來源的動(dòng)物是什么。我們可以寫一個(gè)映射:

meat_to_animal = {
  'bacon': 'pig',
  'pulled pork': 'pig',
  'pastrami': 'cow',
  'corned beef': 'cow',
  'honey ham': 'pig',
  'nova lox': 'salmon'
}

用于series的map方法接受一個(gè)函數(shù)耻姥,或是一個(gè)字典销钝,包含著映射關(guān)系,但這里有一個(gè)小問題琐簇,有些肉是大寫蒸健,有些是小寫。因此婉商,我們先用str.lower把所有的值變?yōu)樾?

In [55]: lowercased = data['food'].str.lower()

In [56]: lowercased
Out[56]: 
0          bacon
1    pulled pork
2          bacon
3       pastrami
4    corned beef
5          bacon
6       pastrami
7      honey ham
8       nova lox
Name: food, dtype: object

In [57]: data['animal'] = lowercased.map(meat_to_animal)

In [58]: data
Out[58]: 
          food  ounces  animal
0        bacon     4.0     pig
1  pulled pork     3.0     pig
2        bacon    12.0     pig
3     Pastrami     6.0     cow
4  corned beef     7.5     cow
5        Bacon     8.0     pig
6     pastrami     3.0     cow
7    honey ham     5.0     pig
8     nova lox     6.0  salmon

我們也可以用一個(gè)函數(shù)解決上面的問題:

In [59]: data['food'].map(lambda x: meat_to_animal[x.lower()])
Out[59]: 
0       pig
1       pig
2       pig
3       cow
4       cow
5       pig
6       cow
7       pig
8    salmon
Name: food, dtype: object

使用map是一個(gè)很簡(jiǎn)便的方法似忧,用于element-wise轉(zhuǎn)換和其他一些數(shù)據(jù)清洗操作。

3 Replacing Values(替換值)

其實(shí)fillna是一個(gè)特殊換的替換操作丈秩。map可以用于修改一個(gè)object里的部分值盯捌,但是replace能提供一個(gè)更簡(jiǎn)單和更靈活的方法做到這點(diǎn)。下面是一個(gè)series:

In [60]: data = pd.Series([1., -999., 2., -999., -1000., 3.])

In [61]: data
Out[61]: 
0       1.0
1    -999.0
2       2.0
3    -999.0
4   -1000.0
5       3.0

這里-999可能是用來表示缺失值的標(biāo)識(shí)符蘑秽。用NA來替代的話饺著,用replace箫攀,會(huì)產(chǎn)生一個(gè)新series(除非使用inplace=True):

In [62]: data.replace(-999, np.nan)
Out[62]: 
0       1.0
1       NaN
2       2.0
3       NaN
4   -1000.0
5       3.0
dtype: float64

如果想要一次替換多個(gè)值,直接用一個(gè)list即可:

In [63]: data.replace([-999, -1000], np.nan)
Out[63]: 
0    1.0
1    NaN
2    2.0
3    NaN
4    NaN
5    3.0
dtype: float64

對(duì)于不同的值用不同的替換值幼衰,也是導(dǎo)入一個(gè)list:

In [64]: data.replace([-999, -1000], [np.nan, 0])
Out[64]: 
0    1.0
1    NaN
2    2.0
3    NaN
4    0.0
5    3.0
dtype: float64

參數(shù)也可以是一個(gè)dict:

In [65]: data.replace({-999: np.nan, -1000: 0})
Out[65]: 
0    1.0
1    NaN
2    2.0
3    NaN
4    0.0
5    3.0
dtype: float64

注意:data.replace方法和data.str.replace方法是不同的靴跛,后者會(huì)對(duì)string進(jìn)行element-wise替換。

4 Renaming Axis Indexes(重命名Axis Indexes)

像是series里的value一樣塑顺,axis label也能類似地是函數(shù)或映射來轉(zhuǎn)換汤求,產(chǎn)生一個(gè)新的object俏险。當(dāng)然也可以設(shè)置in-place不產(chǎn)生新的數(shù)據(jù):

In [66]: data = pd.DataFrame(np.arange(12).reshape((3, 4)),
   ....:                     index=['Ohio', 'Colorado', 'New York'],
   ....:                     columns=['one', 'two', 'three', 'four'])

跟Series一樣严拒,軸索引也有一個(gè)map方法:

In [67]: transform = lambda x: x[:4].upper()

In [68]: data.index.map(transform)
Out[68]: Index(['OHIO', 'COLO', 'NEW '], dtype='object')

可以賦值給index,以in-place的方式修改DataFrame:

In [69]: data.index = data.index.map(transform)

In [70]: data
Out[70]:
one  two  three  four
OHIO    0    1      2     3
COLO    4    5      6     7
NEW     8    9     10    11

如果你想要?jiǎng)?chuàng)建一個(gè)轉(zhuǎn)換后的版本竖独,而且不用修改原始的數(shù)據(jù)裤唠,可以用rename:

In [71]: data.rename(index=str.title, columns=str.upper)
Out[71]: 
      ONE  TWO  THREE  FOUR
Ohio    0    1      2     3
Colo    4    5      6     7
New     8    9     10    11

注意,rename能用于dict一樣的oject莹痢,

In [72]: data.rename(index={'OHIO': 'INDIANA'},
   ....:             columns={'three': 'peekaboo'})
Out[72]:
one two pekaboo four
INDIANA 0   1   2   3
COLO    4   5   6   7
NEW 8   9   10  11

rename能讓你避免陷入手動(dòng)賦值給index和columns的雜務(wù)中种蘸。可以用inplace直接修改原始數(shù)據(jù):

In [73]: data.rename(index={'OHIO': 'INDIANA'}, inplace=True)

In [74]: data
Out[74]: 
         one  two  three  four
INDIANA    0    1      2     3
COLO       4    5      6     7
NEW        8    9     10    11

5 Discretization and Binning(離散化和裝箱)

連續(xù)型數(shù)據(jù)經(jīng)常被離散化或分散成bins(分箱)來分析竞膳。假設(shè)你有一組數(shù)據(jù)航瞭,你想把人分到不同的年齡組里:

In [75]: ages = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]

接下來將這些數(shù)據(jù)劃分為“18到25”、“26到35”坦辟、“35到60”以及“60以上”幾個(gè)面元刊侯。要實(shí)現(xiàn)該功能,你需要使用pandas的cut函數(shù):

In [76]: bins = [18, 25, 35, 60, 100]

In [77]: cats = pd.cut(ages, bins)

In [78]: cats
Out[78]: 
[(18, 25], (18, 25], (18, 25], (25, 35], (18, 25], ..., (25, 35], (60, 100], (35,60], (35, 60], (25, 35]]
Length: 12
Categories (4, interval[int64]): [(18, 25] < (25, 35] < (35, 60] < (60, 100]]

返回的是一個(gè)特殊的Categorical object锉走。我們看到的結(jié)果描述了pandas.cut如何得到bins滨彻。可以看作是一個(gè)string數(shù)組用來表示bin的名字挪蹭,它內(nèi)部包含了一個(gè)categories數(shù)組亭饵,用來記錄不同類別的名字,并伴有表示ages的label(可以通過codes屬性查看):

In [79]: cats.codes
Out[79]: array([0, 0, 0, 1, 0, 0, 2, 1, 3, 2, 2, 1], dtype=int8)

In [80]: cats.categories
Out[80]: 
IntervalIndex([(18, 25], (25, 35], (35, 60], (60, 100]]
              closed='right',
              dtype='interval[int64]')

In [81]: pd.value_counts(cats)
Out[81]: 
(18, 25]     5
(35, 60]     3
(25, 35]     3
(60, 100]    1
dtype: int64

這里pd.value_counts(cats)是pandas.cut后bin的數(shù)量梁厉。

這里我們注意一下區(qū)間辜羊。括號(hào)表示不包含,方括號(hào)表示包含词顾。你可以自己設(shè)定哪一邊關(guān)閉(right=False):

Out[82]: 
[[18, 26), [18, 26), [18, 26), [26, 36), [18, 26), ..., [26, 36), [61, 100), [36,
 61), [36, 61), [26, 36)]
Length: 12
Categories (4, interval[int64]): [[18, 26) < [26, 36) < [36, 61) < [61, 100)]

你也可以用一個(gè)list或數(shù)組給labels選項(xiàng)來設(shè)定bin的名字:

In [83]: group_names = ['Youth', 'YoungAdult', 'MiddleAged', 'Senior']

In [84]: pd.cut(ages, bins, labels=group_names)
Out[84]: 
[Youth, Youth, Youth, YoungAdult, Youth, ..., YoungAdult, Senior, MiddleAged, Mid
dleAged, YoungAdult]
Length: 12
Categories (4, object): [Youth < YoungAdult < MiddleAged < Senior]

如果你只是給一個(gè)bins的數(shù)量來cut八秃,而不是自己設(shè)定每個(gè)bind的范圍,cut會(huì)根據(jù)最大值和最小值來計(jì)算等長(zhǎng)的bins计技。比如下面我們想要做一個(gè)均勻分布的四個(gè)bins:

In [85]: data = np.random.rand(20)

In [86]: pd.cut(data, 4, precision=2) #選項(xiàng)precision=2喜德,限定小數(shù)只有兩位
Out[86]: 
[(0.34, 0.55], (0.34, 0.55], (0.76, 0.97], (0.76, 0.97], (0.34, 0.55], ..., (0.34
, 0.55], (0.34, 0.55], (0.55, 0.76], (0.34, 0.55], (0.12, 0.34]]
Length: 20
Categories (4, interval[float64]): [(0.12, 0.34] < (0.34, 0.55] < (0.55, 0.76] < 
(0.76, 0.97]]

選項(xiàng)precision=2,限定小數(shù)只有兩位垮媒。

一個(gè)近似的函數(shù)舍悯,qcut航棱,會(huì)按照數(shù)據(jù)的分位數(shù)來分箱。取決于數(shù)據(jù)的分布萌衬,用cut通常不能保證每一個(gè)bin有一個(gè)相同數(shù)量的數(shù)據(jù)點(diǎn)饮醇。而qcut是按百分比來切的,所以可以得到等數(shù)量的bins:

In [87]: data = np.random.randn(1000)  # Normally distributed

In [88]: cats = pd.qcut(data, 4)  # Cut into quartiles

In [89]: cats
Out[89]: 
[(-0.0265, 0.62], (0.62, 3.928], (-0.68, -0.0265], (0.62, 3.928], (-0.0265, 0.62]
, ..., (-0.68, -0.0265], (-0.68, -0.0265], (-2.95, -0.68], (0.62, 3.928], (-0.68,
 -0.0265]]
Length: 1000
Categories (4, interval[float64]): [(-2.95, -0.68] < (-0.68, -0.0265] < (-0.0265,
 0.62] <
                                    (0.62, 3.928]]

In [90]: pd.value_counts(cats)
Out[90]:
(0.62, 3.928]       250
(-0.0265, 0.62]     250
(-0.68, -0.0265]    250
(-2.95, -0.68]      250
dtype: int64

類似的秕豫,在cut中我們可以自己指定百分比:

In [91]: pd.qcut(data, [0, 0.1, 0.5, 0.9, 1.])
Out[91]: 
[(-0.0265, 1.286], (-0.0265, 1.286], (-1.187, -0.0265], (-0.0265, 1.286], (-0.026
5, 1.286], ..., (-1.187, -0.0265], (-1.187, -0.0265], (-2.95, -1.187], (-0.0265, 
1.286], (-1.187, -0.0265]]
Length: 1000
Categories (4, interval[float64]): [(-2.95, -1.187] < (-1.187, -0.0265] < (-0.026
5, 1.286] <
                                    (1.286, 3.928]]

在之后的章節(jié)我們還會(huì)用到cut和qcut朴艰,這些離散函數(shù)對(duì)于量化和群聚分析很有用。

6 Detecting and Filtering Outliers(檢測(cè)和過濾異常值)

過濾或轉(zhuǎn)換異常值是數(shù)組操作的一個(gè)重頭戲混移。下面的DataFrame有正態(tài)分布的數(shù)據(jù):

In [92]: data = pd.DataFrame(np.random.randn(1000, 4))

In [93]: data.describe()
Out[93]: 
                 0            1            2            3
count  1000.000000  1000.000000  1000.000000  1000.000000
mean      0.049091     0.026112    -0.002544    -0.051827
std       0.996947     1.007458     0.995232     0.998311
min      -3.645860    -3.184377    -3.745356    -3.428254
25%      -0.599807    -0.612162    -0.687373    -0.747478
50%       0.047101    -0.013609    -0.022158    -0.088274
75%       0.756646     0.695298     0.699046     0.623331
max       2.653656     3.525865     2.735527     3.366626

假設(shè)我們想要找一個(gè)列中祠墅,絕對(duì)值大于3的數(shù)字:

In [94]: col = data[2]

In [95]: col[np.abs(col) > 3]
Out[95]: 
41    -3.399312
136   -3.745356
Name: 2, dtype: float64

選中所有絕對(duì)值大于3的行,可以用any方法在一個(gè)boolean DataFrame上:

In [96]: data[(np.abs(data) > 3).any(1)] # any中axis=1表示column
Out[96]: 
            0         1         2         3
41   0.457246 -0.025907 -3.399312 -0.974657
60   1.951312  3.260383  0.963301  1.201206
136  0.508391 -0.196713 -3.745356 -1.520113
235 -0.242459 -3.056990  1.918403 -0.578828
258  0.682841  0.326045  0.425384 -3.428254
322  1.179227 -3.184377  1.369891 -1.074833
544 -3.548824  1.553205 -2.186301  1.277104
635 -0.578093  0.193299  1.397822  3.366626
782 -0.207434  3.525865  0.283070  0.544635
803 -3.645860  0.255475 -0.549574 -1.907459

根據(jù)數(shù)據(jù)的值是正還是負(fù)歌径,np.sign(data)可以生成1和-1:

In [99]: np.sign(data).head()
Out[99]: 
     0    1    2    3
0 -1.0  1.0 -1.0  1.0
1  1.0 -1.0  1.0 -1.0
2  1.0  1.0  1.0 -1.0
3 -1.0 -1.0  1.0 -1.0
4 -1.0  1.0 -1.0 -1.0

根據(jù)這些條件毁嗦,就可以對(duì)值進(jìn)行設(shè)置。下面的代碼可以將值限制在區(qū)間-3到3以內(nèi):

In [97]: data[np.abs(data) > 3] = np.sign(data) * 3

In [98]: data.describe()
Out[98]: 
                 0            1            2            3
count  1000.000000  1000.000000  1000.000000  1000.000000
mean      0.050286     0.025567    -0.001399    -0.051765
std       0.992920     1.004214     0.991414     0.995761
min      -3.000000    -3.000000    -3.000000    -3.000000
25%      -0.599807    -0.612162    -0.687373    -0.747478
50%       0.047101    -0.013609    -0.022158    -0.088274
75%       0.756646     0.695298     0.699046     0.623331
max       2.653656     3.000000     2.735527     3.000000

7 Permutation and Random Sampling(排列和隨機(jī)采樣)

排列(隨機(jī)排序)一個(gè)series或DataFrame中的row回铛,用numpy.random.permutation函數(shù)很容易就能做到狗准。調(diào)用permutation的時(shí)候設(shè)定好你想要進(jìn)行排列的axis,會(huì)產(chǎn)生一個(gè)整數(shù)數(shù)組表示新的順序:

In [100]: df = pd.DataFrame(np.arange(5 * 4).reshape((5, 4)))

In [101]: sampler = np.random.permutation(5)

In [102]: sampler
Out[102]: array([3, 1, 4, 2, 0])

這個(gè)數(shù)組能被用在基于iloc上的indexing或take函數(shù):

In [103]: df
Out[103]: 
    0   1   2   3
0   0   1   2   3
1   4   5   6   7
2   8   9  10  11
3  12  13  14  15
4  16  17  18  19

In [104]: df.take(sampler)
Out[104]: 
    0   1   2   3
3  12  13  14  15
1   4   5   6   7
4  16  17  18  19
2   8   9  10  11
0   0   1   2   3

為了選中一個(gè)隨機(jī)的子集茵肃,而且沒有代替功能(既不影響原來的值腔长,返回一個(gè)新的series或DataFrame),可以用sample方法:

In [105]: df.sample(n=3)
Out[105]: 
    0   1   2   3
3  12  13  14  15
4  16  17  18  19
2   8   9  10  11

如果想要生成的樣本帶有替代功能(即允許重復(fù))验残,給sample中設(shè)定replace=True:

In [106]: choices = pd.Series([5, 7, -1, 6, 4])

In [107]: draws = choices.sample(n=10, replace=True)

In [108]: draws
Out[108]: 
4    4
1    7
4    4
2   -1
0    5
3    6
1    7
4    4
0    5
4    4
dtype: int64

8 Computing Indicator/Dummy Variables(計(jì)算指示器/虛擬變量)

Dummy Variables:虛擬變量捞附,又稱虛設(shè)變量、名義變量或啞變量,用以反映質(zhì)的屬性的一個(gè)人工變量,是量化了的自變量,通常取值為0或1胚膊。

另一種在統(tǒng)計(jì)模型上的轉(zhuǎn)換或機(jī)器學(xué)習(xí)應(yīng)用是把一個(gè)categorical variable(類別變量)變?yōu)橐粋€(gè)dummy or indicator matrix(虛擬或指示器矩陣)故俐。如果DataFrame中的一列有k個(gè)不同的值,我們可以用一個(gè)矩陣或DataFrame用k列來表示紊婉,1或0药版。pandas有一個(gè)get_dummies函數(shù)實(shí)現(xiàn)這個(gè)工作,當(dāng)然喻犁,你自己設(shè)計(jì)一個(gè)其實(shí)也不難槽片。這里舉個(gè)例子:

In [109]: df = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'],
   .....:                    'data1': range(6)})

In [110]: pd.get_dummies(df['key'])
Out[110]: 
   a  b  c
0  0  1  0
1  0  1  0
2  1  0  0
3  0  0  1
4  1  0  0
5  0  1  0

在一些情況里,如果我們想要給column加一個(gè)prefix肢础, 可以用data.get_dummies里的prefix參數(shù)來實(shí)現(xiàn):

In [11]: dummies = pd.get_dummies(df['key'], prefix='key')
In [12]: dummies
Out[12]:
   key_a  key_b  key_c
0      0      1      0
1      0      1      0
2      1      0      0
3      0      0      1
4      1      0      0
5      0      1      0

In [13]: df_with_dummy = df[['data1']].join(dummies)
    ...: df_with_dummy

Out[13]:
   data1  key_a  key_b  key_c
0      0      0      1      0
1      1      0      1      0
2      2      1      0      0
3      3      0      0      1
4      4      1      0      0
5      5      0      1      0

如果DataFrame中的a row屬于多個(gè)類別还栓,事情會(huì)變得復(fù)雜一些。我們來看一下MoviesLens 1M 數(shù)據(jù)集:

In [114]: mnames = ['movie_id', 'title', 'genres']

In [115]: movies = pd.read_table('datasets/movielens/movies.dat', sep='::',
   .....:                        header=None, names=mnames)

In [116]: movies[:10]
Out[116]: 
   movie_id                               title                        genres
0         1                    Toy Story (1995)   Animation|Children's|Comedy
1         2                      Jumanji (1995)  Adventure|Children's|Fantasy
2         3             Grumpier Old Men (1995)                Comedy|Romance
3         4            Waiting to Exhale (1995)                  Comedy|Drama
4         5  Father of the Bride Part II (1995)                        Comedy
5         6                         Heat (1995)         Action|Crime|Thriller
6         7                      Sabrina (1995)                Comedy|Romance
7         8                 Tom and Huck (1995)          Adventure|Children's
8         9                 Sudden Death (1995)
Action
9        10                    GoldenEye (1995)     Action|Adventure|Thriller

給每個(gè)genre添加一個(gè)指示變量比較麻煩传轰。首先我們先取出所有不同的類別:

In [117]: all_genres = []

In [118]: for x in movies.genres:
   .....:     all_genres.extend(x.split('|'))  #",|;"按,或;來分隔  ;  "|"等同于""剩盒,空或空的意思

In [119]: genres = pd.unique(all_genres)  #不重復(fù)的
In [120]: genres
Out[120]: 
array(['Animation', "Children's", 'Comedy', 'Adventure', 'Fantasy',
       'Romance', 'Drama', 'Action', 'Crime', 'Thriller','Horror',
       'Sci-Fi', 'Documentary', 'War', 'Musical', 'Mystery', 'Film-Noir',
       'Western'], dtype=object)

一種構(gòu)建indicator dataframe的方法是先構(gòu)建一個(gè)全是0的DataFrame:

In [121]: zero_matrix = np.zeros((len(movies), len(genres)))

In [122]: dummies = pd.DataFrame(zero_matrix, columns=genres)
In:dummies.head()
Out:
Animation   Children's  Comedy  Adventure   Fantasy Romance Drama   Action  Crime   Thriller    Horror  Sci-Fi  Documentary War Musical Mystery Film-Noir   Western
0   0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
1   0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
2   0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
3   0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
4   0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0

然后迭代每一部movie,并設(shè)置每一行中的dummies為1慨蛙。使用dummies.columns來計(jì)算每一列的genre的指示器:

In [123]: gen = movies.genres[0]

In [124]: gen.split('|')
Out[124]: ['Animation', "Children's", 'Comedy']

In [125]: dummies.columns.get_indexer(gen.split('|'))
Out[125]: array([0, 1, 2])

然后辽聊,使用.iloc纪挎,根據(jù)索引來設(shè)定值:

In [126]: for i, gen in enumerate(movies.genres):
   .....:     indices = dummies.columns.get_indexer(gen.split('|'))
   .....:     dummies.iloc[i, indices] = 1
   .....:

然后,和以前一樣跟匆,再將其與movies合并起來:

In [127]: movies_windic = movies.join(dummies.add_prefix('Genre_'))

In [128]: movies_windic.iloc[0]
Out[128]: 
movie_id                                       1
title                           Toy Story (1995)
genres               Animation|Children's|Comedy
Genre_Animation                                1
Genre_Children's                               1
Genre_Comedy                                   1
Genre_Adventure                                0
Genre_Fantasy                                  0
Genre_Romance                                  0
Genre_Drama                                    0
                                ...             
Genre_Crime                                    0
Genre_Thriller                                 0
Genre_Horror                                   0
Genre_Sci-Fi                                   0
Genre_Documentary                              0
Genre_War                                      0
Genre_Musical                                  0
Genre_Mystery                                  0
Genre_Film-Noir                                0
Genre_Western                                  0
Name: 0, Length: 21, dtype: object

對(duì)于一個(gè)很大的數(shù)據(jù)集异袄,這種構(gòu)建多個(gè)成員指示變量的方法并不會(huì)加快速度。寫一個(gè)低層級(jí)的函數(shù)來直接寫一個(gè)numpy array玛臂,并把寫過整合到DataFrame會(huì)更快一些烤蜕。

一個(gè)有用的recipe訣竅是把get_dummies和離散函數(shù)(比如cut)結(jié)合起來:

In [129]: np.random.seed(12345)

In [130]: values = np.random.rand(10)

In [131]: values
Out[131]: 
array([ 0.9296,  0.3164,  0.1839,  0.2046,  0.5677,  0.5955,  0.9645,
        0.6532,  0.7489,  0.6536])

In [132]: bins = [0, 0.2, 0.4, 0.6, 0.8, 1]

In [133]: pd.get_dummies(pd.cut(values, bins))
Out[133]: 
   (0.0, 0.2]  (0.2, 0.4]  (0.4, 0.6]  (0.6, 0.8]  (0.8, 1.0]
0           0           0           0           0           1
1           0           1           0           0           0
2           1           0           0           0           0
3           0           1           0           0           0
4           0           0           1           0           0
5           0           0           1           0           0
6           0           0           0           0           1
7           0           0           0           1           0
8           0           0           0           1           0
9           0           0           0           1           0

我們用numpy.random.seed,使這個(gè)例子具有確定性迹冤。本書后面會(huì)介紹pandas.get_dummies讽营。

7.3 String Manipulation(字符串處理)

python很多內(nèi)建方法很適合處理string。而且對(duì)于更復(fù)雜的模式叁巨,可以配合使用正則表達(dá)式斑匪。而pandas則混合了兩種方式。

1 String Object Methods(字符串對(duì)象方法)

大部分string處理锋勺,使用內(nèi)建的一些方法就足夠了。比如狡蝶,可以用split來分割用逗號(hào)區(qū)分的字符串:

In [134]: val = 'a,b,  guido'
In [135]: val.split(',')
Out[135]: ['a', 'b', '  guido']

split常常與strip一起使用庶橱,以去除空白符(包括換行符):

In [136]: pieces = [x.strip() for x in val.split(',')]

In [137]: pieces
Out[137]: ['a', 'b', 'guido']

利用加法,可以將這些子字符串以雙冒號(hào)分隔符的形式連接起來:

In [138]: first, second, third = pieces

In [139]: first + '::' + second + '::' + third
Out[139]: 'a::b::guido'

但這種方法并不python贪惹,更快的方法是直接用join方法:

In [140]: '::'.join(pieces)
Out[140]: 'a::b::guido'

其他一些方法適合鎖定子字符串位置相關(guān)的苏章。用in關(guān)鍵字是檢測(cè)substring最好的方法,當(dāng)然奏瞬,index和find也能完成任務(wù):

In [141]: 'guido' in val
Out[141]: True

In [142]: val.index(',')
Out[142]: 1

In [143]: val.find(':')
Out[143]: -1

注意index和find的區(qū)別枫绅。如果要找的string不存在的話,index會(huì)報(bào)錯(cuò)硼端。而find會(huì)返回-1:

In [144]: val.index(':')
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-144-280f8b2856ce> in <module>()
----> 1 val.index(':')
ValueError: substring not found

count會(huì)返回一個(gè)substring出現(xiàn)的次數(shù):

In [145]: val.count(',')
Out[145]: 2

replace會(huì)取代一種出現(xiàn)方式(pattern)并淋。也通常用于刪除pattern,傳入一個(gè)空字符串即可:

In [146]: val.replace(',', '::')
Out[146]: 'a::b::  guido'

In [147]: val.replace(',', '')
Out[147]: 'ab  guido'

表7-3列出了Python內(nèi)置的字符串方法珍昨。

這些運(yùn)算大部分都能使用正則表達(dá)式實(shí)現(xiàn)(馬上就會(huì)看到)县耽。


Python內(nèi)置的字符串方法

2 Regular Expressions(正則表達(dá)式)

正則表達(dá)式提供了一種靈活的在文本中搜索或匹配(通常比前者復(fù)雜)字符串模式的方式。正則表達(dá)式能讓我們尋找更復(fù)雜的pattern镣典。通常稱一個(gè)表達(dá)式為regex兔毙,由正則表達(dá)語言來代表一個(gè)字符串模式⌒执海可以使用python內(nèi)建的re模塊來使用澎剥。

關(guān)于正則表達(dá)式,有很多教學(xué)資源赶舆,可以自己找?guī)灼獊韺W(xué)一些哑姚,這里不會(huì)介紹太多趾唱。

re模塊有以下三個(gè)類別:patther matching(模式匹配), substitution(替換), splitting(分割)。通常這三種都是相關(guān)的蜻懦,一個(gè)regex用來描述一種pattern甜癞,這樣會(huì)有很多種用法。這里舉個(gè)例子宛乃,假設(shè)我們想要根據(jù)空格(tabs悠咱,spaces,newlines)來分割一個(gè)字符串征炼。用于描述一個(gè)或多個(gè)空格的regex是\s+:

In [148]: import re

In [149]: text = "foo    bar\t baz  \tqux"

In [150]: re.split('\s+', text)
Out[150]: ['foo', 'bar', 'baz', 'qux']

如果想要得到符合regex的所有結(jié)果析既,以一個(gè)list結(jié)果返回,可以使用findall方法:

In [153]: regex.findall(text)
Out[153]: ['    ', '\t ', '  \t']

為了防止\在正則表達(dá)式中的逃逸谆奥,推薦使用raw string literal眼坏,比如r'C:\x',而不是使用'C:\x

如果打算對(duì)許多字符串應(yīng)用同一條正則表達(dá)式酸些,強(qiáng)烈建議通過re.compile創(chuàng)建regex對(duì)象宰译。這樣將可以節(jié)省大量的CPU時(shí)間。

match和search魄懂,與findall關(guān)系緊密沿侈。不過findall會(huì)返回所有匹配的結(jié)果,而search只會(huì)返回第一次匹配的結(jié)果市栗。更嚴(yán)格地說缀拭,match只匹配string開始的部分。這里舉個(gè)例子說明填帽,我們想要找到所有的郵件地址:

text = """Dave dave@google.com
Steve steve@gmail.com
Rob rob@gmail.com
Ryan ryan@yahoo.com
"""
pattern = r'[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}' 
#具有正則表達(dá)式的電子郵件[A-Z0-9 ._%+ - ] + @ [A-Z0-9 .-] {3,65} \蛛淋。[AZ] {2,4}

# re.IGNORECASE makes the regex case-insensitive
regex = re.compile(pattern, flags=re.IGNORECASE)

對(duì)text使用findall將得到一組電子郵件地址:

In [155]: regex.findall(text)
Out[155]: 
['dave@google.com',
 'steve@gmail.com',
 'rob@gmail.com',
 'ryan@yahoo.com']

search返回text中的第一個(gè)匹配結(jié)果。match object能告訴我們找到的結(jié)果在text中開始和結(jié)束的位置:

In [156]: m = regex.search(text)

In [157]: m
Out[157]: <_sre.SRE_Match object; span=(5, 20), match='dave@google.com'>

In [158]: text[m.start():m.end()]
Out[158]: 'dave@google.com'

regex.match返回None篡腌,因?yàn)樗粫?huì)在pattern存在于stirng開頭的情況下才會(huì)返回匹配結(jié)果:

In [159]: print(regex.match(text))
None

而sub返回一個(gè)新的string褐荷,把pattern出現(xiàn)的地方替換為我們指定的string:

In [160]: print(regex.sub('REDACTED', text))
Dave REDACTED
Steve REDACTED
Rob REDACTED
Ryan REDACTED

假設(shè)你想要找到郵件地址,同時(shí)哀蘑,想要把郵件地址分為三個(gè)部分诚卸,username, domain name, and domain suffix.(用戶名,域名绘迁,域名后綴)合溺。需要給每一個(gè)pattern加一個(gè)括號(hào):

In [161]: pattern = r'([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\.([A-Z]{2,4})'

In [162]: regex = re.compile(pattern, flags=re.IGNORECASE)

由這種修改過的正則表達(dá)式所產(chǎn)生的匹配項(xiàng)對(duì)象,可以通過其groups方法返回一個(gè)由模式各段組成的元組:

In [163]: m = regex.match('wesm@bright.net')

In [164]: m.groups()
Out[164]: ('wesm', 'bright', 'net')

對(duì)于帶有分組功能的模式缀台,findall會(huì)返回一個(gè)元組列表:

In [165]: regex.findall(text)
Out[165]:
[('dave', 'google', 'com'),
 ('steve', 'gmail', 'com'),
 ('rob', 'gmail', 'com'),
 ('ryan', 'yahoo', 'com')]

sub還能通過諸如\1棠赛、\2之類的特殊符號(hào)訪問各匹配項(xiàng)中的分組。符號(hào)\1對(duì)應(yīng)第一個(gè)匹配的組,\2對(duì)應(yīng)第二個(gè)匹配的組睛约,以此類推:

In [166]: print(regex.sub(r'Username: \1, Domain: \2, Suffix: \3', text))
Dave Username: dave, Domain: google, Suffix: com
Steve Username: steve, Domain: gmail, Suffix: com
Rob Username: rob, Domain: gmail, Suffix: com
Ryan Username: ryan, Domain: yahoo, Suffix: com
正則表達(dá)式

3 Vectorized String Functions in pandas(pandas中的字符串向量化函數(shù))

一些復(fù)雜的數(shù)據(jù)清理中鼎俘,string會(huì)有缺失值:

import numpy as np
import pandas as pd
In [167]: data = {'Dave': 'dave@google.com', 'Steve': 'steve@gmail.com',
   .....:         'Rob': 'rob@gmail.com', 'Wes': np.nan}

In [168]: data = pd.Series(data)

In [169]: data
Out[169]: 
Dave     dave@google.com
Rob        rob@gmail.com
Steve    steve@gmail.com
Wes                  NaN
dtype: object

In [170]: data.isnull()
Out[170]: 
Dave     False
Rob      False
Steve    False
Wes       True
dtype: bool

可以把一些字符串方法和正則表達(dá)式(用lambda或其他函數(shù))用于每一個(gè)value上,通過data.map辩涝,但是這樣會(huì)得到NA(null)值贸伐。為了解決這個(gè)問題,series有一些數(shù)組導(dǎo)向的方法可以用于字符串操作怔揩,來跳過NA值捉邢。這些方法可以通過series的str屬性;比如商膊,我們想檢查每個(gè)電子郵箱地址是否有'gmail' with str.contains:

In [171]: data.str.contains('gmail')
Out[171]: 
Dave     False
Rob       True
Steve     True
Wes        NaN
dtype: object

正則表達(dá)式也可以用伏伐,配合任意的re選項(xiàng),比如IGNORECASE:

In [172]: pattern
Out[172]: '([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\\.([A-Z]{2,4})'

In [173]: data.str.findall(pattern, flags=re.IGNORECASE)
Out[173]: 
Dave     [(dave, google, com)]
Rob        [(rob, gmail, com)]
Steve    [(steve, gmail, com)]
Wes                        NaN
dtype: object

有很多方法用于向量化晕拆。比如str.get或index索引到str屬性:

In [176]: matches.str.get(1) #數(shù)字代表括號(hào)里第幾個(gè),get第幾個(gè)字符
Out[176]: 
Dave    NaN
Rob     NaN
Steve   NaN
Wes     NaN
dtype: float64

In [177]: matches.str[0]
Out[177]: 
Dave    NaN
Rob     NaN
Steve   NaN
Wes     NaN
dtype: float64

你可以利用這種方法對(duì)字符串進(jìn)行截让牯帷:

In [178]: data.str[:5]
Out[178]: 
Dave     dave@
Rob      rob@g
Steve    steve
Wes        NaN
dtype: object
部分矢量化字符串方法
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市实幕,隨后出現(xiàn)的幾起案子吝镣,更是在濱河造成了極大的恐慌,老刑警劉巖茬缩,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赤惊,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡凰锡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門圈暗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來掂为,“玉大人,你說我怎么就攤上這事员串∮禄” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵寸齐,是天一觀的道長(zhǎng)欲诺。 經(jīng)常有香客問我,道長(zhǎng)渺鹦,這世上最難降的妖魔是什么扰法? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮毅厚,結(jié)果婚禮上塞颁,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好祠锣,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布酷窥。 她就那樣靜靜地躺著,像睡著了一般伴网。 火紅的嫁衣襯著肌膚如雪蓬推。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天澡腾,我揣著相機(jī)與錄音沸伏,去河邊找鬼。 笑死蛋铆,一個(gè)胖子當(dāng)著我的面吹牛馋评,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播刺啦,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼留特,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了玛瘸?” 一聲冷哼從身側(cè)響起蜕青,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎糊渊,沒想到半個(gè)月后右核,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡渺绒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年贺喝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宗兼。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡躏鱼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出殷绍,到底是詐尸還是另有隱情染苛,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布主到,位于F島的核電站茶行,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏登钥。R本人自食惡果不足惜畔师,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望怔鳖。 院中可真熱鬧茉唉,春花似錦固蛾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至懂傀,卻和暖如春趾诗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蹬蚁。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工恃泪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人犀斋。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓贝乎,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親叽粹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子览效,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容