Python / R语言 制作峰峦图

先看一下 matplotlib画峰峦图的效果

学习地址:https://matplotlib.org/matplotblog/posts/create-ridgeplots-in-matplotlib/
在这里插入图片描述

在这里插入图片描述

使用 JoyPy

JoyPy是一个基于 matplotlib+pandas 的单功能Python软件包,其目的仅在于:绘制Joyplots(又称脊线图)。

安装

1
pip install -i http://pypi.douban.com/simple --trusted-host pypi.douban.com joypy

看一下这个库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def joyplot(data,
            column=None,
            by=None,
            grid=False,
            xlabelsize=None, xrot=None, ylabelsize=None, yrot=None,
            ax=None, figsize=None,
            hist=False, bins=10,
            fade=False, ylim='max',
            fill=True, linecolor=None,
            overlap=1, background=None,
            labels=None, xlabels=True, ylabels=True,
            range_style='all',
            x_range=None,
            title=None,
            colormap=None,
            **kwds):
1
2
3
4
5
6
7
8
9
10
import joypy
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import cm


import seaborn as sns
iris = sns.load_dataset('iris')
fig, axes = joypy.joyplot(iris)#连续值的列为一个"脊"

在这里插入图片描述

在这里插入图片描述

1
2
fig, axes = joypy.joyplot(iris, by="species", legend=True)
#根据"Name"分组,每个Name是一行"脊",其中有多个,默认y轴一致

在这里插入图片描述

1
fig, axes = joypy.joyplot(iris, by="species", ylim='own', legend=True)#使用各自y值 但是这就不可比 建议使用:

在这里插入图片描述

1
fig, axes = joypy.joyplot(iris, by="species", overlap=3)

如果overlap=1,就等价于

1
fig, axes = joypy.joyplot(iris, by="species")#根据"species"分组,每个species是一行"脊",其中有多个,默认y轴一致

在这里插入图片描述


1
2
3
fig, axes = joypy.joyplot(iris, by="species", column="sepal_width",
                          hist=True, bins=20, overlap=0,
                          grid=False, legend=True)

在这里插入图片描述


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = ['Times New Roman']
colors = ['#791E94','#58C9B9','#519D9E','#D1B6E1']
fig,axs = joypy.joyplot(iris, by="species",
                         fill=True,
                         legend=True,
                         alpha=.8,
                         # range_style='own',
                         ylim='own',
                         xlabelsize=22,
                         ylabelsize=22,
                         grid='both',
                         linewidth=.8,
                         linecolor='k',
                         figsize=(10,6),
                         color=colors,
                       )
ax = plt.gca()
#设置x刻度
x = np.arange(6)
xlabel=['a','b','c','d','e','f']
ax.set_xlim(left=-.5,right=5.5)
ax.set_xticks(x)
plt.savefig(r'C:\Users\Administrator\Desktop\p1.png',
            width=7,height=5,dpi=900,bbox_inches='tight')

在这里插入图片描述

R语言-ggridges包 制作峰峦图

学习来源:https://wilkelab.org/ggridges/

安装

1
install.packages("ggridges")

加载包

1
2
3
4
5
6
library(tidyverse)
library(ggplot2)
library(ggridges)
library(viridisLite)# 安装  install.packages("viridisLite")
library(viridis)
library(RColorBrewer)

密度脊线图

使用 geom_density_ridges

1
ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges()

在这里插入图片描述
使用rel_min_height美学效果剪掉拖尾,或指定去掉尾部的范围,一般0.01会比较好

1
2
ggplot(iris, aes(x = Sepal.Length, y = Species)) +
  geom_density_ridges(rel_min_height = 0.01)

在这里插入图片描述
可以使用scale参数控制不同密度重叠的程度。设置为scale=1意味着最高的密度曲线仅触及下一个较高的密度曲线的基线。较小的值会在曲线之间产生间隔,较大的值会产生更多的重叠。

总而言之

scale参数控制的是密度图之间重叠的程度,值越小越分开,越大越重叠

1
2
3
# scale = 0.9, not quite touching
# 0.9 时最高点不会接触到上面的基线
ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(scale = 0.9)

在这里插入图片描述

1
2
3
# scale = 1, exactly touching
# 1 时会刚好接触
ggplot(iris, aes(x = Sepal.Length, y = Species)) + geom_density_ridges(scale = 1)

