Press "Enter" to skip to content

改善探索性数据分析的实用技巧

让EDA更轻松(更漂亮)的简短指南

Photo by Sam Dan Truong on Unsplash

介绍

在使用任何机器学习模型之前,探索性数据分析(EDA)是必经的一步。EDA过程需要数据分析师和数据科学家的专注和耐心:在从分析的数据中获得有意义的洞察之前,通常需要花费很多时间积极地使用一个或多个可视化库。

在本文中,我将根据我的个人经验与您分享如何简化EDA过程并加快速度的一些建议。特别是,我将给出三个重要的建议,这些建议是我在与EDA作斗争时学到的:

  1. 使用最适合您任务的非平凡图表;
  2. 充分发挥可视化库的功能;
  3. 寻找更快的制作相同内容的方法。

注意:为了在本文中创建信息图表,我们将使用Kaggle[2]上的风力发电数据。让我们开始吧!

提示1:不要害怕使用非平凡图表

我学会了如何应用这个提示,当我在与风能分析和预测[1]相关的研究论文上工作时。在为这个项目进行EDA时,我面临着创建一个总结矩阵的需要,该矩阵将反映出风参数之间的所有关系,以找出它们之间的最强影响。我首先想到的是构建一个“老旧的”相关矩阵,这是我在许多数据科学/数据分析项目中见过的。

如您所知,相关矩阵用于量化和总结变量之间的线性关系。在下面的代码片段中,corrcoef函数被用于风力发电数据的特征列上。我还应用了Seaborn的heatmap函数将相关矩阵数组绘制为热力图:

import matplotlib.pyplot as pltimport seaborn as snsimport pandas as pdimport numpy as np# 读取数据data = pd.read_csv('T1.csv')print(data)# 重命名列以使标题更短data.rename(columns={'LV ActivePower (kW)':'P',                     'Wind Speed (m/s)':'Ws',                     'Theoretical_Power_Curve (KWh)':'Power_curve',                     'Wind Direction (°)': 'Wa'},inplace=True)cols = ['P', 'Ws', 'Power_curve', 'Wa']# 构建矩阵correlation_matrix = np.corrcoef(data[cols].values.T)hm = sns.heatmap(correlation_matrix,                 cbar=True, annot=True, square=True, fmt='.3f',                 annot_kws={'size': 15},                 cmap='Blues',                 yticklabels=['P', 'Ws', 'Power_curve', 'Wa'],                 xticklabels=['P', 'Ws', 'Power_curve', 'Wa'])# 保存图像plt.savefig('image.png', dpi=600, bbox_inches='tight')plt.show()
Example of the built correlation matrix. Image by Author.

通过分析得到的图形结果,可以得出结论,风速和有功功率之间存在强相关性,但我认为很多人都会同意,使用这种类型的可视化工具来解释结果并不容易,因为这里只有数字。

相关矩阵的一个很好的替代品是散点矩阵,它允许您在一个地方可视化数据集中不同特征之间的成对相关性。在这种情况下,应使用sns.pairplot

import matplotlib.pyplot as pltimport seaborn as snsimport pandas as pd# 读取数据data = pd.read_csv('T1.csv')print(data)# 重命名列以使标题更短data.rename(columns={'LV ActivePower (kW)':'P',                     'Wind Speed (m/s)':'Ws',                     'Theoretical_Power_Curve (KWh)':'Power_curve',                     'Wind Direction (°)': 'Wa'},inplace=True)cols = ['P', 'Ws', 'Power_curve', 'Wa']# 构建矩阵sns.pairplot(data[cols], height=2.5)plt.tight_layout()# 保存图像plt.savefig('image2.png', dpi=600, bbox_inches='tight')plt.show()
散点图矩阵示例。图片由作者提供。

通过观察散点图矩阵,可以快速查看数据的分布情况以及是否存在异常值。然而,这种图表的主要缺点与成对绘制数据有关,可能会导致重复。

最终,我决定将上述图形合并为一个图表,其中左下部分将包含所选参数的散点图,右上部分将包含不同大小和颜色的气泡:较大的圆表示研究参数具有更强的线性相关性。矩阵的对角线将显示每个特征的分布情况:这里的窄峰表示该特定参数变化不大,而其他特征则变化。

下面是构建此摘要矩阵的代码。其中地图包括三个部分 – fig.map_lowerfig.map_diagfig.map_upper

