學R記3:數據可視化-ggplot2

“The simple graph has brought more information to the data analyst’s mind than any other device.” — John Tukey

1.從ggplot開始

R has several systems for making graphs, but ggplot2 is one of the most elegant and most versatile.ggplot2 implements the grammar of graphics, a coherent system for describing and building graphs. With ggplot2, you can do more faster by learning one system and applying it in many places.

2.裝載tidyverse

tidyverse 包含了 ggplot2, readr, dplyr, tibble, purrr 等工具包,可以一站式完成數據讀寫、數據處理和數據可視化的任務。

# 安裝tidyverse包
install.packages('tidyverse')

# 加載tidyverse包
library(tidyverse)

You only need to install a package once, but you need to reload it every time you start a new session.

If we need to be explicit about where a function (or dataset) comes from, we’ll use the special form package::function(). For example, ggplot2::ggplot() tells you explicitly that we’re using the ggplot() function from the ggplot2 package.

案例式學習

在開始前,先介紹這部分用到的案例信息:
Do cars with big engines use more fuel than cars with small engines? You probably already have an answer, but try to make your answer precise. What does the relationship between engine size and fuel efficiency look like? Is it positive? Negative? Linear? Nonlinear?

3.初識數據

mpg是ggplot2包內置的數據集:

# 查看數據基本信息
glimpse(mpg)

# 查看數據格式
str(mpg)

# 查看數據集
View(mpg)

# 編輯數據
edit(mpg)
> glimpse(mpg)
Observations: 234
Variables: 11
$ manufacturer <chr> "audi", "audi", "audi", "audi", "audi", "audi", "audi", "audi", "audi", "audi", "audi", "audi", "au...
$ model        <chr> "a4", "a4", "a4", "a4", "a4", "a4", "a4", "a4 quattro", "a4 quattro", "a4 quattro", "a4 quattro", "...
$ displ        <dbl> 1.8, 1.8, 2.0, 2.0, 2.8, 2.8, 3.1, 1.8, 1.8, 2.0, 2.0, 2.8, 2.8, 3.1, 3.1, 2.8, 3.1, 4.2, 5.3, 5.3,...
$ year         <int> 1999, 1999, 2008, 2008, 1999, 1999, 2008, 1999, 1999, 2008, 2008, 1999, 1999, 2008, 2008, 1999, 200...
$ cyl          <int> 4, 4, 4, 4, 6, 6, 6, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, ...
$ trans        <chr> "auto(l5)", "manual(m5)", "manual(m6)", "auto(av)", "auto(l5)", "manual(m5)", "auto(av)", "manual(m...
$ drv          <chr> "f", "f", "f", "f", "f", "f", "f", "4", "4", "4", "4", "4", "4", "4", "4", "4", "4", "4", "r", "r",...
$ cty          <int> 18, 21, 20, 21, 16, 18, 18, 18, 16, 20, 19, 15, 17, 17, 15, 15, 17, 16, 14, 11, 14, 13, 12, 16, 15,...
$ hwy          <int> 29, 29, 31, 30, 26, 26, 27, 26, 25, 28, 27, 25, 25, 25, 25, 24, 25, 23, 20, 15, 20, 17, 17, 26, 23,...
$ fl           <chr> "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "r", "e",...
$ class        <chr> "compact", "compact", "compact", "compact", "compact", "compact", "compact", "compact", "compact", ...

其中,int 整型,dbl 雙精度,chr 字符型,

以上變量的含義:

  • manufacturer 制造商
  • model 車型
  • displ 排量
  • year 生產年份
  • cyl 氣缸數
  • trans 傳輸類型
  • drv 驅動形式
  • cty 每加侖城市里程
  • hwy 每加侖高速里程
  • fl 燃料類型
  • class 汽車品牌

4.初識ggplot2

Wilkinson(2005)提出語法規則→Wickham(2009)編寫ggplot2