在这里插入图片描述
如果 scale 继续增大,则“峰峦”之间会相互重叠

其次,

The scaling is calculated separately per panel, so if we facet-wrap by species each density curve exactly touches the next higher baseline.

可以使用facet_wrap将不同类别分到不同的面板(坐标系)

1
2
ggplot(iris, aes(x = Sepal.Length, y = Species)) +
  geom_density_ridges(scale = 1) + facet_wrap(~Species)

在这里插入图片描述

沿x轴改变填充颜色

使用
geom_ridgeline_gradient
geom_density_ridges_gradient

不允许在填充中使用Alpha透明度

数据准备

使用lincoln_weather数据集(2016年在内布拉斯加州林肯的天气)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
head(lincoln_weather)


# A tibble: 6 x 24
  CST   `Max Temperatur~ `Mean Temperatu~ `Min Temperatur~ `Max Dew Point ~
  <chr>            <int>            <int>            <int>            <int>
1 2016~               37               24               11               19
2 2016~               41               23                5               22
3 2016~               37               23                8               23
4 2016~               30               17                4               24
5 2016~               38               29               19               29
6 2016~               34               33               32               33
# ... with 19 more variables: `Mean Dew Point [F]` <int>, `Min Dewpoint
#   [F]` <int>, `Max Humidity` <int>, `Mean Humidity` <int>, `Min
#   Humidity` <int>, `Max Sea Level Pressure [In]` <dbl>, `Mean Sea Level
#   Pressure [In]` <dbl>, `Min Sea Level Pressure [In]` <dbl>, `Max
#   Visibility [Miles]` <int>, `Mean Visibility [Miles]` <int>, `Min
#   Visibility [Miles]` <int>, `Max Wind Speed [MPH]` <int>, `Mean Wind
#   Speed[MPH]` <int>, `Max Gust Speed [MPH]` <int>, `Precipitation
#   [In]` <chr>, CloudCover <int>, Events <chr>, `WindDir [Degrees]` <int>,
#   Month <fct>

这个数据集是366x24的结构

在这里插入图片描述

在这里插入图片描述
查看数据集全貌

1
str(lincoln_weather)

结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
'data.frame':   366 obs. of  24 variables:
 $ CST                         : chr  "2016-1-1" "2016-1-2" "2016-1-3" "2016-1-4" ...
 $ Max.Temperature..F.         : int  37 41 37 30 38 34 33 28 22 31 ...
 $ Mean.Temperature..F.        : int  24 23 23 17 29 33 30 25 9 11 ...
 $ Min.Temperature..F.         : int  11 5 8 4 19 32 27 22 -4 -9 ...
 $ Max.Dew.Point..F.           : int  19 22 23 24 29 33 32 25 17 20 ...
 $ Mean.Dew.Point..F.          : int  13 14 15 13 25 32 30 22 4 5 ...
 $ Min.Dewpoint..F.            : int  8 4 8 2 19 29 25 18 -8 -13 ...
 $ Max.Humidity                : int  88 100 92 92 96 100 100 92 87 87 ...
 $ Mean.Humidity               : int  68 72 73 82 83 91 96 85 77 75 ...
 $ Min.Humidity                : int  47 44 54 72 70 82 92 78 67 63 ...
 $ Max.Sea.Level.Pressure..In. : num  30.5 30.4 30.5 30.5 30.2 ...
 $ Mean.Sea.Level.Pressure..In.: num  30.4 30.3 30.4 30.4 30.1 ...
 $ Min.Sea.Level.Pressure..In. : num  30.3 30.2 30.3 30.2 30 ...
 $ Max.Visibility..Miles.      : int  10 10 10 10 10 10 9 10 10 10 ...
 $ Mean.Visibility..Miles.     : int  10 10 10 9 8 4 3 6 9 10 ...
 $ Min.Visibility..Miles.      : int  10 10 10 6 5 0 0 2 5 10 ...
 $ Max.Wind.Speed..MPH.        : int  20 15 13 17 22 16 16 25 25 10 ...
 $ Mean.Wind.Speed.MPH.        : int  9 6 5 7 13 7 7 16 14 5 ...
 $ Max.Gust.Speed..MPH.        : int  23 18 14 23 28 21 21 32 28 12 ...
 $ Precipitation..In.          : chr  "0" "0" "0" "0" ...
 $ CloudCover                  : int  0 0 0 1 4 8 8 8 5 0 ...
 $ Events                      : chr  NA NA NA NA ...
 $ WindDir..Degrees.           : int  280 312 330 155 178 167 7 338 340 268 ...
 $ Month                       : Factor w/ 12 levels "December","November",..: 12 12 12 12 12 12 12 12 12 12 ...