import pandas as pdimport matplotlib.pyplot as pltimport seaborn as sns# 读取数据data = pd.read_csv('T1.csv')print(data)# 将列名重命名为缩短标题data.rename(columns={'LV ActivePower (kW)':'P',                     'Wind Speed (m/s)':'Ws',                     'Theoretical_Power_Curve (KWh)':'Power_curve',                     'Wind Direction (°)': 'Wa'},inplace=True)cols = ['P', 'Ws', 'Power_curve', 'Wa']# 构建矩阵def correlation_dots(*args, **kwargs):    corr_r = args[0].corr(args[1], 'pearson')    ax = plt.gca()    ax.set_axis_off()    marker_size = abs(corr_r) * 3000    ax.scatter([.5], [.5], marker_size,               [corr_r], alpha=0.5,               cmap = 'Blues',               vmin = -1, vmax = 1,               transform = ax.transAxes)    font_size = abs(corr_r) * 40 + 5sns.set(style = 'white', font_scale = 1.6)fig = sns.PairGrid(data, aspect = 1.4, diag_sharey = False)fig.map_lower(sns.regplot)fig.map_diag(sns.histplot)fig.map_upper(correlation_dots)# 保存图像plt.savefig('image3.jpg', dpi = 600, bbox_inches = 'tight')plt.show()
摘要矩阵示例。图片由作者提供。

摘要矩阵结合了之前研究的两种图表的优点 – 其下(左)部分模仿了散点图矩阵,其上(右)部分以图形方式反映了相关矩阵的数值结果。

提示2:充分利用可视化库的功能

我经常需要向同事和客户展示EDA的结果,因此可视化对我来说是一个关键的助手。我总是尝试给图表添加各种元素,如箭头和注释,以使它们更加吸引人和易读。

让我们回到上面讨论的风能项目的EDA实现案例。在涉及到风能时,最重要的参数之一是功率曲线。风力发电机(或整个风电场)的功率曲线是一个图表,显示在不同风速下生成的电力量。值得注意的是,风力发电机在低风速下不会运行。它们的启动与切入速度有关,通常在2.5-5 m/s的范围内。在12和15 m/s的速度之间,达到额定功率。最后,每个风力发电机都有一个安全运行的风速上限。一旦达到这个切出速度的限制,风力发电机将不会发电,除非速度降至运行范围内。

研究的数据集包括理论功率曲线(这是一个典型的制造商曲线,没有任何异常值)和如果绘制风力与速度的实际曲线。后者通常包含许多不符合理论理想形状的点,可能是由于风力发电机故障、错误的SCADA测量或非计划维护引起的。

现在我们将创建一个图像,显示功率曲线的两种类型——首先,除了图例之外,没有任何额外的项目:

import pandas as pdimport matplotlib.pyplot as plt# 读取数据data = pd.read_csv('T1.csv')print(data)# 重命名列以缩短标题data.rename(columns={'LV ActivePower (kW)':'P',                     'Wind Speed (m/s)':'Ws',                     'Theoretical_Power_Curve (KWh)':'Power_curve',                     'Wind Direction (°)': 'Wa'},inplace=True)# 构建图像plt.scatter(data['Ws'], data['P'], color='steelblue', marker='+', label='实际值')plt.scatter(data['Ws'], data['Power_curve'], color='black', label='理论值')plt.xlabel('风速')plt.ylabel('功率')plt.legend(loc='best')# 保存图像plt.savefig('image4.png', dpi=600, bbox_inches='tight')plt.show()
风力曲线的‘无声’图表。作者提供的图像。

如您所见,该图表需要解释,因为它不包含任何附加详细信息。

但是,如果我们添加线条来突出图表的三个主要区域,标记进入、额定和退出速度,并且使用箭头注释显示离群值之一,会是怎样的效果呢?

让我们看看在这种情况下图表的样子:

import pandas as pdimport matplotlib.pyplot as plt# 读取数据data = pd.read_csv('T1.csv')print(data)# 重命名列以缩短标题data.rename(columns={'LV ActivePower (kW)':'P',                     'Wind Speed (m/s)':'Ws',                     'Theoretical_Power_Curve (KWh)':'Power_curve',                     'Wind Direction (°)': 'Wa'},inplace=True)# 构建图像plt.scatter(data['Ws'], data['P'], color='steelblue', marker='+', label='实际值')plt.scatter(data['Ws'], data['Power_curve'], color='black', label='理论值')# 添加垂直线、文本注释和箭头plt.vlines(x=3.05, ymin=10, ymax=350, lw=3, color='black')plt.text(1.1, 355, r"进入", fontsize=15)plt.vlines(x=12.5, ymin=3000, ymax=3500, lw=3, color='black')plt.text(13.5, 2850, r"额定", fontsize=15)plt.vlines(x=24.5, ymin=3080, ymax=3550, lw=3, color='black')plt.text(21.5, 2900, r"退出", fontsize=15)plt.annotate('离群值!', xy=(18.4,1805), xytext=(21.5,2050),            arrowprops={'color':'red'})plt.xlabel('风速')plt.ylabel('功率')plt.legend(loc='best')# 保存图像plt.savefig('image4_2.png', dpi=600, bbox_inches='tight')plt.show()
风力曲线的‘健谈’图表。作者提供的图像。

提示3:始终找到更快的方法来完成相同的工作

在分析风能数据时,我们常常希望获得关于风能潜力的全面信息。因此,除了风能的动力学外,还需要有一个显示风速与风向之间关系的图表。

可以使用以下代码来说明风力的变化:

import pandas as pdimport matplotlib.pyplot as plt# 读取数据data = pd.read_csv('T1.csv')print(data)# 重命名列以缩短标题data.rename(columns={'LV ActivePower (kW)':'P',                     'Wind Speed (m/s)':'Ws',                     'Theoretical_Power_Curve (KWh)':'Power_curve',                     'Wind Direction (°)': 'Wa'},inplace=True)# 将10分钟数据重新采样为每小时时间测量data['Date/Time'] = pd.to_datetime(data['Date/Time'])fig = plt.figure(figsize=(10,8))group_data = (data.set_index('Date/Time')).resample('H')['P'].sum()# 绘制风力动态图group_data.plot(kind='line')plt.ylabel('功率')plt.xlabel('日期/时间')plt.title('发电功率(重新采样为每小时)')# 保存图像plt.savefig('wind_power.png', dpi=600, bbox_inches='tight')plt.show()

下面是生成的图表:

风力动态。图片由作者提供。

正如人们可能注意到的那样,风力动态的轮廓具有相当复杂、不规则的形状。

风向玫瑰图,或极坐标玫瑰图,是一种专门用于表示气象数据分布的图表,通常是按风向分布的风速[3]。在matplotlib库中有一个简单的模块windrose,可以轻松构建这种类型的可视化,例如:

import pandas as pdimport matplotlib.pyplot as pltimport numpy as npfrom windrose import WindroseAxes# 读取数据data = pd.read_csv('T1.csv')print(data)# 重命名列以使它们的标题更短data.rename(columns={'LV ActivePower (kW)':'P',                     'Wind Speed (m/s)':'Ws',                     'Theoretical_Power_Curve (KWh)':'Power_curve',                     'Wind Direction (°)': 'Wa'},inplace=True)wd  = data['Wa']ws = data['Ws']# 以堆叠直方图的形式绘制归一化的风向玫瑰图ax = WindroseAxes.from_ax()ax.bar(wd, ws, normed=True, opening=0.8, edgecolor='white')ax.set_legend()# 保存图像plt.savefig('windrose.png', dpi = 600, bbox_inches = 'tight')plt.show()
根据可用数据获得的风向玫瑰图。图片由作者提供。

从风玫瑰图中可以看出,主要有两个主要的风向,即东北和西南。

但如何将这两个图像合并成一个图像?最明显的选择是使用add_subplot。然而,由于windrose库的特殊性,这并不是一个直接的任务:

import pandas as pdimport matplotlib.pyplot as pltimport numpy as npfrom windrose import WindroseAxes# 读取数据data = pd.read_csv('T1.csv')print(data)# 重命名列以使它们的标题更短data.rename(columns={'LV ActivePower (kW)':'P',                     'Wind Speed (m/s)':'Ws',                     'Theoretical_Power_Curve (KWh)':'Power_curve',                     'Wind Direction (°)': 'Wa'},inplace=True)data['Date/Time'] = pd.to_datetime(data['Date/Time'])fig = plt.figure(figsize=(10,8))# 将两个图表作为子图绘制ax1 = fig.add_subplot(211)group_data = (data.set_index('Date/Time')).resample('H')['P'].sum()group_data.plot(kind='line')ax1.set_ylabel('Power')ax1.set_xlabel('Date/Time')ax1.set_title('Power generation (resampled to 1 hour)')ax2 = fig.add_subplot(212, projection='windrose')wd  = data['Wa']ws = data['Ws']ax = WindroseAxes.from_ax()ax2.bar(wd, ws, normed=True, opening=0.8, edgecolor='white')ax2.set_legend()# 保存图像plt.savefig('image5.png', dpi=600, bbox_inches='tight')plt.show()

