• Python seaborn数据可视化


    使用seaborn进行数据可视化

    seaborn 简介

    Seaborn是一种基于matplotlib的图形可视化python libraty。它提供了一种高度交互式界面,便于用户能够做出各种有吸引力的统计图表。Seaborn其实是在matplotlib的基础上进行了更高级的API封装,从而使得作图更加容易,在大多数情况下使用seaborn就能做出很具有吸引力的图,而使用matplotlib就能制作具有更多特色的图。应该把Seaborn视为matplotlib的补充,而不是替代物。同时它能高度兼容numpy与pandas数据结构以及scipy与statsmodels等统计模式。掌握seaborn能很大程度帮助我们更高效的观察数据与图表,并且更加深入了解它们。

    其有如下特点:

    • 基于matplotlib aesthetics绘图风格,增加了一些绘图模式
    • 增加调色板功能,利用色彩丰富的图像揭示您数据中的模式
    • 运用数据子集绘制与比较单变量和双变量分布的功能
    • 运用聚类算法可视化矩阵数据
    • 灵活运用处理时间序列数据
    • 利用网格建立复杂图像集

    官方网站:http://seaborn.pydata.org

    在接下来的一段时间内,我们将带大家深入地了解各类seaborn绘图函数。

    使用散点图发现数据之间的关联

    散点图是数据可视化中最常用的图像。它直观地向我们展示了数据之间的分布关系,如果数据之间存在线性或者其他相关关系,很容易通过散点图观察出来。

    除了之前提到的plt.scatter(),使用seaborn也可以绘制散点图。对用的命令是scatterplot()

    There are several ways to draw a scatter plot in seaborn. The most basic, which should be used when both variables are numeric, is the scatterplot() function. In the categorical visualization tutorial, we will see specialized tools for using scatterplots to visualize categorical data. The scatterplot() is the default kind in relplot() (it can also be forced by setting kind="scatter"):

    In [1]:
    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt
    import seaborn as sns
    sns.set(style="darkgrid")
    
    </div>
    
    In [2]:
    tips = sns.load_dataset("tips")
    
    </div>
    
    In [3]:
    tips.head()
    
    Out[3]:
    total_bill tip sex smoker day time size
    0 16.99 1.01 Female No Sun Dinner 2
    1 10.34 1.66 Male No Sun Dinner 3
    2 21.01 3.50 Male No Sun Dinner 3
    3 23.68 3.31 Male No Sun Dinner 2
    4 24.59 3.61 Female No Sun Dinner 4
    </div>
    
    In [4]:
    plt.scatter(tips.total_bill,tips.tip)
    
    Out[4]:
    <matplotlib.collections.PathCollection at 0x7fbbcebdd240>
    </div>
    
    In [5]:
    sns.scatterplot(tips.total_bill,tips.tip)
    
    Out[5]:
    <matplotlib.axes._subplots.AxesSubplot at 0x7fbbcebc0978>
    </div>
    

    除此之外,我们还可以通过在 relplot()中指定kind="scatter"获取同样的效果。

    In [6]:
    sns.relplot(x="total_bill", y="tip", data=tips);
    
    </div>
    

    相比于scatterplotrelplot的集成度更高,在使用方法上也更为方便。例如,如果我们想给散点图加上第三个维度,就直接可以通过一个参数hue进行传递即可。该参数将会给不同的类别赋以不同的颜色。

    In [7]:
    sns.relplot(x="total_bill", y="tip", hue="smoker", data=tips);
    
    </div>
    

    To emphasize the difference between the classes, and to improve accessibility, you can use a different marker style for each class:

    除此之外,我们还可以继续修改散点的形状,只需要引入style参数

    In [8]:
    sns.relplot(x="total_bill", y="tip", hue="smoker", style="size",
                data=tips);
    
    </div>
    

    结合huestyle两个参数,我们就可以实现四维数据的绘图。需要注意的是,人眼的形状的敏感性不如颜色,因此该方法应该慎用,因为第四个维度不容易被观察到。

    In [9]:
    sns.relplot(x="total_bill", y="tip", hue="smoker", style="time", data=tips);
    
    </div>
    

    在上面的案例中,hue参数接受到的是离散的类别数据,而如果我们给它传入一个数值形式的数据,那么relplot将会用连续变化的色谱来区分该数据。

    In [10]:
    sns.relplot(x="total_bill", y="tip", hue="size", data=tips);
    
    </div>
    

    当然了,除了散点的颜色的形状,我们还可以修改散点的大小,只需要指定size参数即可。

    In [11]:
    sns.relplot(x="total_bill", y="tip", size="size", data=tips);
    
    </div>
    

    下面是一个综合使用以上参数,得到的一个五维的散点图。

    In [12]:
    sns.relplot(x="total_bill", y="tip", hue='sex',
                style = 'time',size="size", data=tips);
    
    </div>
    

    数据的汇总和不确定性的展示

    In [13]:
    fmri = sns.load_dataset("fmri")
    fmri
    
    Out[13]:
    subject timepoint event region signal
    0 s13 18 stim parietal -0.017552
    1 s5 14 stim parietal -0.080883
    2 s12 18 stim parietal -0.081033
    3 s11 18 stim parietal -0.046134
    4 s10 18 stim parietal -0.037970
    5 s9 18 stim parietal -0.103513
    6 s8 18 stim parietal -0.064408
    7 s7 18 stim parietal -0.060526
    8 s6 18 stim parietal -0.007029
    9 s5 18 stim parietal -0.040557
    10 s4 18 stim parietal -0.048812
    11 s3 18 stim parietal -0.047148
    12 s2 18 stim parietal -0.086623
    13 s1 18 stim parietal -0.046659
    14 s0 18 stim parietal -0.075570
    15 s13 17 stim parietal -0.008265
    16 s12 17 stim parietal -0.088512
    17 s7 9 stim parietal 0.058897
    18 s10 17 stim parietal -0.016847
    19 s9 17 stim parietal -0.121574
    20 s8 17 stim parietal -0.076287
    21 s7 17 stim parietal -0.043812
    22 s6 17 stim parietal -0.014746
    23 s5 17 stim parietal -0.056682
    24 s4 17 stim parietal -0.044582
    25 s3 17 stim parietal -0.053514
    26 s2 17 stim parietal -0.077292
    27 s1 17 stim parietal -0.038021
    28 s0 17 stim parietal -0.071300
    29 s13 16 stim parietal -0.002856
    ... ... ... ... ... ...
    1034 s5 13 cue frontal -0.014985
    1035 s4 13 cue frontal -0.021514
    1036 s3 13 cue frontal -0.047639
    1037 s2 13 cue frontal 0.047918
    1038 s1 13 cue frontal 0.028379
    1039 s0 13 cue frontal -0.021729
    1040 s13 12 cue frontal -0.020686
    1041 s12 12 cue frontal -0.003034
    1042 s11 12 cue frontal 0.055766
    1043 s10 12 cue frontal 0.005711
    1044 s9 12 cue frontal 0.024292
    1045 s7 12 cue frontal -0.014005
    1046 s2 7 cue frontal -0.078363
    1047 s10 10 cue frontal -0.016124
    1048 s8 10 cue frontal -0.015141
    1049 s10 8 cue frontal -0.052505
    1050 s9 8 cue frontal -0.008729
    1051 s8 8 cue frontal 0.007278
    1052 s7 8 cue frontal 0.015765
    1053 s6 8 cue frontal -0.063961
    1054 s5 8 cue frontal -0.028292
    1055 s4 8 cue frontal -0.160821
    1056 s3 8 cue frontal -0.033848
    1057 s2 8 cue frontal -0.069666
    1058 s1 8 cue frontal -0.136059
    1059 s0 8 cue frontal 0.018165
    1060 s13 7 cue frontal -0.029130
    1061 s12 7 cue frontal -0.004939
    1062 s11 7 cue frontal -0.025367
    1063 s0 0 cue parietal -0.006899

    1064 rows × 5 columns

    </div>
    
    In [14]:
    sns.relplot(x="timepoint", y="signal", kind="line", data=fmri);
    
    </div>
    

    此外,不确定度还可以用标准差来衡量,只需要设置ci='sd'即可

    In [15]:
    sns.relplot(x="timepoint", y="signal", kind="line", ci="sd", data=fmri);
    
    </div>
    

    如果我们关掉数据汇总,那么绘制出来的图像会非常奇怪。这是因为在某一个时间,会有多个测量数据。

    In [16]:
    sns.relplot(x="timepoint", y="signal", estimator=None, kind="line", data=fmri);
    
    </div>
    

    绘制子数据集

    scatterplot()一样,我们可以通过修改hue,style,size,来增加更多的绘图维度,用法也是非常一致的,这意味着我们可以非常简单地在两种方法之间进行替换。

    例如,如果我们引入hue参数,对event进行分类。即可得到如下的数据汇总图。

    In [17]:
    sns.relplot(x="timepoint", y="signal", hue="event", kind="line", data=fmri);
    
    </div>
    

    我们继续加入另一个style参数,可以把region也考虑进去。

    In [18]:
    sns.relplot(x="timepoint", y="signal", hue="region", style="event",
                kind="line", data=fmri);
    
    </div>
    

    为了突出显示,我们还可以修改线型。

    In [19]:
    sns.relplot(x="timepoint", y="signal", hue="region", style="event",
                dashes=False, markers=True, kind="line", data=fmri);
    
    </div>
    

    下面我们考虑另一个数据集

    In [20]:
    dots = sns.load_dataset("dots").query("align == 'dots'")
    dots.head()
    
    Out[20]:
    align choice time coherence firing_rate
    0 dots T1 -80 0.0 33.189967
    1 dots T1 -80 3.2 31.691726
    2 dots T1 -80 6.4 34.279840
    3 dots T1 -80 12.8 32.631874
    4 dots T1 -80 25.6 35.060487
    </div>
    

    上面的例子我们用的是离散变量作为参数huechioce的值,实际上,他们也可以使用连续变量。比如下面的例子

    In [21]:
    sns.relplot(x="time", y="firing_rate",
                hue="coherence", style="choice",
                kind="line", data=dots);
    
    </div>
    

    请注意,当style的可能性增多时,肉眼可能不太能够区分不同的线型,因此该方法需要在变量取值范围较小的情况下使用。

    In [22]:
    palette = sns.cubehelix_palette(light=.8, n_colors=6)
    sns.relplot(x="time", y="firing_rate",
               hue="coherence", size="choice",
               palette=palette,
               kind="line", data=dots);
    
    </div>
    

    使用多张图展示数据之间的相互关系

    下面我们学习如何使用多张子图,分析与展示数据之间的相关性。使用的参数主要是col

    In [23]:
    sns.relplot(x="total_bill", y="tip", hue="smoker",
                col="time", data=tips);
    
    </div>
    

    此外,我们还可以通过row参数,进一步地扩大子图的规模

    In [24]:
    sns.relplot(x="timepoint", y="signal", hue="subject",
                col="region", row="event", height=4,
                kind="line", estimator=None, data=fmri);
    
    </div>
    

    col_wrap参数可以帮我们把多张子图切分为规定列数的格式。

    In [25]:
    sns.relplot(x="timepoint", y="signal", hue="event", style="event",
                col="subject", col_wrap=3,
                height=3, aspect=.75, linewidth=2.5,
                kind="line", data=fmri.query("region == 'frontal'"));
    
    </div>
    

    以上的一系列可视化方法,称为小倍数绘图( “lattice” plots or “small-multiples”),在研究大规模数据集的时候尤为重要,因为使用该方法,可以把复杂的数据根据一定的规律展示出来,并且借助可视化,使人的肉眼可以识别这种规律。需要注意的是,有的时候,简单的图比复杂的图更能帮助我们发现和解决问题。

    绘制离散形式的变量

    在上一节中,我们学习了如何使用relplot()描述数据集中多变量之间的关系,其中我们主要关心的是两个数值型变量之间的关系。本节我们进一步地,讨论离散型( categorical)变量的绘制方法。

    在seaborn中,我们有很多可视化离散型随机变量的方法。类似于relplot()之于scatterplot()lineplot()的关系, 我们有一个catplot()方法,该方法提高了我们一个从更高层次调用各类函数的渠道,例如swarmplot(),boxplot(),violinplot()等。

    在详细地学习这些方法之前,对他们做一个系统地分类是非常有必要的,他们按照绘制内容可以分成如下三类:

    • Categorical scatterplots:

      • stripplot() (with kind="strip"; the default)
      • swarmplot() (with kind="swarm")
    • Categorical distribution plots:

      • boxplot() (with kind="box")
      • violinplot() (with kind="violin")
      • boxenplot() (with kind="boxen")
    • Categorical estimate plots:
      • pointplot() (with kind="point")
      • barplot() (with kind="bar")
      • countplot() (with kind="count")

    以上三个类别代表了绘图的不同角度,在实践中,我们要根据要解决的问题,合理地选择使用其中哪种方法。如果不知道哪种方法比较好,可以都尝试一遍,选择可视化效果更好的一个。

    在本教程中,我们主要使用catplot()函数进行绘图,如前所述,该函数是基于其他许多函数基础上一个更高层次的调用渠道。因此如果有针对其中任何一种方法的疑问,都可以在对应的方法的详细介绍中找到解释。

    首先,我们需要导入需要的库seabornmatplotlib.pyplot

    In [26]:
    import seaborn as sns
    import matplotlib.pyplot as plt
    sns.set(style="ticks", color_codes=True)
    
    </div>
    

    分类散点图

    The default representation of the data in catplot() uses a scatterplot. There are actually two different categorical scatter plots in seaborn. They take different approaches to resolving the main challenge in representing categorical data with a scatter plot, which is that all of the points belonging to one category would fall on the same position along the axis corresponding to the categorical variable. The approach used by stripplot(), which is the default “kind” in catplot() is to adjust the positions of points on the categorical axis with a small amount of random “jitter”:

    catplot()中的默认绘图方法是scatterplot,在catplot()中。如果我们只有一个类别,那么散点图绘制的时候,许多数据点会重叠(overlap)在一起,数据区分度和美观性都不强。为了解决这一问题,实际上有两类绘制散点图的方法。

    方法一: 我们可以考虑采用stripplot(),该方法通过给每一个数据点一个在x轴上的小扰动,使得数据点不会过分重叠。stripplot()catplot()的默认参数。

    In [27]:
    tips = sns.load_dataset("tips")
    tips.head()
    
    Out[27]:
    total_bill tip sex smoker day time size
    0 16.99 1.01 Female No Sun Dinner 2
    1 10.34 1.66 Male No Sun Dinner 3
    2 21.01 3.50 Male No Sun Dinner 3
    3 23.68 3.31 Male No Sun Dinner 2
    4 24.59 3.61 Female No Sun Dinner 4
    </div>
    
    In [28]:
    sns.catplot(x="day", y="total_bill", data=tips);
    
    </div>
    

    我们可以吧jitter参数关掉,观察一下效果。

    In [29]:
    sns.catplot(x="day", y="total_bill", jitter=0.2, data=tips);
    
    </div>
    

    方法二: 使用swarmplot(),该方法通过特定算法将数据点在横轴上分隔开,进一步提高区分度,防止重叠。该方法对于小数据集尤其适用,调用该方法只需要在catplot()中指定参数kind="swarm"即可。

    In [30]:
    sns.catplot(x="day", y="total_bill", kind="swarm", data=tips);
    
    </div>
    

    类似于relplot(),catplot()也可以通过添加颜色进一步增加绘图的维度,对应的参数为hue。需要注意的是,catplot()暂时不支持sytlesize参数。

    In [31]:
    sns.catplot(x="day", y="total_bill", hue="sex",kind="swarm", data=tips);
    
    </div>
    

    不像数值型的数据,有的时候,我们对于离散型的类别数据很难得到一个排序的标准。当然,seaborn会尽可能地按照合理的方法给他们排序,如果需要的话也可以人为指定排序。

    In [32]:
    sns.catplot(x="size", y="total_bill", kind="swarm",
                data=tips.query("size != 3"));
    
    </div>
    

    当我们想要指定绘制顺序的时候,可以使用order参数。

    In [33]:
    sns.catplot(x="smoker", y="tip", order=["Yes", "No"], data=tips);
    
    </div>
    

    有的时候,我们想要把散点图横着画,尤其是在类别比较多的时候,这时我们可以对调xy参数,达到该效果。

    In [34]:
    sns.catplot(x="day", y="total_bill", hue="time", kind="swarm", data=tips);
    
    </div>
    
    In [35]:
    # sns.catplot(x="sex", y="day", hue="time", kind="swarm", data=tips);
    
    </div>
    

    分类分布统计图

    如前所述,类别形式的散点图受限于数据集的大小,当数据量很大时,即使是使用swarmplot或者stripplot也无法使数据点分开。此时,我们考虑使用基于数据分布的绘图方法,而不再采用散点图。

    Boxplots

    基于分布的绘图方法中最简单的就是箱线图了,关于箱线图的理论已经在之前的讲义中进行了介绍,这里不再展开。箱线图的调用方法也很简单,直接kind = "box"就行。

    In [36]:
    sns.catplot(x="day", y="total_bill", kind="box", data=tips);
    
    </div>
    

    当然了,我们还可以加入hue参数,增加数据的绘图维度。

    In [37]:
    sns.catplot(x="day", y="total_bill", hue="smoker", kind="box", data=tips);
    
    </div>
    

    有的时候,我们想认为指定绘图的染色方法,此时可以借用dodge参数。

    In [38]:
    tips["weekend"] = tips["day"].isin(["Sat", "Sun"])
    #tips
    
    </div>
    
    In [39]:
    sns.catplot(x="day", y="total_bill", hue="weekend",
                kind="box", dodge=False, data=tips);
    
    </div>
    

    有一个和箱线图很像的图,叫boxenplot(),它会绘制一个跟箱线图很像的图片,但是展示了更多箱线图无法展示的信息,尤其适用于数据集比较大的情况。

    In [40]:
    diamonds = sns.load_dataset("diamonds")
    diamonds
    
    Out[40]:
    carat cut color clarity depth table price x y z
    0 0.23 Ideal E SI2 61.5 55.0 326 3.95 3.98 2.43
    1 0.21 Premium E SI1 59.8 61.0 326 3.89 3.84 2.31
    2 0.23 Good E VS1 56.9 65.0 327 4.05 4.07 2.31
    3 0.29 Premium I VS2 62.4 58.0 334 4.20 4.23 2.63
    4 0.31 Good J SI2 63.3 58.0 335 4.34 4.35 2.75
    5 0.24 Very Good J VVS2 62.8 57.0 336 3.94 3.96 2.48
    6 0.24 Very Good I VVS1 62.3 57.0 336 3.95 3.98 2.47
    7 0.26 Very Good H SI1 61.9 55.0 337 4.07 4.11 2.53
    8 0.22 Fair E VS2 65.1 61.0 337 3.87 3.78 2.49
    9 0.23 Very Good H VS1 59.4 61.0 338 4.00 4.05 2.39
    10 0.30 Good J SI1 64.0 55.0 339 4.25 4.28 2.73
    11 0.23 Ideal J VS1 62.8 56.0 340 3.93 3.90 2.46
    12 0.22 Premium F SI1 60.4 61.0 342 3.88 3.84 2.33
    13 0.31 Ideal J SI2 62.2 54.0 344 4.35 4.37 2.71
    14 0.20 Premium E SI2 60.2 62.0 345 3.79 3.75 2.27
    15 0.32 Premium E I1 60.9 58.0 345 4.38 4.42 2.68
    16 0.30 Ideal I SI2 62.0 54.0 348 4.31 4.34 2.68
    17 0.30 Good J SI1 63.4 54.0 351 4.23 4.29 2.70
    18 0.30 Good J SI1 63.8 56.0 351 4.23 4.26 2.71
    19 0.30 Very Good J SI1 62.7 59.0 351 4.21 4.27 2.66
    20 0.30 Good I SI2 63.3 56.0 351 4.26 4.30 2.71
    21 0.23 Very Good E VS2 63.8 55.0 352 3.85 3.92 2.48
    22 0.23 Very Good H VS1 61.0 57.0 353 3.94 3.96 2.41
    23 0.31 Very Good J SI1 59.4 62.0 353 4.39 4.43 2.62
    24 0.31 Very Good J SI1 58.1 62.0 353 4.44 4.47 2.59
    25 0.23 Very Good G VVS2 60.4 58.0 354 3.97 4.01 2.41
    26 0.24 Premium I VS1 62.5 57.0 355 3.97 3.94 2.47
    27 0.30 Very Good J VS2 62.2 57.0 357 4.28 4.30 2.67
    28 0.23 Very Good D VS2 60.5 61.0 357 3.96 3.97 2.40
    29 0.23 Very Good F VS1 60.9 57.0 357 3.96 3.99 2.42
    ... ... ... ... ... ... ... ... ... ... ...
    53910 0.70 Premium E SI1 60.5 58.0 2753 5.74 5.77 3.48
    53911 0.57 Premium E IF 59.8 60.0 2753 5.43 5.38 3.23
    53912 0.61 Premium F VVS1 61.8 59.0 2753 5.48 5.40 3.36
    53913 0.80 Good G VS2 64.2 58.0 2753 5.84 5.81 3.74
    53914 0.84 Good I VS1 63.7 59.0 2753 5.94 5.90 3.77
    53915 0.77 Ideal E SI2 62.1 56.0 2753 5.84 5.86 3.63
    53916 0.74 Good D SI1 63.1 59.0 2753 5.71 5.74 3.61
    53917 0.90 Very Good J SI1 63.2 60.0 2753 6.12 6.09 3.86
    53918 0.76 Premium I VS1 59.3 62.0 2753 5.93 5.85 3.49
    53919 0.76 Ideal I VVS1 62.2 55.0 2753 5.89 5.87 3.66
    53920 0.70 Very Good E VS2 62.4 60.0 2755 5.57 5.61 3.49
    53921 0.70 Very Good E VS2 62.8 60.0 2755 5.59 5.65 3.53
    53922 0.70 Very Good D VS1 63.1 59.0 2755 5.67 5.58 3.55
    53923 0.73 Ideal I VS2 61.3 56.0 2756 5.80 5.84 3.57
    53924 0.73 Ideal I VS2 61.6 55.0 2756 5.82 5.84 3.59
    53925 0.79 Ideal I SI1 61.6 56.0 2756 5.95 5.97 3.67
    53926 0.71 Ideal E SI1 61.9 56.0 2756 5.71 5.73 3.54
    53927 0.79 Good F SI1 58.1 59.0 2756 6.06 6.13 3.54
    53928 0.79 Premium E SI2 61.4 58.0 2756 6.03 5.96 3.68
    53929 0.71 Ideal G VS1 61.4 56.0 2756 5.76 5.73 3.53
    53930 0.71 Premium E SI1 60.5 55.0 2756 5.79 5.74 3.49
    53931 0.71 Premium F SI1 59.8 62.0 2756 5.74 5.73 3.43
    53932 0.70 Very Good E VS2 60.5 59.0 2757 5.71 5.76 3.47
    53933 0.70 Very Good E VS2 61.2 59.0 2757 5.69 5.72 3.49
    53934 0.72 Premium D SI1 62.7 59.0 2757 5.69 5.73 3.58
    53935 0.72 Ideal D SI1 60.8 57.0 2757 5.75 5.76 3.50
    53936 0.72 Good D SI1 63.1 55.0 2757 5.69 5.75 3.61
    53937 0.70 Very Good D SI1 62.8 60.0 2757 5.66 5.68 3.56
    53938 0.86 Premium H SI2 61.0 58.0 2757 6.15 6.12 3.74
    53939 0.75 Ideal D SI2 62.2 55.0 2757 5.83 5.87 3.64

    53940 rows × 10 columns

    </div>
    
    In [41]:
    diamonds = sns.load_dataset("diamonds")
    sns.catplot(x="color", y="price", kind="boxen",
                data=diamonds.sort_values("color"));
    
    </div>
    

    Violinplots

    接下来我们看小提琴图violinplot(),它结合了箱线图核密度估计的思想(该思想会在后面介绍)

    In [42]:
    sns.catplot(x="total_bill", y="day", hue="time",
                kind="violin", data=tips);
    
    </div>
    

    该方法用到了kernel density estimate (KDE),进而提供了更为丰富的数据分布信息。另一方面,由于KDE的引入,该方法也有更多的参数可以修改,例如bw.cut

    In [43]:
    sns.catplot(x="total_bill", y="day", hue="time",
                kind="violin", bw=0.5, cut=0,
                data=tips);
    
    </div>
    

    此外,如果数据的hue参数只有两个类别,一种非常高效的方法是给定split=True,仅绘制具有对称性的图像的一半。

    In [44]:
    f, ax = plt.subplots(figsize=(20, 5))
    a = sns.catplot(x="day", y="total_bill", hue="sex",
                kind="violin", split=True, data=tips,ax = ax);
    
    </div>
    

    当然了,小提琴图的内部绘制也可以修改,如果我们想要展示原始数据点而不是分位数等统计数据,我们可以指定inner="stick",那么所有的原始数据点会被绘制在图中。

    In [45]:
    sns.catplot(x="day", y="total_bill", hue="sex",
                kind="violin", inner="stick", split=True,
                palette="Set1", data=tips);
    
    </div>
    

    我们还可以把swarmplot()或者striplot()放置在boxplot或者violinplot中,从而实现总体与局部的整体展示。

    In [46]:
    g = sns.catplot(x="day", y="total_bill", kind="violin", inner=None, data=tips)
    sns.swarmplot(x="day", y="total_bill", color="k", size=3, data=tips, ax=g.ax);
    
    </div>
    
    In [47]:
    g = sns.catplot(x="day", y="total_bill", kind="box",data=tips)
    sns.swarmplot(x="day", y="total_bill", color="k", size=3, data=tips, ax=g.ax);
    
    </div>
    

    在分类数据中实现参数估计

    在有些情况下,我们不仅想要了解数据的分布,还想要了解数据的发展趋势。seaborn提供了两种方法,分别是barplotpointplots

    Bar plots

    我们最常用的分类数据的可视化方式是柱状图方式,在seaborn中,barplot()在总的数据集中选用某种估计方法进行参数的估计(默认是平均值)。 当每一个类别中有多个数据时,该方法还会使用bootstrapping绘制出均值的置信区间(通过errorbar的形式)

    In [48]:
    titanic = sns.load_dataset("titanic")
    titanic.head()
    
    Out[48]:
    survived pclass sex age sibsp parch fare embarked class who adult_male deck embark_town alive alone
    0 0 3 male 22.0 1 0 7.2500 S Third man True NaN Southampton no False
    1 1 1 female 38.0 1 0 71.2833 C First woman False C Cherbourg yes False
    2 1 3 female 26.0 0 0 7.9250 S Third woman False NaN Southampton yes True
    3 1 1 female 35.0 1 0 53.1000 S First woman False C Southampton yes False
    4 0 3 male 35.0 0 0 8.0500 S Third man True NaN Southampton no True
    </div>
    
    In [49]:
    titanic = sns.load_dataset("titanic")
    sns.catplot(x="sex", y="survived", hue="class", kind="bar", data=titanic);
    
    </div>
    

    有的时候我们只是想了解一下在不同类别中数据的个数,这时候我们只需要使用简单的countplot()即可。

    In [50]:
    sns.catplot(x="deck", kind="count", palette="rocket", data=titanic);
    
    </div>
    

    当然了,barplot()countplot()都可以传入其他控制参数,这里给了一个详细的示例说明。

    In [51]:
    sns.catplot(y="deck", hue="class", kind="count",
                palette="pastel", edgecolor=".6",
                data=titanic);
    
    </div>
    

    Point plots

    另一种展示数据分布趋势的方法是折线图。该折线图会包含的要素包括:均值、置信区间以及连接不同类别的连线,从而展示数据在不同类别间的变化趋势。

    In [52]:
    sns.catplot(x="sex", y="survived", hue="class", kind="point", data=titanic);
    
    </div>
    

    此外,如果我们需要增强图片对于黑白打印的支持,除了指定不同hue参数之外,我们还可以改变线条和标记的形状。

    In [53]:
    sns.catplot(x="class", y="survived", hue="sex",
                palette={"male": "g", "female": "m"},
                markers=["^", "o"], linestyles=["-", "--"],
                kind="point", data=titanic);
    
    </div>
    

    处理更多种类型的数据

    除了支持以上的传入格式外,这些函数也支持传入其他形式的数据,比如DataFrame 和 two-dimensional numpy arrays。

    In [54]:
    iris = sns.load_dataset("iris")
    iris.head()
    
    Out[54]:
    sepal_length sepal_width petal_length petal_width species
    0 5.1 3.5 1.4 0.2 setosa
    1 4.9 3.0 1.4 0.2 setosa
    2 4.7 3.2 1.3 0.2 setosa
    3 4.6 3.1 1.5 0.2 setosa
    4 5.0 3.6 1.4 0.2 setosa
    </div>
    
    In [55]:
    iris = sns.load_dataset("iris")
    sns.catplot(data=iris, orient="h", kind="box");
    
    </div>
    
    In [56]:
    sns.violinplot(x=iris.species, y=iris.sepal_length);
    
    </div>
    

    如果需要控制输出图片的形状和大小,可以通过figsize参数

    In [57]:
    f, ax = plt.subplots(figsize=(10, 5))
    sns.countplot(y="deck", data=titanic, color="m");
    
    </div>
    

    在多张图片中展示数据

    正如之前relplot()中提到的,如果我们希望绘制多张分类的图像,只需要通过设定rowcol参数即可。

    In [58]:
    sns.catplot(x="time", y="total_bill", hue="smoker",
                col="day", aspect=.6,
                kind="swarm", data=tips);
    
    </div>
    

    如果我们需要进一步修改图像的其他性质,我们可以返回一个对象。

    In [59]:
    g = sns.catplot(x="fare", y="survived", row="class",
                    kind="box", orient="h", height=1.5, aspect=4,
                    data=titanic.query("fare > 0"))
    g.set(xscale="log");
    
    </div>
    

    当我们遇到一个新的数据集的时候,往往我们首先要搞清楚的就是其中每一个变量的分布。本节我们将会给大家介绍seaborn中一些用于可视化数据分布的函数。

    首先我们导入numpy,pandas,seaborn,pyplotstats

    In [60]:
    import numpy as np
    import pandas as pd
    import seaborn as sns
    import matplotlib.pyplot as plt
    from scipy import stats
    
    </div>
    
    In [61]:
    sns.set(color_codes=True)
    
    </div>
    

    绘制单变量分布

    在seaborn中,绘制单变量分布的最简单的函数是displot(),该函数默认返回一张频率分布直方图以及其对应的核密度估计曲线(KDE)。

    In [62]:
    x = np.random.normal(size =100)
    sns.distplot(x);
    
    </div>
    

    频率分布直方图

    seaborn中的频率分布直方图displot()和matplotlib中的hist()非常相似。不过,seaborn给出了更为高层次的调用方法,我们可以通过参数kderug控制直方图中kde估计和数据点标记的展示与否。

    In [63]:
    sns.distplot(x, kde=True, rug=True);
    
    </div>
    

    当绘制直方图的时候,我们经常会调整的一个参数是直方的个数,控制直方个数的参数是bins,如果不认为指定bins的取值,seaborn会根据自己的算法得到一个较为合理的直方个数,但是通过人为调整直方个数,我们往往能发现新的规律。

    In [64]:
    sns.distplot(x, bins=5, kde=False, rug=True);
    
    </div>
    
    In [65]:
    sns.distplot(x, bins=25, kde=False, rug=True);
    
    </div>
    

    核密度估计Kernel density estimation

    核密度估计是一种分布的平滑(smooth)方法,所谓核密度估计,就是采用平滑的峰值函数(“核”)来拟合观察到的数据点,从而对真实的概率分布曲线进行模拟。

    In [66]:
    sns.distplot(x, hist=False, rug=True,kde = True);
    
    </div>
    

    那么,我们是符合得到这样一条曲线的呢? 实际上,我们将每一个数据点用一个以其为中心的高斯分布曲线代替,然后将这些高斯分布曲线叠加得到的。

    In [67]:
    x = np.random.normal(0, 1, size=30)   # 生成中心在0,scale为1,30维的正态分布数据 
    bandwidth = 1.06 * x.std() * x.size ** (-1 / 5.) # 确定带宽
    support = np.linspace(-4, 4, 200)  
    kernels = []
    for x_i in x:
        kernel = stats.norm(x_i, bandwidth).pdf(support)
        kernels.append(kernel)
        plt.plot(support, kernel, color="r")
    sns.rugplot(x, color=".2", linewidth=3);
    
    </div>
    

    将每一个数据转化为以其为中心的正态分布曲线以后,将其叠加,然后归一化,即可得到最终的KDE曲线。

    In [68]:
    from scipy.integrate import trapz
    density = np.sum(kernels, axis=0)
    density /= trapz(density, support) # 使用梯形积分计算曲线下面积,然后归一化
    plt.plot(support, density);
    
    </div>
    

    我们可以通过观察,发现,使用seaborn中的kdeplot()我们会得到同样的曲线,或者使用distplot(kde = True)也有同样的效果。

    In [69]:
    sns.kdeplot(x, shade=True);
    
    </div>
    

    除了核函数,另一个影响KDE的参数是带宽(h)。带宽反映了KDE曲线整体的平坦程度,也即观察到的数据点在KDE曲线形成过程中所占的比重 — 带宽越大,观察到的数据点在最终形成的曲线形状中所占比重越小,KDE整体曲线就越平坦;带宽越小,观察到的数据点在最终形成的曲线形状中所占比重越大,KDE整体曲线就越陡峭。

    In [70]:
    sns.kdeplot(x)
    sns.kdeplot(x, bw=.2, label="bw: 0.2")
    sns.kdeplot(x, bw=2, label="bw: 2")
    plt.legend();
    
    </div>
    

    通过观察以上的图像我们可以发现,由于高斯分布的引入,我们往往会扩大了变量的取值范围,我们可以通过cut参数控制最终图像距离最小值和最大值的距离。需要注意的是,cut参数仅仅是改变了图像的展示方法,对kde的计算过程没有影响。

    In [71]:
    sns.kdeplot(x, shade=True, cut=4)
    sns.rugplot(x);
    
    </div>
    

    参数分布的拟合

    我们也可以使用displot()拟合参数分布,并且将拟合结果与实际数据的分布做对比。

    In [72]:
    x = np.random.gamma(6, size=200)
    sns.distplot(x, kde=False, fit=stats.gamma); # 是用gamma分布拟合,并可视化
    
    </div>
    

    绘制两变量之间的联合分布

    有的时候,我们在数据分析的时候,也会关系两个变量之间的联合概率分布关系。seaborn中给我们提供了一个非常方便的jointplot()函数可以实现该功能。

    In [73]:
    mean = [0, 1]
    cov = [(1, .5), (.5, 1)]
    data = np.random.multivariate_normal(mean, cov, 200)
    df = pd.DataFrame(data, columns=["x", "y"])
    
    </div>
    
    In [74]:
    df.head()
    
    Out[74]:
    x y
    0 1.819591 1.557201
    1 -0.136995 0.814663
    2 -0.487868 1.262799
    3 -0.773655 -0.177352
    4 1.311222 1.988374
    </div>
    

    散点图

    我们最熟悉的绘制联合分布的方法莫过于散点图了。jointplot()会返回一张散点图(联合分布),并在上方和右侧展示两个变量各自的单变量分布。

    In [75]:
    sns.jointplot(x="x", y="y", data=df);
    
    </div>
    

    Hexbin plots

    与一维柱状图对应的二维图像称之为Hexbin plots,该图像帮助我们统计位于每一个六边形区域的数据的个数,然后用颜色加以表示,这种方法尤其对于大规模的数据更为适用。

    In [76]:
    x, y = np.random.multivariate_normal(mean, cov, 1000).T
    sns.jointplot(x=x, y=y, kind="hex", color="k");
    # with sns.axes_style("white"):
    #     sns.jointplot(x=x, y=y, kind="hex", color="k");
    
    </div>
    

    该方法尤其适用于白色风格

    In [77]:
    x, y = np.random.multivariate_normal(mean, cov, 1000).T
    with sns.axes_style("white"):
        sns.jointplot(x=x, y=y, kind="reg", color="k");
    
    </div>
    

    联合分布的核密度估计

    类似于一维情况,我们在二维平面一样可以进行核密度估计。通过设置kind = 'kde',我们就可以得到一个核密度估计的云图,以及两个单变量的核密度估计曲线。

    In [78]:
    sns.jointplot(x="x", y="y", data=df, kind="kde");
    
    </div>
    
    In [79]:
    with sns.axes_style("white"):
        sns.jointplot(x="x", y="y", data=df, kind="kde");
    
    </div>
    

    我们也可以直接使用kdeplot()绘制二维平面上的核密度估计。而且,结合面向对象的方法,我们还可以把新的绘图加入到已有的图片上。

    In [80]:
    f, ax = plt.subplots(figsize=(6, 6))
    sns.kdeplot(df.x, df.y, ax=ax)
    sns.rugplot(df.x, color="g", ax=ax)
    sns.rugplot(df.y, vertical=True, ax=ax);
    
    </div>
    

    If you wish to show the bivariate density more continuously, you can simply increase the number of contour levels:

    In [81]:
    f, ax = plt.subplots(figsize=(6, 6))
    cmap = sns.cubehelix_palette(as_cmap=True, dark=0, light=1, reverse=True)
    sns.kdeplot(df.x, df.y, cmap=cmap, n_levels=509, shade=True);
    
    </div>
    

    我们还可以给图片添加新的图层,将数据的散点图绘制在原图上,包括给图片添加坐标轴标签等等。

    In [82]:
    g = sns.jointplot(x="x", y="y", data=df, kind="kde", color="m")
    g.plot_joint(plt.scatter, c="w", s=30, linewidth=1, marker="+")
    g.ax_joint.collections[0].set_alpha(0.5)
    g.set_axis_labels("$X$", "$Y$");
    
    </div>
    

    分组可视化

    借助于上述的双变量分布绘图方法,我们可以绘制多变量两两之间的联合分布,seaborn中实现这个功能的函数为pairplot(),该函数会返回一个方形的绘图窗口,在该窗口中绘制两两变量之间的关系。在对角线上,pairplot()会展示单变量分布。

    In [83]:
    iris = sns.load_dataset("iris")
    sns.pairplot(iris);
    
    </div>
    
    In [84]:
    import matplotlib.pyplot as plt
    import numpy as np
    x = [1, 2, 3, 4, 5]
    y1 = [3, 1, 5, 9, 4]
    y4 = [4, 2, 1, 3, 9]
    barWidth = 0.2
    plt.bar(x, y1, barWidth, align= 'center', color = 'c', tick_label = ['label1','label2','labe3','label4','label5']) # c means greenish blue
    plt.bar(np.array(x)+barWidth, y4, barWidth, align= 'center', bottom = np.add(y1, y4), color = 'g', tick_label = ['label1','label2','labe3','label4','label5'])
    
    Out[84]:
    <BarContainer object of 5 artists>
    </div>
    

    可视化线性关系

    许多数据集都包含了众多变量,有的时候我们希望能够将其中的一个或者几个联系起来。上一节我们讲到了seaborn中很多绘制联合分布的方法,比如jointplot(),本节我们进一步地,讨论变量之间线性关系的可视化。

    需要注意的是,seaborn并不是一个统计学的库,seaborn想要实现的是:通过运用简单的统计工具,尽可能简单而直观地给我们呈现出数据之间相互关系。有的时候,对数据有一个直观的认识,能帮助我们更好地建立模型。

    In [85]:
    import numpy as np
    import seaborn as sns
    import matplotlib.pyplot as plt
    
    </div>
    
    In [86]:
    sns.set(color_codes=True)
    
    </div>
    
    In [87]:
    tips = sns.load_dataset("tips")
    
    </div>
    

    绘制线性回归的函数

    在seaborn中,有两个函数经常被用于实现线性回归,他们是lmplotregplot。接下来我们会介绍这两个函数的异同。

    在最简单的情况下,两个函数均会返回一个散点图,并给出y关于x的线性回归方程以及一个95%的置信区间。

    In [88]:
    sns.regplot(x="total_bill", y="tip", data=tips);
    
    </div>
    
    In [89]:
    sns.lmplot(x="total_bill", y="tip", data=tips);
    
    </div>
    

    我们发现,除了图的尺寸,这两个图的内容是完全一致的。

    那么,这两个函数有什么不同呢?

    • regplot()能接受更多种形式的数据,例如numpy arrays, pandas Series, references to variables in a pandas DataFrame,而 lmplot()只能接受references to variables in a pandas DataFrame,也就是只能接受“tidy” data
    • regplot() 仅仅指出 lmplot()的一部分参数

    我们可以对一个离散变量和一个连续变量绘制线性回归线,不过,可视化结果往往是不尽如人意的。

    In [90]:
    sns.lmplot(x="size", y="tip", data=tips);
    
    </div>
    

    针对上面这个图像,一个选择是给每一个离散的变量增加一个随机的扰动jitter,使得数据的分布更容易观察,请注意,jitter参数的存在仅仅是改变了可视化的效果,不会影响线性回归方程。

    In [91]:
    sns.lmplot(x="size", y="tip", data=tips, x_jitter=.1);
    
    </div>
    

    另一个选择是,我们直接将每一个离散类别中的所有数据统一处理,得到一个综合的趋势以及每个数据点对应的置信区间。

    In [92]:
    sns.lmplot(x="size", y="tip", data=tips, x_estimator=np.mean);
    
    </div>
    

    拟合其他形式的模型

    简单的线性拟合非常容易操作,也很容易理解。但是真实的数据往往不一定是线性相关的,因此我们需要考虑更多的拟合方法。

    我们这里使用的是 The Anscombe’s quartet dataset,在这个数据集中,不同形式的数据会得到同样的一个回归方程,但是拟合效果却是不同的。

    首先我们来看第一个

    In [93]:
    anscombe = sns.load_dataset("anscombe")
    
    </div>
    
    In [94]:
    sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'I'"),
               ci=None, scatter_kws={"s": 80});
    
    </div>
    

    我们接着来看第二个线性拟合,其拟合方程和第一个模型是一样的,但是显然其拟合效果并不好。

    In [95]:
    sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'II'"),
               ci=None, scatter_kws={"s": 80});
    
    </div>
    

    我们可以给lmplot()传入一个order参数,修改数据拟合的阶次,进而可以拟合非线性趋势。

    In [96]:
    sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'II'"),
               order=129, ci=None, scatter_kws={"s": 80});
    
    /opt/conda/lib/python3.6/site-packages/seaborn/regression.py:237: RankWarning: Polyfit may be poorly conditioned
      return np.polyval(np.polyfit(_x, _y, order), grid)
    
    </div>
    

    接着我们来看第三个例子,在这个案例中,我们引入了一个离群点,由于离群点的存在,其拟合方程显然偏离了主要趋势。

    In [97]:
    sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'III'"),
               ci=None, scatter_kws={"s": 80});
    
    </div>
    

    此时我们可以通过引入robust参数增强拟合的稳定性,该参数设置为True的时候,程序会自动忽略异常大的残差。

    In [98]:
    sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'III'"),
               robust=True, ci=None, scatter_kws={"s": 80});
    
    </div>
    

    当y参数传入了二分数据的时候,线性回归也会给出结果,但是该结果往往是不可信的。

    In [99]:
    tips["big_tip"] = (tips.tip / tips.total_bill) > .15
    sns.lmplot(x="total_bill", y="big_tip", data=tips,
               y_jitter=.03);
    
    </div>
    

    可以考虑采取的一个方法是引入逻辑回归,从而回归的结果可以用于估计在给定的x数据下,y=1的概率

    In [100]:
    sns.lmplot(x="total_bill", y="big_tip", data=tips,
               logistic=True, y_jitter=.03);
    
    </div>
    

    请注意,相比如简单的线性回归,逻辑回归以及robust regression 计算量较大,同时,置信区间的计算也会涉及到bootstrap,因此如果我们想要加快计算速度的话,可以把bootstrap关掉。

    其他拟合数据的方法包括非参数拟合中的局部加权回归散点平滑法(LOWESS)。LOWESS 主要思想是取一定比例的局部数据,在这部分子集中拟合多项式回归曲线,这样我们便可以观察到数据在局部展现出来的规律和趋势。

    In [101]:
    sns.lmplot(x="total_bill", y="tip", data=tips,
               lowess=True);
    
    </div>
    

    使用residplot(),我们可以检测简单的线性回顾是否能够比较好地拟合原数据集。 理想情况下,简单线性回归的残差应该随机地分布在y=0附近。

    In [102]:
    sns.residplot(x="x", y="y", data=anscombe.query("dataset == 'I'"),
                  scatter_kws={"s": 80});
    
    </div>
    

    如果出现了如下图所示的残差图,则说明线性回归的效果并不好。

    In [103]:
    sns.residplot(x="x", y="y", data=anscombe.query("dataset == 'II'"),
                  scatter_kws={"s": 80});
    
    </div>
    

    引入第三个参数

    我们知道,线性回归可以帮助我们描述两个变量之间的关系。不过,一个跟有趣的问题是:“这两个变量之间的关系是否跟第三个因素有关呢?”

    这时regplot()lmplot()就有区别了。regplot()只能展示两个变量之间的关系,而lmplot()则能进一步地引入第三个因素(categorical variables)。

    我们可以通过不同的颜色来区分不同的类别,在同一张图中绘制多个线性回归曲线:

    In [104]:
    sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips);
    
    </div>
    

    除了颜色之外,为了观察和打印方便,我们还可以引入不同的图形标记,区分不同的类别。

    In [105]:
    sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips,
               markers=["o", "x"], palette="Set1");
    
    </div>
    

    To add another variable, you can draw multiple “facets” which each level of the variable appearing in the rows or columns of the grid:

    如果我们想进一步地增加维度(变成四维绘图甚至五位),我们可以增加一个col参数。

    In [106]:
    sns.lmplot(x="total_bill", y="tip", hue="smoker", col="time", data=tips);
    
    </div>
    
    In [107]:
    sns.lmplot(x="total_bill", y="tip", hue="smoker",
               col="time", row="sex", data=tips);
    
    </div>
    

    调整绘图的尺寸和形状

    前面我们注意到了,regplotlmplot做出的图像基本类似,但是在图像的尺寸和形状上有所区别。

    这是因为,regplot的绘图,是图层层面的绘图,这意味着我们可以同时对多个图层进行操作,然后对每个图层进行精细化的格式设置。为了控制图片尺寸,我们必须先生成一个固定尺寸的对象。

    In [108]:
    f, ax = plt.subplots(figsize=(5, 6))
    sns.regplot(x="total_bill", y="tip", data=tips, ax=ax);
    
    </div>
    

    regplot不同的是,lmplot是一个集成化的命令,如果我们想要修改图片的尺寸和大小,只能通过传入参数的格式进行实现,sizeaspect分别用来控制尺寸和长宽比。

    In [109]:
    sns.lmplot(x="total_bill", y="tip", col="day", data=tips,
               col_wrap=2, height=3);
    
    </div>
    
    In [110]:
    sns.lmplot(x="total_bill", y="tip", col="day", data=tips,
               aspect=.5);
    
    </div>
    

    在其他绘图中加入线性回归

    其他的一些seaborn函数也在更高的层面上支持了线性回归的加入。例如,在我们之前讲过的jointplot里面,我们通过给出kind = 'reg'参数,就可以绘制出数据的线性回归。

    In [111]:
    sns.jointplot(x="total_bill", y="tip", data=tips, kind="reg");
    
    </div>
    

    同样的,pairplot也是支持线性回归的加入的。

    In [112]:
    sns.pairplot(tips, x_vars=["total_bill", "size"], y_vars=["tip"],
                 height=5, aspect=.8, kind="reg");
    
    </div>
    

    进一步地,我们可以通过在pairplot中引入huekind = 'reg',研究更高维度数据的线性线性关系。

    In [113]:
    sns.pairplot(tips, x_vars=["total_bill", "size"], y_vars=["tip"],
                 hue="smoker", height=5, aspect=.8, kind="reg");
    
    </div>
    

    当我我们探索中等维度的数据集的时候,将数据全部展示在一张图片中已经不太现实,但是通过网格化绘图的方法,我们仍可以方便地观察数据的分布规律。前面我们已经在matplotlib中提到了网格化绘图方法subplot,seaborn在matplotlib的基础上,进一步集成了网格化绘图的方法,使得我们能够更加方便地可视化中等维度的数据。

    不过需要指出的是,为了使用seaborn的网格化绘图方法,我们的原始数据必须是Pandas DataFrame格式的。同时,该数据也必须是“tidy” data,换句话说,该数据的构成必须是每列代表一个特征,每行代表一个样本。不符合标准的数据是无法使用seaborn 的网格化绘图方法的。

    In [114]:
    import seaborn as sns
    import matplotlib.pyplot as plt
    
    </div>
    
    In [115]:
    sns.set(style="ticks")
    
    </div>
    

    FacetGrid 网格化绘图方法

    FacetGrid方法至多可以帮助我们绘制四个维度的数据,其运用的三个参数主要是hue,colrow.其中hue代表颜色,col代表行,row代表列。

    FacetGrid方法首先根据我们给定的hue,colrow参数,初始化一个绘图网格,需要注意的是,这三个参数需要取为离散型(或者类别型)的数据,否则,绘制的图片会非常过,不美观也不利于观察。

    其实,之前的relplot(), catplot(), 以及 lmplot()内部都使用了这里介绍的网格化绘图方法,因此借用本节课学到的知识,也可以对这三个函数的运行结果进行修改。

    In [116]:
    tips = sns.load_dataset("tips")
    
    </div>
    

    Initializing the grid like this sets up the matplotlib figure and axes, but doesn’t draw anything on them.

    这里的sns.FacetGrid()的作用是初始化绘图网格,和之前提到过的plt.figure()一样,他们只初始化,而不进行绘图。

    如果我们想要绘图,需要使用FacetGrid.map()方法,该方法中,我们需要提供的参数包括:

    • 绘图变量名
    • 绘图函数

    例如,我们想绘制午饭和晚饭的小费分布,就可以执行如下的命令:

    In [117]:
    g = sns.FacetGrid(tips, col="time")
    g.map(plt.hist, "tip");
    
    </div>
    

    我们可以看到,FacetGrid方法会自动给我们的坐标轴加上注释,同时加上标题。这大大节省了我们调整格式的时间。

    再比如,我们想研究性别、是否吸烟、总花费与小费数的关系。就可以使用如下的代码。从中我们可以看到,关键词参数alpha = 0.7也可以直接作用FacetGrid.map()的输入,该参数也会传递给绘图函数,在这个问题中,绘图函数就是plt.scatter

    In [118]:
    g = sns.FacetGrid(tips, col="sex", hue="smoker")
    g.map(plt.scatter, "total_bill", "tip", alpha=.7)
    g.add_legend();
    
    </div>
    

    当我们绘制FacetGrid的时候,还有一些其他的参数可以调节,比如margin_titles

    In [119]:
    g = sns.FacetGrid(tips, row="smoker", col="time", margin_titles=True)
    g.map(sns.regplot, "size", "total_bill", color=".3", fit_reg=False, x_jitter=.1);
    
    </div>
    
    <div class="alert alert-block alert-danger"> <b>注意:</b> margin_titles目前还没有在matplotlib中有正式的api支持,因此可能会在某些情况下报错。报错的话,就关掉这个选项即可。 </div>

    当然了,我们也是能够调整图片的尺寸的,主要用的两个参数是heightaspect

    In [120]:
    g = sns.FacetGrid(tips, col="day", height=4, aspect=.5)
    g.map(sns.barplot, "sex", "total_bill");
    
    /opt/conda/lib/python3.6/site-packages/seaborn/axisgrid.py:715: UserWarning: Using the barplot function without specifying `order` is likely to produce an incorrect plot.
      warnings.warn(warning)
    
    </div>
    

    FacetGrid中的默认绘图顺序是根据Dataframe中的信息来的。通常地,绘图会根据类别出现的先后次序绘图。当然,在需要的时候,我们可以人为给定绘图的顺序。

    In [121]:
    ordered_days = ['Sat', 'Sun', 'Thur', 'Fri']
    g = sns.FacetGrid(tips, row="day", row_order=ordered_days,
                      height=1.7, aspect=4,)
    g.map(sns.distplot, "total_bill", hist=True, rug=True);
    
    </div>
    

    不仅rowcol顺序可调,hue参数对应的颜色我们也可以自行制定,例如下面的例子:

    In [122]:
    pal = dict(Lunch="seagreen", Dinner="gray")
    g = sns.FacetGrid(tips, hue="time", palette=pal, height=5)
    g.map(plt.scatter, "total_bill", "tip", s=50, alpha=.7, linewidth=.5, edgecolor="white")
    g.add_legend();
    
    </div>
    

    之前我们提到过,如果仅仅依靠颜色区别类别,在黑白打印的情况下,读者就不能提取信息了,因此我们需要引入形状参数,如果说在FacetGrid中想要引入形状参数,可以用如下的方法。

    In [123]:
    g = sns.FacetGrid(tips, hue="sex", palette="Set1", height=5, hue_kws={"marker": ["o", "v"]})
    g.map(plt.scatter, "total_bill", "tip", s=100, linewidth=.5, edgecolor="white")
    g.add_legend();
    
    </div>
    

    有些情况下,尽管我们给row或者col指定的是一个类别型的数据。由于类别数过大,图像还是比较拥挤。

    In [124]:
    attend = sns.load_dataset("attention").query("subject <= 12")
    g = sns.FacetGrid(attend, col="subject", height=2, ylim=(0, 10))
    g.map(sns.pointplot, "solutions", "score", color=".3", ci=None);
    
    /opt/conda/lib/python3.6/site-packages/seaborn/axisgrid.py:715: UserWarning: Using the pointplot function without specifying `order` is likely to produce an incorrect plot.
      warnings.warn(warning)
    
    </div>
    

    此时,我们只需要引入一个col_wrap参数就可以解决这个问题。请注意,如果用了col_wrap参数,就不能用row参数了。

    In [125]:
    attend = sns.load_dataset("attention").query("subject <= 12")
    g = sns.FacetGrid(attend, col="subject", col_wrap=4,height=2, ylim=(0, 10))
    g.map(sns.pointplot, "solutions", "score", color=".3", ci=None);
    
    /opt/conda/lib/python3.6/site-packages/seaborn/axisgrid.py:715: UserWarning: Using the pointplot function without specifying `order` is likely to produce an incorrect plot.
      warnings.warn(warning)
    
    </div>
    

    当然我们还可以对绘图以后的格式进行调整,使用的方法是FacetGrid.set()方法

    In [126]:
    with sns.axes_style("white"):
        g = sns.FacetGrid(tips, row="sex", col="smoker", margin_titles=True, height=2.5)
    g.map(plt.scatter, "total_bill", "tip", color="#334488", edgecolor="white", lw=.5);
    g.set_axis_labels("Total bill (US Dollars)", "Tip");
    g.set(xticks=[10, 30, 50], yticks=[2, 6, 10]);
    g.fig.subplots_adjust(wspace=.02, hspace=.02);
    
    </div>
    

    For even more customization, you can work directly with the underling matplotlib Figure and Axes objects, which are stored as member attributes at fig and axes (a two-dimensional array), respectively. When making a figure without row or column faceting, you can also use the ax attribute to directly access the single axes.

    In [127]:
    g = sns.FacetGrid(tips, col="smoker", margin_titles=True, height=4)
    g.map(plt.scatter, "total_bill", "tip", color="#338844", edgecolor="white", s=50, lw=1)
    for ax in g.axes.flat:
        ax.plot((0, 50), (0, .2 * 50), c=".2", ls="--")
    g.set(xlim=(0, 60), ylim=(0, 14));
    
    </div>
    

    Using custom functions

    You’re not limited to existing matplotlib and seaborn functions when using FacetGrid. However, to work properly, any function you use must follow a few rules:

    It must plot onto the “currently active” matplotlib Axes. This will be true of functions in the matplotlib.pyplot namespace, and you can call plt.gca to get a reference to the current Axes if you want to work directly with its methods. It must accept the data that it plots in positional arguments. Internally, FacetGrid will pass a Series of data for each of the named positional arguments passed to FacetGrid.map(). It must be able to accept color and label keyword arguments, and, ideally, it will do something useful with them. In most cases, it’s easiest to catch a generic dictionary of **kwargs and pass it along to the underlying plotting function. Let’s look at minimal example of a function you can plot with. This function will just take a single vector of data for each facet:

    In [128]:
    from scipy import stats
    def quantile_plot(x, **kwargs):
        qntls, xr = stats.probplot(x, fit=False)
        plt.scatter(xr, qntls, **kwargs)
    

    g = sns.FacetGrid(tips, col="sex", height=4)
    g.map(quantile_plot, "total_bill");

    </div>
    

    If we want to make a bivariate plot, you should write the function so that it accepts the x-axis variable first and the y-axis variable second:

    In [129]:
    def qqplot(x, y, **kwargs):
        _, xr = stats.probplot(x, fit=False)
        _, yr = stats.probplot(y, fit=False)
        plt.scatter(xr, yr, **kwargs)
    

    g = sns.FacetGrid(tips, col="smoker", height=4)
    g.map(qqplot, "total_bill", "tip");

    </div>
    

    Because plt.scatter accepts color and label keyword arguments and does the right thing with them, we can add a hue facet without any difficulty:

    In [130]:
    g = sns.FacetGrid(tips, hue="time", col="sex", height=4)
    g.map(qqplot, "total_bill", "tip")
    g.add_legend();
    
    </div>
    

    This approach also lets us use additional aesthetics to distinguish the levels of the hue variable, along with keyword arguments that won’t be dependent on the faceting variables:

    In [131]:
    g = sns.FacetGrid(tips, hue="time", col="sex", height=4,
                      hue_kws={"marker": ["s", "D"]})
    g.map(qqplot, "total_bill", "tip", s=40, edgecolor="w")
    g.add_legend();
    
    </div>
    

    Sometimes, though, you’ll want to map a function that doesn’t work the way you expect with the color and label keyword arguments. In this case, you’ll want to explicitly catch them and handle them in the logic of your custom function. For example, this approach will allow use to map plt.hexbin, which otherwise does not play well with the FacetGrid API:

    In [132]:
    def hexbin(x, y, color, **kwargs):
        cmap = sns.light_palette(color, as_cmap=True)
        plt.hexbin(x, y, gridsize=15, cmap=cmap, **kwargs)
    

    with sns.axes_style("dark"):
    g = sns.FacetGrid(tips, hue="time", col="time", height=4)
    g.map(hexbin, "total_bill", "tip", extent=[0, 50, 0, 10]);

    </div>
    

    Plotting pairwise data relationships

    PairGrid also allows you to quickly draw a grid of small subplots using the same plot type to visualize data in each. In a PairGrid, each row and column is assigned to a different variable, so the resulting plot shows each pairwise relationship in the dataset. This style of plot is sometimes called a “scatterplot matrix”, as this is the most common way to show each relationship, but PairGrid is not limited to scatterplots.

    It’s important to understand the differences between a FacetGrid and a PairGrid. In the former, each facet shows the same relationship conditioned on different levels of other variables. In the latter, each plot shows a different relationship (although the upper and lower triangles will have mirrored plots). Using PairGrid can give you a very quick, very high-level summary of interesting relationships in your dataset.

    The basic usage of the class is very similar to FacetGrid. First you initialize the grid, then you pass plotting function to a map method and it will be called on each subplot. There is also a companion function, pairplot() that trades off some flexibility for faster plotting.

    In [133]:
    iris = sns.load_dataset("iris")
    g = sns.PairGrid(iris)
    g.map(plt.scatter);
    
    </div>
    

    It’s possible to plot a different function on the diagonal to show the univariate distribution of the variable in each column. Note that the axis ticks won’t correspond to the count or density axis of this plot, though.

    In [134]:
    g = sns.PairGrid(iris)
    g.map_diag(plt.hist)
    g.map_offdiag(plt.scatter);
    
    </div>
    

    A very common way to use this plot colors the observations by a separate categorical variable. For example, the iris dataset has four measurements for each of three different species of iris flowers so you can see how they differ.

    In [135]:
    g = sns.PairGrid(iris, hue="species")
    g.map_diag(plt.hist)
    g.map_offdiag(plt.scatter)
    g.add_legend();
    
    </div>
    

    By default every numeric column in the dataset is used, but you can focus on particular relationships if you want.

    In [136]:
    g = sns.PairGrid(iris, vars=["sepal_length", "sepal_width"], hue="species")
    g.map(plt.scatter);
    
    </div>
    

    It’s also possible to use a different function in the upper and lower triangles to emphasize different aspects of the relationship.

    In [137]:
    g = sns.PairGrid(iris)
    g.map_upper(plt.scatter)
    g.map_lower(sns.kdeplot)
    g.map_diag(sns.kdeplot, lw=3, legend=False);
    
    </div>
    

    The square grid with identity relationships on the diagonal is actually just a special case, and you can plot with different variables in the rows and columns.

    In [138]:
    g = sns.PairGrid(tips, y_vars=["tip"], x_vars=["total_bill", "size"], height=4)
    g.map(sns.regplot, color=".3")
    g.set(ylim=(-1, 11), yticks=[0, 5, 10]);
    
    </div>
    

    Of course, the aesthetic attributes are configurable. For instance, you can use a different palette (say, to show an ordering of the hue variable) and pass keyword arguments into the plotting functions.

    In [139]:
    g = sns.PairGrid(tips, hue="size", palette="GnBu_d")
    g.map(plt.scatter, s=50, edgecolor="white")
    g.add_legend();
    
    </div>
    

    PairGrid is flexible, but to take a quick look at a dataset, it can be easier to use pairplot(). This function uses scatterplots and histograms by default, although a few other kinds will be added (currently, you can also plot regression plots on the off-diagonals and KDEs on the diagonal).

    In [140]:
    sns.pairplot(iris, hue="species", height=2.5);
    
    </div>
    

    You can also control the aesthetics of the plot with keyword arguments, and it returns the PairGrid instance for further tweaking.

    In [141]:
    g = sns.pairplot(iris, hue="species", palette="Set2", diag_kind="kde", height=2.5)
    
    </div>
    

    画出令人赏心悦目的图形,是数据可视化的目标之一。我们知道,数据可视化可以帮助我们向观众更加直观的展示定量化的insight, 帮助我们阐述数据中蕴含的道理。除此之外,我们还希望可视化的图表能够帮助引起读者的兴趣,使其对我们的工作更感兴趣。

    Matplotlib给了我们巨大的自由空间,我们可以根据自己的需要,任意调整图像的风格。然而,为了绘制一张上述的“令人赏心悦目”的图片,往往需要长期的绘图经验。这对新手来说时间成本无疑是非常高的。为此,seaborn也给我们集成好了一些设置好的绘图风格,使用这些内置风格,我们就能“傻瓜式”地获得美观的绘图风格。

    In [142]:
    import numpy as np
    import seaborn as sns
    import matplotlib.pyplot as plt
    
    </div>
    

    让我们来定义一簇简单的正弦曲线,然后观察一下不同的绘图风格的区别。

    In [143]:
    def sinplot(flip=1):
        x = np.linspace(0, 14, 100)
        for i in range(1, 7):
            plt.plot(x, np.sin(x + i * .5) * (7 - i) * flip)
    
    </div>
    

    这是matplotlib默认风格:

    In [144]:
    sinplot()
    
    </div>
    

    现在我们切换成seaborn默认风格。

    In [145]:
    sns.set()
    sinplot()
    
    </div>
    

    (Note that in versions of seaborn prior to 0.8, set() was called on import. On later versions, it must be explicitly invoked).

    Seaborn 把matplotlib中的参数分为了两类。其中第一类用来调整图片的风格(背景、线型线宽、字体、坐标轴等),第二类用来根据不同的需求微调绘图格式(图片用在论文、ppt、海报时有不同的格式需求。)

    其他格式修改方法

    Seaborn 绘图风格

    在seaborn中,有五种预置好的绘图风格,分别是:darkgrid, whitegrid, dark, whiteticks。其中darkgrid是默认风格。

    用户可以根据个人喜好和使用场合选择合适的风格。例如,如果图像中数据非常密集,那么使用white风格是比较合适的,因为这样就不会有多于的元素影响原始数据的展示。再比如,如果看图的读者有读数需求的话,显然带网格的风格是比较好的,这样他们就很容易将图像中的数据读出来。

    先来看whitegrid风格

    In [146]:
    sns.set_style("whitegrid")
    data = np.random.normal(size=(20, 6)) + np.arange(6) / 2
    sns.boxplot(data=data);
    
    </div>
    

    在很多场合下(比如ppt展示时,用户不会详细读数据,而主要看趋势),用户对网格的需求是不大的,此时我们可以去掉网格。

    In [147]:
    sns.set_style("dark")
    sinplot()
    
    </div>
    
    In [148]:
    sns.set_style("white")
    sinplot()
    
    </div>
    

    ticks风格介于grid风格与完全没有grid的风格之间,坐标轴上提供了刻度线。

    In [149]:
    sns.set_style("ticks")
    sinplot()
    
    </div>
    

    移除侧边边界线

    In [150]:
    sinplot()
    sns.despine()
    
    </div>
    

    当然,左侧和下方的线也是可以移除的。

    In [151]:
    sns.set_style("white")
    sns.boxplot(data=data, palette="deep")
    sns.despine(left=True,bottom=True)
    
    </div>
    

    暂时性地设置风格

    尽管你可以在风格与风格之间通过sns.set_style("ticks")转换风格,你也可以临时使用一次某种风格,而不影响剩余图片的风格。这种操作可以通过with实现。

    In [152]:
    f = plt.figure()
    with sns.axes_style("darkgrid"):
        ax = f.add_subplot(1, 2, 1)
        sinplot()
    ax = f.add_subplot(1, 2, 2)
    sinplot(-1)
    
    </div>
    

    自定义seaborn styles

    当然了,如果这五种seaborn自带风格也不能满足你的需求,你还可以自行设置自己的风格,可以设置的参数有:

    In [153]:
    sns.axes_style()
    
    Out[153]:
    {'axes.facecolor': 'white',
     'axes.edgecolor': '.15',
     'axes.grid': False,
     'axes.axisbelow': True,
     'axes.labelcolor': '.15',
     'figure.facecolor': 'white',
     'grid.color': '.8',
     'grid.linestyle': '-',
     'text.color': '.15',
     'xtick.color': '.15',
     'ytick.color': '.15',
     'xtick.direction': 'out',
     'ytick.direction': 'out',
     'lines.solid_capstyle': 'round',
     'patch.edgecolor': 'w',
     'image.cmap': 'rocket',
     'font.family': ['sans-serif'],
     'font.sans-serif': ['Arial',
      'DejaVu Sans',
      'Liberation Sans',
      'Bitstream Vera Sans',
      'sans-serif'],
     'patch.force_edgecolor': True,
     'xtick.bottom': False,
     'xtick.top': False,
     'ytick.left': False,
     'ytick.right': False,
     'axes.spines.left': True,
     'axes.spines.bottom': True,
     'axes.spines.right': True,
     'axes.spines.top': True}
    </div>
    

    设置的方法如下:

    In [154]:
    sns.set_style("white", {"ytick.right": True,'axes.grid':False})
    sinplot()
    
    </div>
    
  • 相关阅读:
    排序算法的实现(冒泡,选择,插入 O(N*N)--理解方法实现
    HTTPS工作原理和TCP握手机制
    HTTP协议学习
    IP头,TCP头,UDP头,MAC帧头定义
    单链表的实现
    数字图像处理------中值滤波
    对于矩阵的理解-- by 孟岩老师
    java编码问题总结
    jsp数据库连接大全和数据库操作封装到Javabean
    构建一个高可扩展性javabean和jsp连接数据库操作
  • 原文地址:https://www.cnblogs.com/hichens/p/13295559.html
Copyright © 2020-2023  润新知