大致了解数据集后,开始绘图

使用geom_density_ridges_gradient,参数如下

1
2
3
4
5
6
7
8
9
10
11
12
geom_density_ridges_gradient(
  mapping = NULL,
  data = NULL,
  stat = "density_ridges",
  position = "points_sina",
  panel_scaling = TRUE,
  na.rm = TRUE,
  gradient_lwd = 0.5,
  show.legend = NA,
  inherit.aes = TRUE,
  ...
)

绘图代码

注意:这里fill = stat(x)参数,为什么这么做

官网给出的解释是:

要绘制峰峦图,需要将计算出的x值(stat(x))映射到填充美学上,而不是使用原始变量 ,如fill= x

之所以如此,是因为geom_density_ridges_gradient调用的stat_density_ridges会计算新的x值作为其密度计算的一部分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
ggplot(lincoln_weather,
    # 美学映射 x轴 y轴 渐变
    aes(
        x=`Mean Temperature [F]`,
        y=`Month`,
        # 使用x进行渐变填充
        fill = stat(x)))+
 
    # 沿x轴绘制具有填充梯度的山脊线和山脊线图
        geom_density_ridges_gradient(
                        # 缩放因子,用于相对于脊线之间的间距缩放脊线的高度。值为1表示,假设基线之间的间距均匀,则任何脊线的最高点都将碰到上方的基线。
                        scale=3,
                        # 高度低于此临界值的线将被删除,即剪掉拖尾。
                        # 截断值是相对于整体最大值测量的,因此rel_min_height=0.01将删除所有1 \ 棱线。
                        # 默认值为0,因此不会删除任何内容。
                         rel_min_height=0.01,
                        # 梯度线宽
                        gradient_lwd =.6)+
    # 为两个轴删除不需要的填充
        scale_x_continuous(expand = c(0.01, 0))+
    # 通常必须设置“expand”选项
        scale_y_discrete(expand = c(0.01,0))+

    # 使用的X进行渐变填充
    # 改变 option 可以使用不同的颜色
        scale_fill_viridis(name="颜色棒名字", option = "D")+

        labs(
        title="标题",
        subtitle="副标题")+

    # 山脊线图的主题设置 (字体大小,网格线)
        theme_ridges(font_size = 13, grid = TRUE)
    +theme(axis.title.y = element_blank())# 去掉y轴标签

在这里插入图片描述

更改 option

1
scale_fill_viridis(name="颜色棒名字", option = "A")

在这里插入图片描述

1
scale_fill_viridis(name="颜色棒名字", option = "E")

在这里插入图片描述

分位数线和按分位数或概率着色

使用 stat_density_ridges(quantile_lines = TRUE) 可以在密度脊线图中画出下四分位数、中位数、上四分位数

1
2
ggplot(iris, aes(x = Sepal.Length, y = Species)) +
  stat_density_ridges(quantile_lines = TRUE)

在这里插入图片描述
或者

1
2
ggplot(iris, aes(x = Sepal.Length, y = Species)) +
  stat_density_ridges(quantile_lines = TRUE,scale = 0.9)

在这里插入图片描述
仅需修改

注:geom_density_ridges_gradient会调用stat_density_ridges

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ggplot(iris,
    # 美学映射 x轴 y轴 渐变
    aes(
        x=`Sepal.Length`,
        y=`Species`,
        # 使用x进行渐变填充
        fill = stat(x)))+
 
    # 沿x轴绘制具有填充梯度的山脊线和山脊线图
        geom_density_ridges_gradient(
                        # 缩放因子
                        scale=1,
                        # 高度低于此临界值的线将被删除。
                         rel_min_height=0.01,
                        # 梯度线宽
                        gradient_lwd =.6,
                        quantile_lines = TRUE)+
    scale_fill_viridis(name="Sepal.Length", option = "C")

在这里插入图片描述