在这种情况下,结果如下:

包含风力动态和风向玫瑰图的单个图像。图片由作者提供。

主要的缺点是两个子图的大小不同,因此在风向玫瑰图周围有很多空白空间。

为了简化操作,我建议使用Python Imaging Library(PIL)[4],只需11行代码:

import numpy as np
import PIL
from PIL import Image

# 列出需要合并的图片
list_im = ['wind_power.png','windrose.png']
imgs = [PIL.Image.open(i) for i in list_im]

# 将所有图片调整大小以匹配最小的尺寸
min_shape = sorted([(np.sum(i.size), i.size) for i in imgs])[0][1]

# 垂直堆叠图片
images_comb = np.vstack((np.asarray(i.resize(min_shape)) for i in imgs))
images_comb = PIL.Image.fromarray(images_comb)

# 保存合并后的图片
images_comb.save('image5_2.png', dpi=(600,600))

这样输出的结果看起来更漂亮,因为两个图片的尺寸相同,代码会选择最小的尺寸,并将其他图片调整尺寸以匹配:

使用PIL合并的包含风力动态和风向图的单个图片。作者提供的图片。

顺便说一下,在使用PIL时,也可以进行水平堆叠,比如,我们将一个“沉默”和一个“健谈”的功率曲线图进行比较:

import numpy as np
import PIL
from PIL import Image

list_im = ['image4.png','image4_2.png']
imgs = [PIL.Image.open(i) for i in list_im]

# 选择最小的图片,并调整其他图片以匹配它(可以是任意形状的图片)
min_shape = sorted([(np.sum(i.size), i.size) for i in imgs])[0][1]

# 水平堆叠图片
imgs_comb = np.hstack((np.asarray(i.resize(min_shape)) for i in imgs))

# 保存合并后的图片
imgs_comb = PIL.Image.fromarray(imgs_comb)
imgs_comb.save('image4_merged.png', dpi=(600,600))
比较和对比两个带有功率曲线的图表。作者提供的图片。

结论

在本篇文章中,我与大家分享了三个使数据探索性分析(EDA)过程更轻松的技巧。希望这些建议对你自己有用,并且你也会开始将它们应用到你的数据任务中。

这些技巧完全符合我在进行EDA时尝试应用的公式:定制化 → 细分化 → 优化

那么,你可能会问,为什么这很重要?我可以说,实际上这很重要,因为:

  • 非常重要的一点是根据你目前面临的具体需求来定制你的图表。例如,不要创建大量的信息图表,而是思考如何将几个图表合并成一个,就像我们在创建摘要矩阵时所做的那样,它结合了散点图和相关性图表的优点。
  • 所有的图表都应该能够自我解释。因此,你需要知道如何将图表中的重要内容细分和清晰地展示出来。比较一下“沉默”和“健谈”的功率曲线之间的差异有多大。
  • 最后,每个数据专家都应该学会优化EDA过程,使事情变得更快捷(并且更轻松)。如果你需要将两个图像合并成一个,不一定每次都要使用add_subplot选项。

还有什么?我可以明确地说,EDA是使用数据进行工作中非常富有创造力和有趣的一步(更不用说它还非常重要了)。

让你的信息图表闪耀如钻石,并且别忘了享受这个过程!

参考文献列表

  1. 论文“Data-driven applications for wind energy analysis and prediction: The case of”La Haute Borne” wind farm”。https://doi.org/10.1016/j.dche.2022.100048
  2. 风力发电数据:https://www.kaggle.com/datasets/bhavikjikadara/wind-power-generated-data?resource=download
  3. 关于风向图库的教程:https://windrose.readthedocs.io/en/latest/index.html
  4. PIL库:https://pillow.readthedocs.io/en/stable/index.html

感谢阅读!

Leave a Reply

Your email address will not be published. Required fields are marked *