Python中可视化数据(直方图和密度图)

本文将全面介绍如何使用matplotlib和seaborn库在python中使用直方图和密度图。在整个过程中,我们将探索一个真实世界的数据集,因为在网上提供大量资源的情况下,没有理由不使用实际数据!我们将可视化的nycflights13数据,其中包含超过300,000个航班离开纽约市的航班2013年的观测数据。
在我们开始绘图之前先检查我们的数据。我们可以将数据读入熊猫数据框并显示前10行:
import pandas as pd
# read in data and examine first 10 rowsflights = pd.read_csv('data/formatted_flights.csv')flights.head(10)
dataframe负责人
航班到达延误在几分钟内,负值意味着航班较早(事实证明,航班往往会提早到达,只是从来没有当我们在他们身上!)有超过30万次的航班,至少延迟60分钟和最大延迟时间为120分钟。数据框中的另一列是我们可用于比较的航空公司的名称。
直方图
为了使 python 中的基本直方图, 我们可以使用 matplotlib 或 seaborn。下面的代码显示两个库中创建等效数字的函数调用。对于绘图调用, 我们指定 binwidth 的数量。
# import the libraries
import matplotlib.pyplot as plt
import seaborn as sns
# matplotlib histogram
plt.hist(flights['arr_delay'], color = 'blue', edgecolor = 'black',
bins = int(180/5))
# seaborn histogram
sns.distplot(flights['arr_delay'], hist=true, kde=false,
bins=int(180/5), color = 'blue',
hist_kws={'edgecolor':'black'})
# add labels
plt.title('histogram of arrival delays')
plt.xlabel('delay (min)')
plt.ylabel('flights')
直方图(由matplotlib和seaborn生成的等效图)
对于大多数基本的直方图,我会使用matplotlib代码,因为它更简单,但我们稍后将使用seaborn distplot函数来创建不同的分布。
我怎么得到5分钟的binwidth?找出最佳binwidth的唯一方法是尝试多个值!下面的代码是在matplotlib中使用一系列binwidth来制作相同的图形。最终,对于binwidth没有正确或错误的答案,但我选择5分钟,因为我认为它最能代表分布。
# show 4 different binwidths
for i, binwidth in enumerate([1, 5, 10, 15]):
# set up the plot
ax = plt.subplot(2, 2, i + 1)
# draw the plot
ax.hist(flights['arr_delay'], bins = int(180/binwidth),
color = 'blue', edgecolor = 'black')
# title and labels
ax.set_title('histogram with binwidth = %d' % binwidth, size = 30)
ax.set_xlabel('delay (min)', size = 22)
ax.set_ylabel('flights', size= 22)
plt.tight_layout()
plt.show()
不同带宽的直方图
binwidth的选择会显着影响结果图。较小的带宽可能会使绘图混乱,但较大的带宽可能会掩盖数据中的细微差别。matplotlib会自动为你选择一个合理的binwidth,但我喜欢在尝试了几个值后自己指定binwidth。没有真正的正确或错误答案,因此请尝试几个选项并查看哪些最适合您的特定数据。
当直方图失败时
直方图是开始探索从一个类别中提取的单个变量的一个很好的方法。但是, 当我们要比较一个变量分布在多个类别, 直方图有问题的可读性。例如, 如果我们要比较航空公司之间的到达延迟分布, 一个不太好的方法是在同一地块上为每家航空公司创建直方图:
与多个航空公司重叠的直方图
(请注意,y轴已被标准化以考虑航空公司之间航班的不同数量。为此,请将参数传递norm_hist = true给sns.distplot函数调用。)
解决方案#1:并排直方图
我们可以将它们并排放置,而不是重叠航空公司的直方图。为此,我们为每家航空公司创建一个到达延迟列表,然后将其plt.hist作为列表列表传递给函数调用。我们必须为每家航空公司和一个标签指定不同的颜色,以便我们可以区分它们。代码,包括为每家航空公司创建列表如下:
# make a separate list for each airline
x1 = list(flights[flights['name'] == 'united air lines inc.']['arr_delay'])
x2 = list(flights[flights['name'] == 'jetblue airways']['arr_delay'])
x3 = list(flights[flights['name'] == 'expressjet airlines inc.']['arr_delay'])
x4 = list(flights[flights['name'] == 'delta air lines inc.']['arr_delay'])
x5 = list(flights[flights['name'] == 'american airlines inc.']['arr_delay'])
# assign colors for each airline and the names
colors = ['#e69f00', '#56b4e9', '#f0e442', '#009e73', '#d55e00']
names = ['united air lines inc.', 'jetblue airways', 'expressjet airlines inc.'',
'delta air lines inc.', 'american airlines inc.']
# make the histogram using a list of lists
# normalize the flights and assign colors and names
plt.hist([x1, x2, x3, x4, x5], bins = int(180/15), normed=true,
color = colors, label=names)
# plot formatting
plt.legend()
plt.xlabel('delay (min)')
plt.ylabel('normalized flights')
plt.title('side-by-side histogram with multiple airlines')
默认情况下,如果我们传入列表列表,matplotlib将并排放置条形图。
解决方案 #2: 堆积条形图
我们可以通过将参数传递stacked = true给直方图调用来堆叠它们,而不是为每个航空公司并排绘制条形图:
# stacked histogram with multiple airlinesplt.hist([x1, x2, x3, x4, x5], bins = int(180/15), stacked=true,normed=true, color = colors, label=names)
那么,这绝对不是更好!在这里,每个航空公司都代表每个舱位的整体部分,但几乎不可能进行比较。我们使用直方图尝试的两种解决方案都不成功,因此是时候转移到密度图。
密度图
首先,什么是密度图?甲密度图是从数据中估计的直方图的平滑化,连续版本。最常见的估计形式被称为核密度估计。在这种方法中,在每个单独的数据点处绘制连续曲线(内核),然后将所有这些曲线相加在一起以进行单个平滑密度估计。最常用的内核是高斯(在每个数据点产生高斯钟形曲线)。
kernel density estimation
这里,x轴上的每条黑色小垂直线代表一个数据点。单个内核(本例中为高斯)在每个点上方用红色虚线表示。坚实的蓝色曲线是通过对各个高斯求和来创建的,并形成整体密度图。
x轴是变量的值,就像直方图一样,但y轴代表什么?密度图中的y轴是核密度估计的概率密度函数。但是,我们需要小心地指定这是一个概率密度,而不是概率。不同之处在于概率密度是x轴上每单位的概率。要转换为实际概率,我们需要在x轴上找到特定间隔下曲线下方的区域。有点令人困惑的是,因为这是一个概率密度而不是概率,所以y轴可以取大于1的值。密度图的唯一要求是曲线下的总面积合并为一。我通常倾向于将密度图上的y轴视为仅用于不同类别之间的相对比较的值。
seaborn中的密度图
为了使 seaborn 中的密度图, 我们可以使用 distplot 或 kdeplot 函数。我将继续使用 distplot 函数, 因为它允许我们用一个函数调用进行多个分布。例如, 我们可以使一个密度图显示所有到达延迟在对应的直方图之上:
# density plot and histogram of all arrival delayssns.distplot(flights['arr_delay'], hist=true, kde=true,bins=int(180/5), color = 'darkblue',hist_kws={'edgecolor':'black'},kde_kws={'linewidth': 4})
曲线显示了密度图, 它实质上是直方图的平滑版本。y 轴是按密度计算的, 而直方图在缺省情况下是规范化的, 因此它与密度图具有相同的 y 尺度。
与直方图的 binwidth 类似, 密度图有一个参数称为带宽,绘图库将为我们选择一个合理的带宽值 (默认情况下使用 斯科特 估计), 而不像直方图的 binwidth, 我通常使用默认带宽。但是, 我们可以使用不同的带宽来查看是否有更好的选择。
请注意,更宽的带宽会使分配更平滑。我们也看到,即使我们将数据限制在-60到120分钟,密度图也超出了这些限制。这是密度图的一个潜在问题:因为它计算每个数据点的分布,所以它可以生成超出原始数据范围的数据。这可能意味着我们最终会得到原始数据中从来不存在的x轴上不可能的值!值得注意的是,我们也可以更改内核,这会改变在每个数据点处绘制的分布,从而改变整体分布。但是,对于大多数应用程序,默认的内核,高斯和默认的带宽估计工作得很好。
解决方案#3密度图
为了在同一个图上显示分布,我们可以遍历航空公司,每次调用distplot内核密度估计值设置为true并将直方图设置为false。下面是用多个航空公司绘制密度图的代码:
# list of five airlines to plot
airlines = ['united air lines inc.', 'jetblue airways', 'expressjet airlines inc.'',
'delta air lines inc.', 'american airlines inc.']
# iterate through the five airlines
for airline in airlines:
# subset to the airline
subset = flights[flights['name'] == airline]
# draw the density plot
sns.distplot(subset['arr_delay'], hist = false, kde = true,
kde_kws = {'linewidth': 3},
label = airline)
# plot formatting
plt.legend(prop={'size': 16}, title = 'airline')
plt.title('density plot with multiple airlines')
plt.xlabel('delay (min)')
plt.ylabel('density')
最后,我们已经达成了一个有效的解决方案!通过密度图,我们可以轻松地进行航空公司之间的比较,因为图形不那么混乱。我们得出这样的结论:所有这些航空公司都有几乎相同的到达延迟分布!但是,数据集中还有其他航空公司,我们可以绘制一个稍有不同的图形来说明密度图的另一个可选参数,并为图形着色。
阴影密度图
填充密度图可以帮助我们区分重叠分布。虽然这不总是一个好方法,但它可以帮助强调分布之间的差异。为了遮蔽密度图,我们传递shade = true给调用中的kde_kws参数distplot。
sns.distplot(subset ['arr_delay'],hist = false,kde = true, kde_kws = {'shade':true,'linewidth':3}, label = airline)
阴影密度图
对于这个图表,我认为这是有道理的,因为阴影可以帮助我们区分它们重叠区域的地块。现在,我们终于获得了一些有用的信息:阿拉斯加航空公司的航班往往比联合航空公司的航班时间更早。
rug plots
如果要显示分布中的每个值而不仅是平滑密度,则可以添加小地图。这显示了x轴上的每个数据点,使我们可以看到所有的实际值。使用seaborn的好处distplot是,我们可以添加单个参数调用rug = true(具有一些格式化)的地毯情节。
# subset to alaska airlines
subset = flights[flights['name'] == 'alaska airlines inc.']
# density plot with rug plot
sns.distplot(subset['arr_delay'], hist = false, kde = true, rug = true,
color = 'darkblue',
kde_kws={'linewidth': 3},
rug_kws={'color': 'black'})
# plot formatting
plt.title('density plot with rug plot for alaska airlines')
plt.xlabel('delay (min)')
plt.ylabel('density')
有了许多数据点, 地毯图会变得拥挤, 但是对于某些数据集来说, 查看每一个资料点都是有帮助的。