Wilkinson在2005年提出一套用來描述所有統計圖形深層特性的語法規則:一張統計圖形就是從數據到幾何對象(geometric object,縮寫為geom,如點、線、條形等)的圖形屬性(aesthetic attributes,縮寫為aes,如顏色、形狀、大小等)的一個映射,此外,圖形中還可能包含數據的統計變換(statistical system,縮寫為stats),最后繪制在某個特定的坐標系(coordinate system,縮寫為coord)中,而分面(facet,指將繪圖窗口劃分為若干個子窗口)則可以用來生成數據不同子集的圖形(毛里里求斯)。

ggplot2包由Hadley Wickham(2009a)編寫,提供了一種基于Wilkinson(2005)所述圖形語法的圖形系統,Wickham(2009b)還對該語法進行了擴展。ggplot2包的目標是提供一個全面的、基于語法的、連貫一致的圖形生成系統,允許用戶創建新穎的、有創新性的數據可視化圖形。該方法的力量已經使得ggplot2成為使用R進行數據可視化的重要工具(攀董)。

ggplot2有以下特點(黃寶臣):

  • ggplot2的核心理念是將繪圖與數據分離,數據相關的繪圖與數據無關的繪圖分離
  • ggplot2是按圖層作圖
  • ggplot2保有命令式作圖的調整函數,使其更具靈活性
  • ggplot2將常見的統計變換融入到了繪圖中

以下是ggplot2圖層函數的示意圖:


image.png
image.png

5.練中學

基礎的命令:

ggplot(data = <DATA>) +
    <GEOM_FUNCTION>(mapping = aes(<MAPPINGS>))

<DATA> 數據
<GEOM_FUNCTION> 幾何對象,如point,line...
<MAPPINGS> 數據和圖形的映射關系

汽車排量和每加侖高速里程

# 汽車排量和每加侖高速里程
ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy))

image.png

The plot shows a negative relationship between engine size (displ) and fuel efficiency (hwy). In other words, cars with big engines use more fuel.

With ggplot2, you begin a plot with the function ggplot(). ggplot()creates a coordinate system that you can add layers to. The first argument of ggplot() is the dataset to use in the graph. So ggplot(data = mpg) creates an empty graph.

You complete your graph by adding one or more layers to ggplot(). The function geom_point() adds a layer of points to your plot, which creates a scatterplot. ggplot2 comes with many geom functions that each add a different type of layer to a plot.

Each geom function in ggplot2 takes a mapping argument. This defines how variables in your dataset are mapped to visual properties. The mapping argument is always paired with aes(), and the x and y arguments of aes() specify which variables to map to the x and y axes. ggplot2 looks for the mapped variables in the data argument, in this case, mpg.

接下來我們從<MAPPINGS>映射關系拓展開來~

“The greatest value of a picture is when it forces us to notice what we never expected to see.” — John Tukey

很容易看出剛剛繪制的圖形中有一些異常值,如何來分析呢?

image.png

5.1 圖形屬性映射

Let’s hypothesize that the cars are hybrids. One way to test this hypothesis is to look at the class value for each car.The class variable of the mpg dataset classifies cars into groups such as compact, midsize, and SUV. If the outlying points are hybrids, they should be classified as compact cars or, perhaps, subcompact cars (keep in mind that this data was collected before hybrid trucks and SUVs became popular).

You can add a third variable, like class, to a two dimensional scatterplot by mapping it to an aesthetic.An aesthetic is a visual property of the objects in your plot. Aesthetics include things like the size, the shape, or the color of your points.You can display a point (like the one below) in different ways by changing the values of its aesthetic properties. Since we already use the word “value” to describe data, let’s use the word “level” to describe aesthetic properties. Here we change the levels of a point’s size, shape, and color to make the point small, triangular, or blue:

image.png

按形狀

# 按形狀:汽車排量和每加侖高速里程
ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy,
      shape = class))
image.png