指定分位数,如只要中位数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ggplot(iris,
    # 美学映射 x轴 y轴 渐变
    aes(
        x=`Sepal.Length`,
        y=`Species`,
        # 使用x进行渐变填充
        fill = stat(x)))+
 
    # 沿x轴绘制具有填充梯度的山脊线和山脊线图
        geom_density_ridges_gradient(
                        # 缩放因子
                        scale=1,
                        # 高度低于此临界值的线将被删除。
                         rel_min_height=0.01,
                        # 梯度线宽
                        gradient_lwd =.6,
                        quantile_lines = TRUE, quantiles = 2)+
    scale_fill_viridis(name="Sepal.Length", option = "D")

在这里插入图片描述

还可以通过切点而不是数字来指定分位数。例如,可以指出2.5%和97.5%的尾巴。

1
2
ggplot(iris, aes(x = Sepal.Length, y = Species)) +
  stat_density_ridges(quantile_lines = TRUE, quantiles = c(0.025, 0.975), alpha = 0.7)

在这里插入图片描述
通过分位数进行着色

1
2
3
4
5
6
ggplot(iris, aes(x=Sepal.Length, y=Species, fill = factor(stat(quantile)))) +
  stat_density_ridges(
    geom = "density_ridges_gradient", calc_ecdf = TRUE,
    quantiles = 4, quantile_lines = TRUE
  ) +
  scale_fill_viridis_d(name = "Quartiles",option = "E")

在这里插入图片描述
下四分位、上四分位

1
2
3
4
5
6
ggplot(iris, aes(x=Sepal.Length, y=Species, fill = factor(stat(quantile)))) +
  stat_density_ridges(
    geom = "density_ridges_gradient", calc_ecdf = TRUE,
    quantiles = c(0.25, 0.75), quantile_lines = TRUE
  ) +
  scale_fill_viridis_d(name = "Quartiles",option = "F")

在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
ggplot(iris, aes(x = Sepal.Length, y = Species, fill = factor(stat(quantile)))) +
  stat_density_ridges(
    geom = "density_ridges_gradient",
    calc_ecdf = TRUE,
    quantiles = c(0.025, 0.975)
  ) +
  scale_fill_manual(
    name = "Probability", values = c("#FF0000A0", "#A0A0A0A0", "#0000FFA0"),
    labels = c("(0, 0.025]", "(0.025, 0.975]", "(0.975, 1]")
  )

在这里插入图片描述
最后,当时calc_ecdf = TRUE,还可以访问计算出的美学stat(ecdf),该美学代表分布的经验累积密度函数。这使可以将概率直接映射到颜色上。

1
2
3
ggplot(iris, aes(x = Sepal.Length, y = Species, fill = 0.5 - abs(0.5 - stat(ecdf)))) +
  stat_density_ridges(geom = "density_ridges_gradient", calc_ecdf = TRUE) +
  scale_fill_viridis_c(name = "Tail probability", direction = -1,option = "C")

在这里插入图片描述

1
option = "A"

在这里插入图片描述

最后,关于颜色

参考:R语言的颜色应用

颜色数据集RColorBrewer

1
2
library(RColorBrewer)
brewer.pal.info

在这里插入图片描述

使用:

brewer.pal(n, name),n为要生成多少个颜色,name对应上面图片中颜色条的名字

比如:brewer.pal(11,'Spectral')

在这里插入图片描述

自定义渐变色板 colorRampPalette()

用法:
1.输入调色板的主要颜色,返回函数
2.在函数中输入想要获取的颜色数量,返回颜色数值

1
2
3
4
library(scales) #调用scales库中的show_col函数
mypalette<- colorRampPalette(c("#FF66CC" , "#FF6600"))
mycolors<- mypalette(12)#12种颜色,返回颜色数值
show_col(mycolors)

在这里插入图片描述

1
2
3
mypalette<- colorRampPalette(c("#FF66CC" , "#FF6600",'#66CCFF'))
mycolors<- mypalette(16)#16种颜色,返回颜色数值
show_col(mycolors)

在这里插入图片描述
在这里,可以使用scale_fill_gradientn来设置梯度颜色

1
2
3
4
5
6
7
8
ggplot(lincoln_weather, aes(x = `Mean Temperature [F]`, y = `Month`, fill = ..density..)) +
# ..density..  指按照计算出来的density填充颜色
geom_density_ridges_gradient(
                    scale =3,
                    rel_min_height = 0.00,#不剪尾
                    size = 0.3) +
