Excel在当今商业中的使用非常普遍。在Dataquest,出于很多原因,我们通常推荐使用代码处理数据,并且我们的许多数据科学课程的目标是教授数据分析和数据科学的高效编码。但是,无论您多么喜欢使用Python,在一天结束时,有时您还是需要使用Excel来展示您的发现或共享您的数据。
但这并不意味着我们就不能继续享受Python的一些效率了!实际上,使用一个名为xlwings的库,您可以使用Python使Excel运行得更快。
在这个xlwings教程中,我们将介绍如何在Excel中使用Python来执行和使用一些常见的操作,比如根据特定的条件删除行、使用Excel函数和公式、自动填充、创建表单、图表等。为了跟随这篇文章,你应该熟悉基本的Python概念(对象、方法、属性、函数)和Python的语法,并对Excel和VBA有一定的了解。
我们将使用一个数据集,其中包含有关欧洲彩票抽奖的信息,称为EuroMillions。这个数据集是从这个链接下载的,它包含了截止到9月20日的所有EuroMillions彩票信息。当你阅读这篇文章的时候,这个链接上的数据应该会被更新为最新的信息,但是以防它是不可用的,这里是一个CSV文件,其中包含了来自该链接的9月20日的数据。
Euromillions是一种跨国彩票,在一些欧洲国家有售,特别是在安道尔、奥地利、比利时、法国(包括海外地区和集体)、爱尔兰、马恩岛、列支敦士登、卢森堡、摩纳哥、葡萄牙、西班牙、瑞士和英国(来源)。
在写这篇文章的时候,抽奖由从50个数字(从1到50)池中抽出的5个数字和从12个数字池中抽出的两个称为幸运之星的数字组成。为了赢得头奖,参加者必须正确选择所有抽奖号码和幸运之星。史上最大的头等奖是1.9亿欧元。(请注意,我们的数据集是以英镑而不是欧元来计算奖金的)。
在本教程中,我们将使用Python和xlwings来与Excel一起清理一个数据集,然后生成一些图形来可视化哪些数字最常赢得EuroMillions。
第一列是抽奖号码,第N1-L2列是已经抽到的号码和幸运星(按抽奖顺序排列),Jackpot列是欧元的头奖,Wins列告诉我们有多少下注中了头奖。
遇见 xlwings
xlwings是一个Python库,它使Python的一些数据分析特性可以在Excel实例中使用,包括对numpy数组、pandas Series和DataFrame的支持。与其他任何Python库一样,我们可以使用pip或conda等常用方法来安装它,但是如果您需要更多详细信息,您可以在这里访问xlwings的文档。(地址:https://docs.xlwings.org/en/stable/installation.html )
请注意,您需要在使用本xlwings教程的计算机上安装一个Microsoft Excel版本。
xlwings 对象
在xlwings中,有四种主要的对象类型,按递减的层次顺序排列:App(代表一个Excel实例)、Book、Sheet和Range。除了这些之外,我们还将处理Chart和Shape对象。您可以在官方文档中找到关于这些对象和其他对象的有用信息,但是我们将一次查看一个对象。
我们首先创建一个Book实例并将其命名为wb(工作簿)。
当您运行该代码时,它应该是这样的。
注意,当代码单元在Jupyter Notebook中运行时,Excel将自动启动。
通过实例化一个Book对象,一个属于我们的Book对象的App对象会被自动创建。下面是检查所有打开的Excel实例的方法。
注意:我们不打算在本教程中包含每个步骤的gif图片,因为我们不希望这个页面对于互联网连接缓慢或受限的人来说是一个麻烦的加载过程。但是,后续的代码运行步骤应该与我们上面所看到的类似:当我们在Juypter中运行一个单元格时,Excel电子表格将根据我们所运行的代码进行更新。
xw.apps对象是一个可迭代对象。要检查哪些工作簿属于这个可迭代对象的唯一实例,我们可以像这样对它调用books方法。
正如预期的那样,这个可迭代对象的惟一实例是工作簿wb。我们在下面来检查这个事实。
同样,我们可以检查哪些工作表属于这个工作簿:
我们也可以用它们的名字来引用工作表:
Sheet 对象有一个按预期工作的name属性。让我们更改我们唯一的工作表名称。
我们可以将数据从某些Python对象(例如列表和元组)移入到Excel中。让我们将我们dataframe中的数据移入到EuroMillions工作表中。为此,我们将使用range创建一个range对象,该对象将我们的DataFrame中的数据存储在Excel中的一排单元格内,在本例中从单元格A1开始:
>>>今日签到口令:r7kt<<<
现在运行结果是这样子:
可以看到,df的索引列也被移动到了Excel中。让我们清除此工作表的内容并复制不带索引的数据。
能够知道我们的表在哪里结束是很有用的。更具体地说,我们需要包含了数据的最后一行。为此,我们可以使用end方法和Range对象的row属性。
不出所料,row方法会返回Range对象的row。
方法end接受一个方向(“up”(或1)、“right”(或2)、“left”(或3)、“down”(或4))作为参数,并返回另一个range对象。它模仿Excel中常见的CTRL+Shift+箭头动作。
看,它检查出来了!
API属性
并不是所有的Excel功能都可以作为一个本地xlwings特性来使用。有时我们必须找到变通办法来做我们想做的事。幸运的是,xlwings让这一切变得非常简单。来自官方文档中“缺失的特性”一节:
解决方案:本质上,xlwings只是一个围绕Windows平台上的pywin32和Mac平台上的appscript的灵活的包装器。你可以通过调用其api属性来访问底层对象。底层对象将使用pywin32语法(感觉很像VBA)和appscript语法(感觉不像VBA)为您提供几乎所有可以用VBA做的事情。但是除了看起来很丑之外,请记住它使您的代码平台变成特定的(!)。Excel Visual Basic for Applications是对各种现有Excel对象的一个详细解释。
排序就是xlwings中缺少的功能之一。您可能已经注意到,那些记录是从最近到最远的抽签顺序排列的。在接下来的几个步骤中,我们将反转顺序。
对象ws.range(“A2:N{row}”.format(row=last_row))是一个Range对象。将api属性追加给它之后,会生成一个VBA Range对象,该对象反过来又可以访问它的VBA特性。
我们将使用这个VBA对象的Sort属性。在它的最简单的应用程序中,Sort接受两个参数:对表排序所使用的列(作为一个VBA Range对象)和排序类型(无论希望按升序还是降序排序)。第二个参数的参数文档可以在这里看到。我们将按升序进行排序。
把所有这些放在一起看起来就是这样的:
下面是它运行后在屏幕上显示的样子(注意:第一列已经改变,现在是按升序而不是降序进行排序。
分析数据
在尝试分析这个数据集时,我们将遇到一个问题,即日期分散在三个不同的列中。我们需要把它压缩到一列中。为此,我们将使用Python将Excel中的列适当地连接起来。我们首先在空的相邻列中插入一个头部。
接下来,我们可以插入想要用作字符串的Excel公式。注意:您应该使用什么参数分隔符的具体细节取决于您机器的本地区域设置。在我的示例中,参数分隔符是逗号,这也是我在本教程中使用的,但在您的示例中,它可能是分号。
在第一个单元格中插入公式后,在常规的Excel工作流中,第二个特性是通过表的末尾自动填充其余单元格。Autofill是VBA Range对象的一个方法。它接受两个参数,即一个将目标单元格作为参数的VBA Range对象和填充类型。我们感兴趣的是枚举为0的默认值。
这是执行这一步后屏幕显示的大致样子;注意最右边的新“Date”列。
我们还可以使用所需填充类型的命名形式。为此,我们需要从模块xlwings.constants中检索它,该模块包含大多数VBA属性的枚举参数的命名版本。您可以经常通过打印dir(xlwings.constants)来检查可用的属性。
(如果您不熟悉它,dir是一个本地Python函数,可以接受多种参数(模块、类和常规对象(如列表和字符串))。例如,如果您打印dir(some_list),它将为您输出您可以通过列表使用的所有方法和属性。
我们在上面所做的也可以通过下面的代码片段来实现。
由于我们将经常使用这个操作,我们将创建一个应用默认填充的函数,给定:
- 一个工作表
- 一个表示工作表中单元格的字符串
- 一个需要填充的末尾行。
为此,我们将引入一个名为get_address的新的Range方法。它接受四个布尔参数并返回一个字符串,该字符串标识不同详细级别的范围。下面是这个方法的一个很有启发性的说明。
现在我们来定义函数。
为了避免Excel进行不必要的计算,我们将使用硬编码的值替换刚才插入到列O中的公式。在此之前,让我们花点时间思考一下当Range是一个数组时,Range.value是什么类型的Python对象。
它是一个列表。我们来看它的前十个元素。
如果我们将这个列表插入到任何范围中,它将会水平放置值,这不是我们想要的。为了将它们垂直放置,我们需要使用Range对象的options方法,并将transpose=True选项作为一个参数,如下所示:
现在我们可以删除列C到E。
EuroMillions格式多年来经历了一些温和的修改,最后一次修改是在2016年9月24日。
从2016年9月24日起,幸运星的数量从11个数字变为12个数字。为了进行有意义的分析,我们只考虑在最后修改之后进行的抽奖。下一个代码片段将查找修改之前的最后一次抽奖,并将其命名为to_delete。
我们现在可以删除从第一次抽奖到to_delete的每一行。
这是我们目前的情况,此时:
在完成数据准备之后,我们现在将格式化这个表。我们首先将第一行的字体设置为粗体。
我们可以按此操作将“Jackpot”列以百万为单位进行格式化。请注意,下面的字符串格式取决于您机器的本地区域设置。如果您的格式看起来很奇怪,试着用圆点替换逗号。更多关于Excel自定义格式的内容,请查看这里。(地址:https://exceljet.net/custom-number-formats )
作为后续工作的辅助步骤,我们将找到与最后一列数据对应的字母。
现在,我们在header单元格的底部添加边框。与我们所做的类似,我们将使用api属性。此外,我们还将需要Range对象的Border属性、边框对齐枚举和边框样式。我们将在header单元格的底部设置一个双边边框(行样式-4119,对齐方式9)。
现在让我们对行和列进行自动调整。
哎呦!这看起来有点压扁了,让我们设置所有列的宽度为J列的宽度,这似乎是最大的值。这里(https://docs.microsoft.com/en-us/office/vba/api/excel.range.columnwidth ) 是我们下面正在使用的ColumnWidth的文档。
那样看起来更好一点。这个工作表我们就处理完成了!
让我们add一个名为Frequencies的新空白工作表,并将其分配给Python变量frequencies。
我们将在这个工作表中填入我们刚刚在EuroMillions工作表中处理过的数据集中每个数字和每个幸运星的绝对频率。
下面,我们将为单元格B1中的频率插入一个header,在单元格B2中,我们将输入一个公式,计算A2中的值在C2:G201范围内出现的次数。换句话说,我们将计算1在N1-N5列中出现的次数。在此之后,我们将自动填充B列上的其余单元格,以对它们各自的行执行相同的操作。
我们对幸运星也执行同样的操作:
此时,我们的新工作表看起来应该像这样:
我们正在接近我们的目标。让我们创建一个名为Graphs的工作表。
现在我们将创建一个Chart对象。这只会产生一个空白的白框,但是不要担心!我们稍后会用这个方框来图表化我们的数据。
我们可以像name工作表一样来name图表。方法set_source_data允许我们通过传入一个范围对象来定义我们图表的数据源。
Excel将尝试猜测x轴应该是什么,但我们可以使用VBA Chart方法FullSeriesCollection来强制让它成为我们在Frequencies上创建的数字。我们可以通过使用nr_freq.api索引1的对象来编辑图表:
Excel非常擅长猜测用户想要什么样的图表,但是为了防止它猜错,我们将强制它成为柱状图。这里列出了各种类型的图表。唉,将这些图表与chart_type属性的可能值连接起来的唯一文档就是其源代码本身。
现在我们将定义图表的高度和宽度。度量单位为points。
此时,我们应该会看到:
使用SetElement方法和参数2一起设置图表上方的标题。请在这里查看其它参数。(地址;https://docs.microsoft.com/en-us/dotnet/api/microsoft.office.core.msochartelementtype?view=office-pia )
我们来添加最后的修饰。我们使用HasLegend属性删除图例。
我们将xlCategory类别作为参数1传入给Axes方法,并将TickLabelSpacing属性设置为1,这确保了图表轴上的每个元素都被显示。
为了完成对这个图表的格式化,我们通过将Line对象的Visible属性设置为0来删除轮廓。
这里我们会看到:
下面我们对幸运星也做了几乎同样的事情。
最后,我们创建了一个显示jackpot演变的时间序列图。
我们通过将TickLabels的NumberForma属性设置为期望的外观来修复垂直轴“labels”的格式。
这样我们就完成了!现在我们保存该文件并退出我们的Excel实例。
希望本xlwings教程对您有所帮助!
学习xlwings的一些有用资源包括官方文档、这个格式化备忘录、用于Excel文档的VBA和由xlwings的开发者——Felix Zumstein本人设计的《xlwings: 使用Python处理Excel》课程。
英文原文:https://www.dataquest.io/blog/python-excel-xlwings-tutorial/
译者:好酒不上头