注意:The shape palette can deal with a maximum of 6 discrete values because more than 6 becomes difficult to discriminate; you have 7. Consider specifying shapes manually if you must have them.

按顏色
You can convey information about your data by mapping the aesthetics in your plot to the variables in your dataset. For example, you can map the colors of your points to the class variable to reveal the class of each car.

# 按顏色:汽車排量和每加侖高速里程
ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy,
      color = class))
image.png

To map an aesthetic to a variable, associate the name of the aesthetic to the name of the variable inside aes(). ggplot2 will automatically assign a unique level of the aesthetic (here a unique color) to each unique value of the variable, a process known as scaling.ggplot2 will also add a legend that explains which levels correspond to which values.

注意:如果在mapping外部設置color時,只是改變了所有點的顏色,并沒有做映射。

# 顏色:汽車排量和每加侖高速里程
ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy),
             color = "blue")
image.png

為什么會是兩座車?
The colors reveal that many of the unusual points are two-seater cars. These cars don’t seem like hybrids, and are, in fact, sports cars!Sports cars have large engines like SUVs and pickup trucks, but small bodies like midsize and compact cars, which improves their gas mileage. In hindsight, these cars were unlikely to be hybrids since they have large engines.

按大小

In the above example, we mapped class to the color aesthetic, but we could have mapped class to the size aesthetic in the same way. In this case, the exact size of each point would reveal its class affiliation. We get a warning here, because mapping an unordered variable (class) to an ordered aesthetic (size) is not a good idea.

# 按大小:汽車排量和每加侖高速里程
ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy,
      size = class))

Warning message:
Using size for a discrete variable is not advised. 
image.png

除了按顏色、形狀等分類外,我們還可以有如下的操作:

What does the stroke aesthetic do? What shapes does it work with? (Hint: use ?geom_point)

# 改變形狀的邊界厚度
# 數據集更換為mtcars
ggplot(mtcars, aes(wt, mpg)) +
  geom_point(shape = 21, color = "red", fill = "white",
             size = 5, stroke = 2)
image.png

What happens if you map an aesthetic to something other than a variable name, like aes(colour = displ < 5)? Note, you’ll also need to specify x and y.

# 分區間:汽車排量和每加侖高速里程
ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy, 
            color = displ < 5))
image.png

5.2分面

One way to add additional variables is with aesthetics. Another way, particularly useful for categorical variables, is to split your plot into facets, subplots that each display one subset of the data.

To facet your plot by a single variable, use facet_wrap(). The first argument of facet_wrap() should be a formula, which you create with ~ followed by a variable name (here “formula” is the name of a data structure in R, not a synonym for “equation”). The variable that you pass to facet_wrap() should be discrete.

# 分面:汽車排量和每加侖高速里程
ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy)) +
  facet_wrap(~class, nrow = 2)
image.png

To facet your plot on the combination of two variables, add facet_grid() to your plot call. The first argument of facet_grid() is also a formula. This time the formula should contain two variable names separated by a ~.

# 分面:汽車排量和每加侖高速里程
ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy)) +
  facet_grid(drv ~ cyl)
image.png

If you prefer to not facet in the rows or columns dimension, use a .instead of a variable name.

# 分面:汽車排量和每加侖高速里程
ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy)) +
  facet_grid(.~ cyl)
image.png
# 分面:汽車排量和每加侖高速里程
ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy)) +
  facet_grid(drv ~. )
image.png

分面有什么好處
What are the advantages to using faceting instead of the colour aesthetic? What are the disadvantages? How might the balance change if you had a larger dataset?

當變量較多的時候,圖形屬性顏色區分度不高,不能很好區分各個樣本點,而分面可以,但是分面后不同面上的點之間不好比較,所以變量少容易區分時可以用圖形屬性映射,多的時候顏色大小等不容易區分可以考慮分面(TidyFridy筆記本)。

單變量和雙變量的分面
Read ·?facet_wrap·. What does nrow do? What does ncol do? What other options control the layout of the individual panels? Why doesn’t ·facet_grid()· have nrow and ncol arguments?