# 填充梯度
scale_fill_gradientn(colours = colorRampPalette(rev(brewer.pal(5,'GnBu')))(24))#通过5中颜色,产生24种渐变颜色

在这里插入图片描述

1
2
3
4
5
6
7
8
ggplot(lincoln_weather, aes(x = `Mean Temperature [F]`, y = `Month`, fill = ..density..)) +
# ..density..  指按照计算出来的density填充颜色
geom_density_ridges_gradient(
                    scale =3,
                    rel_min_height = 0.00,#不剪尾
                    size = 0.3,quantile_lines = TRUE, quantiles = 2) +
# 填充梯度
scale_fill_gradientn(colours = colorRampPalette(rev(brewer.pal(5,'Greys')))(24))#通过5中颜色,产生24种渐变颜色

在这里插入图片描述

用fill = stat(x)是一样的
在这里插入图片描述
最后,参考大佬做的图

https://evvail.com/2020/05/26/557.html

ggarrange解决多图排版问题
R成精系列-一页多图

需要安装R包ggpubr(版本> = 0.1.3),以轻松创建基于ggplot2的发布就绪图。

关于安装,我使用的时候遇到安装麻烦,可以采取手动安装,手动安装包教程

下载包地址
https://cran.r-project.org/web/packages/available_packages_by_name.html


代码运行结果

在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#加载包
library(ggplot2)
library(ggridges)
library(tidyverse)
library(gridExtra)
library(ggpubr)

# 根据Sepal.Length绘制山峦图
p1 <- ggplot(iris) +
    geom_density_ridges_gradient(aes(x = `Sepal.Length`, y = `Species`, fill = ..x..), scale = 3,
                                 rel_min_height = 0.01,
                                 gradient_lwd = 0.1) +
    scale_x_continuous(expand = c(0.01, 0)) +
    scale_y_discrete(expand = c(0.01, 0)) +
    scale_fill_viridis(name = "Value", option = "C") +
    # 标题设置
    labs(title = "Sepal.Length") +
    # 主题设置
    theme_ridges(font_size = 13, grid = T) +
    theme(axis.title.y = element_blank())
p2 <- ggplot(iris) +
    geom_density_ridges_gradient(aes(x = `Sepal.Width`, y = `Species`, fill = ..x..), scale = 3,
                                 rel_min_height = 0.01,
                                 show.legend = T,
                                 gradient_lwd = 0.1) +
    scale_x_continuous(expand = c(0.01, 0)) +
    scale_y_discrete(expand = c(0.01, 0)) +
    scale_fill_viridis(name = "Value", option = "C") +
    labs(title = "Sepal.Width") +
    theme_ridges(font_size = 13, grid = FALSE) +
    theme(axis.title.y = element_blank())
p3 <- ggplot(iris) +
    geom_density_ridges_gradient(aes(x = `Petal.Length`, y = `Species`, fill = ..x..), scale = 3,
                                 rel_min_height = 0.01,
                                 gradient_lwd = 0.1) +
    scale_x_continuous(expand = c(0.01, 0)) +
    scale_y_discrete(expand = c(0.01, 0)) +
    scale_fill_viridis(name = "Value", option = "C") +
    labs(title = "Petal.Length") +
    theme_ridges(font_size = 13, grid = FALSE) +
    theme(axis.title.y = element_blank())
p4 <- ggplot(iris) +
    geom_density_ridges_gradient(aes(x = `Petal.Width`, y = `Species`, fill = ..x..), scale = 3,
                                 rel_min_height = 0.01,
                                 gradient_lwd = 0.1) +
    scale_x_continuous(expand = c(0.01, 0)) +
    scale_y_discrete(expand = c(0.01, 0)) +
    scale_fill_viridis(name = "Value", option = "C") +
    labs(title = "Petal.Width") +
    theme_ridges(font_size = 13, grid = FALSE) +
    theme(axis.title.y = element_blank())
# 组合各个图片
ggarrange(
    p1,
    p2,
    p3,
    p4,
    ncol = 2,
    nrow = 2,
    common.legend = TRUE, # legend共用
    legend = "right" # 右侧显示legend
)