nrow 和 ncol 控制分面子圖的排版,facet_grid() 對應 x 方向和 y 方向的分面圖個數是確定的,所有不用設置。

5.3幾何對象

A geom is the geometrical object that a plot uses to represent data. People often describe plots by the type of geom that the plot uses. For example, bar charts use bar geoms, line charts use line geoms, boxplots use boxplot geoms, and so on. Scatterplots break the trend; they use the point geom.

image.png

To change the geom in your plot, change the geom function that you add to ggplot(). For instance, to make the plots above, you can use this code:

# 幾何對象:汽車排量和每加侖高速里程
ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy))
image.png
# 幾何對象:汽車排量和每加侖高速里程
ggplot(data = mpg) +
  geom_smooth(mapping = aes(x = displ, y = hwy))
image.png

調整線段形式
Every geom function in ggplot2 takes a mapping argument. However, not every aesthetic works with every geom. You could set the shape of a point, but you couldn’t set the “shape” of a line. On the other hand, you could set the linetype of a line.geom_smooth()will draw a different line, with a different linetype, for each unique value of the variable that you map to linetype.

# 幾何對象:汽車排量和每加侖高速里程
ggplot(data = mpg) +
  geom_smooth(mapping = aes(x = displ, y = hwy, linetype = drv))
image.png

Here geom_smooth() separates the cars into three lines based on their drv value, which describes a car’s drivetrain. One line describes all of the points with a 4 value, one line describes all of the points with an f value, and one line describes all of the points with an r value. Here, 4 stands for four-wheel drive, f for front-wheel drive, and r for rear-wheel drive.

對比group和color
Many geoms, like geom_smooth(), use a single geometric object to display multiple rows of data. For these geoms, you can set the group aesthetic to a categorical variable to draw multiple objects. ggplot2 will draw a separate object for each unique value of the grouping variable. In practice, ggplot2 will automatically group the data for these geoms whenever you map an aesthetic to a discrete variable (as in the linetype example). It is convenient to rely on this feature because the group aesthetic by itself does not add a legend or distinguishing features to the geoms.

# 幾何對象:汽車排量和每加侖高速里程
p1 <- ggplot(data = mpg) +
  geom_smooth(mapping = aes(x = displ, y = hwy))

p2 <- ggplot(data = mpg) +
  geom_smooth(mapping = aes(x = displ, y = hwy, group = drv))

p3 <- ggplot(data = mpg) +
  geom_smooth(
    mapping = aes(x = displ, y = hwy, color = drv),
    show.legend = FALSE
  )
p <- cowplot::plot_grid(p1, p2, p3, nrow = 1, labels = LETTERS[1:3])

p
image.png

多幾何對象
To display multiple geoms in the same plot, add multiple geom functions to ggplot():

# 幾何對象:汽車排量和每加侖高速里程
ggplot(data = mpg) +
  geom_point(mapping = aes(x = displ, y = hwy)) +
  geom_smooth(mapping = aes(x = displ, y = hwy))
image.png

全局映射
This, however, introduces some duplication in our code. Imagine if you wanted to change the y-axis to display cty instead of hwy. You’d need to change the variable in two places, and you might forget to update one. You can avoid this type of repetition by passing a set of mappings to ggplot(). ggplot2 will treat these mappings as global mappings that apply to each geom in the graph. In other words, this code will produce the same plot as the previous code:

# 幾何對象:汽車排量和每加侖高速里程
ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
  geom_point() +
  geom_smooth()
image.png

If you place mappings in a geom function, ggplot2 will treat them as local mappings for the layer. It will use these mappings to extend or overwrite the global mappings for that layer only. This makes it possible to display different aesthetics in different layers.

# 幾何對象:汽車排量和每加侖高速里程
ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) + 
  geom_point(mapping = aes(color = class)) + 
  geom_smooth()
image.png

You can use the same idea to specify different data for each layer. Here, our smooth line displays just a subset of the mpg dataset, the subcompact cars. The local data argument in geom_smooth() overrides the global data argument in ggplot() for that layer only.

# 幾何對象:汽車排量和每加侖高速里程
ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) + 
  geom_point(mapping = aes(color = class)) + 
  geom_smooth(data = filter(mpg, class == "subcompact"), se = FALSE)
image.png

se 代表是否在圖形中顯示標準差
filter(mpg, class == "subcompact") 只選擇車型為subcompact的汽車

5.4統計變換

Next, let’s take a look at a bar chart. Bar charts seem simple, but they are interesting because they reveal something subtle about plots. Consider a basic bar chart, as drawn with geom_bar(). The following chart displays the total number of diamonds in the diamonds dataset, grouped by cut. The diamonds dataset comes in ggplot2 and contains information about ~54,000 diamonds, including the price, carat, color, clarity, and cut of each diamond. The chart shows that more diamonds are available with high quality cuts than with low quality cuts.

# 統計變換:鉆石
ggplot(data = diamonds) +
  geom_bar(mapping = aes(x = cut))
image.png

On the x-axis, the chart displays cut, a variable from diamonds. On the y-axis, it displays count, but count is not a variable in diamonds! Where does count come from? Many graphs, like scatterplots, plot the raw values of your dataset. Other graphs, like bar charts, calculate new values to plot:

  • bar charts, histograms, and frequency polygons bin your data and then plot bin counts, the number of points that fall in each bin.
  • smoothers fit a model to your data and then plot predictions from the model.
  • boxplots compute a robust summary of the distribution and then display a specially formatted box.

The algorithm used to calculate new values for a graph is called a stat, short for statistical transformation. The figure below describes how this process works with geom_bar().

image.png

默認屬性
You can learn which stat a geom uses by inspecting the default value for the stat argument. For example, ?geom_bar shows that the default value for stat is “count”, which means that geom_bar()uses stat_count(). stat_count() is documented on the same page as geom_bar(), and if you scroll down you can find a section called “Computed variables”. That describes how it computes two new variables: count and prop.

You can generally use geoms and stats interchangeably. For example, you can recreate the previous plot using stat_count() instead of geom_bar():

# 統計變換:鉆石
ggplot(data = diamonds) +
  stat_count(mapping = aes(x = cut))
image.png

This works because every geom has a default stat; and every stat has a default geom. This means that you can typically use geoms without worrying about the underlying statistical transformation. There are three reasons you might need to use a stat explicitly:

  1. You might want to override the default stat. In the code below, I change the stat of geom_bar() from count (the default) to identity. **This lets me map the height of the bars to the raw values of a y variable. **Unfortunately when people talk about bar charts casually, they might be referring to this type of bar chart, where the height of the bar is already present in the data, or the previous bar chart where the height of the bar is generated by counting rows.
# 統計變換:鉆石
demo <- tribble(
  ~cut,         ~freq,
  "Fair",       1610,
  "Good",       4906,
  "Very Good",  12082,
  "Premium",    13791,
  "Ideal",      21551
)

ggplot(data = demo) +
  geom_bar(mapping = aes(x = cut, y = freq), stat = "identity")
image.png
  1. You might want to override the default mapping from transformed variables to aesthetics. For example, you might want to display a bar chart of proportion, rather than count:
# 統計變換:鉆石
ggplot(data = diamonds) +
  geom_bar(mapping = aes(x = cut, y = stat(prop),group = 1))
image.png

注:group = 1 將所有的數據看作一組,如果不設置,所有的 bar 將是等高的

  1. You might want to draw greater attention to the statistical transformation in your code. For example, you might use stat_summary(), which summarises the y values for each unique x value, to draw attention to the summary that you’re computing:
# 統計變換:鉆石
ggplot(data = diamonds) + 
  stat_summary(
    mapping = aes(x = cut, y = depth),
    fun.ymin = min,
    fun.ymax = max,
    fun.y = median
  )
image.png

ggplot2 provides over 20 stats for you to use. Each stat is a function, so you can get help in the usual way, e.g. ?stat_bin. To see a complete list of stats, try the ggplot2 cheatsheet.

5.5位置調整

單變量:邊緣和填充
There’s one more piece of magic associated with bar charts. You can colour a bar chart using either the colour aesthetic, or, more usefully, fill:

# 位置調整:鉆石
ggplot(data = diamonds) + 
  geom_bar(mapping = aes(x = cut, colour = cut))
image.png

**多變量混合**
# 位置調整:鉆石
ggplot(data = diamonds) + 
  geom_bar(mapping = aes(x = cut, fill = cut))

image.png

Note what happens if you map the fill aesthetic to another variable, like clarity: the bars are automatically stacked. Each colored rectangle represents a combination of cut and clarity.

# 統計變換:鉆石
ggplot(data = diamonds) + 
  geom_bar(mapping = aes(x = cut, fill = clarity))
image.png

The stacking is performed automatically by the position adjustment specified by the position argument. If you don’t want a stacked bar chart, you can use one of three other options: "identity", "dodge" or "fill".

以上是自動調整,接下來以條形圖為例來看刊 ggplot 支持的幾種位置調整方式。

1. position = 'identity'
position = "identity" will place each object exactly where it falls in the context of the graph. This is not very useful for bars, because it overlaps them. To see that overlapping we either need to make the bars slightly transparent by setting alpha to a small value, or completely transparent by setting fill = NA.

# 統計變換:鉆石
ggplot(data = diamonds, mapping = aes(x = cut, fill = clarity)) + 
  geom_bar(alpha = 1/5, position = "identity")
image.png
# 統計變換:鉆石
ggplot(data = diamonds, mapping = aes(x = cut, color = clarity)) + 
  geom_bar(fill = NA, position = "identity")
image.png

The identity position adjustment is more useful for 2d geoms, like points, where it is the default.

2. position = "fill"
position = "fill" works like stacking, but makes each set of stacked bars the same height. This makes it easier to compare proportions across groups.

# 統計變換:鉆石
ggplot(data = diamonds) + 
  geom_bar(mapping = aes(x = cut, fill = clarity), position = "fill")
image.png

3. position = "dodge"
position = "dodge" places overlapping objects directly beside one another. This makes it easier to compare individual values.

# 統計變換:鉆石
ggplot(data = diamonds) + 
  geom_bar(mapping = aes(x = cut, fill = clarity), position = "dodge")
image.png

**There’s one other type of adjustment that’s not useful for bar charts, but it can be very useful for scatterplots. **Recall our first scatterplot. Did you notice that the plot displays only 126 points, even though there are 234 observations in the dataset?

原因是有些點被覆蓋了,可以用geom_point(position = 'jitter')來緩解

# 位置調整:汽車數據
p1 <- ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy), position = "jitter")
p2 <- ggplot(data = mpg) + 
  geom_point(mapping = aes(x = displ, y = hwy))
p <- cowplot::plot_grid(p1, p2, nrow = 1, labels = LETTERS[1:2])
p
image.png

5.6坐標系

Coordinate systems are probably the **most complicated part of ggplot2. **The default coordinate system is the Cartesian coordinate system where the x and y positions act independently to determine the location of each point. There are a number of other coordinate systems that are occasionally helpful.

參考資料:
R for Data Science
每天 5 分鐘,輕輕松松上手 R 語言(一)
如何使用 ggplot2 ?
R-可視化 | ggplot2框架與主要函數
ggplot2 專題分析

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,517評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,087評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,521評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,493評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,207評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,603評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,624評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,813評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,364評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,110評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,305評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,874評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,532評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,953評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,209評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,033評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,268評論 2 375

推薦閱讀更多精彩內容