Python的xlwings模块在Excel中的使用总结

1. xlwings自定义Excel的数组公式

数组公式运用前是要以数组的形式嵌入公式,要选定数组区间,再按F2输入公式再按下ctrl+shift+enter,这样才能执行数组计算,得出的结果才是正确的。

输入数组公式首先必须选择用来存放结果的单元格区域(可以是一个单元格),在编辑栏输入公式,然后按Ctrl+Shift+Enter组合键锁定数组公式,Excel将在公式两边自动加上花括号“{}”。注意:不要自己键入花括号,否则,Excel认为输入的是一个正文标签。

数组包含数个单元格,这些单元格形成一个整体,所以,数组里的某一单元格不能单独编辑。在编辑数组前,必须先选取整个数组。 选取数组的步骤为:

(1) 选取数组中的任一单元格。

(2) 按下Ctrl+Shift+Enter

示例中用到的xlwings自定义公式如下:

@xw.func
def add_one(data):
return [[cell+1 for cell in row] for row in data]

上面的公式存在一个问题:它需要一个类似嵌套列表 [[1, 2], [3, 4]] 的“2维”的输入。所以如果你把这个公式用于一个单元格,会得到下面的错误信息: 类型错误: 'float' 对象不可迭代 。

要强制Excel不管输入参数是单元格、单行/单列还是2维区域都把它转化为2维数组,可以把上面的公式做如下扩展:

@xw.func
@xw.arg('data', ndim=2)
def add_one(data):
    return [[cell + 1 for cell in row] for row in data]

 通常会在自定义公式(UDF)中用到NumPy array或者Pandas DataFrame。要用numpy array来定义一个矩阵运算公式,可以定义下面的公式:

import xlwings as xw
import numpy as np
@xw.func
@xw.arg('x', np.array, ndim=2)
@xw.arg('y', np.array, ndim=2)
def matrix_mult(x, y):
    return x @ y

把Pandas用于实际工作的一个例子是创建基于数组的 CORREL 公式。Excel版的 CORREL 必须用在2个数据集上。Pandas使得创建数组相关的 CORREL2 公式就比较简单:

import xlwings as xw
import pandas as pd
@xw.func
@xw.arg('x', pd.DataFrame, index=False, header=False)
@xw.ret(index=False, header=False)
def CORREL2(x):
    return x.corr()

其中header和index的含义如下图所示: 

>>> import pandas as pd
>>> df = pd.DataFrame([[1,2,3],[4,5,6],[7,8,9]],columns=['a','b','c'])
>>> sheet['A1'].value = df
>>> sheet['A1'].options(pd.DataFrame, expand='table').value
       a    b    c
0.0  1.0  2.0  3.0
1.0  4.0  5.0  6.0
2.0  7.0  8.0  9.0

前面使用Excel的数组公式的时候,要预先通过选定结果区域来确定范围,然后输入公式,最后敲组合键 Ctrl-Shift-Enter 。这在实际操作中常常弄得很复杂,特别是在处理与时间序列相关的动态数组时更是如此。xlwings提供了动态UDF扩展,下面这个简单的例子展示了UDF扩展的语法和效果:

import numpy as np

@xw.func
@xw.ret(expand='table')
def dynamic_array(r, c):
    return np.random.randn(int(r), int(c))

2. 装饰器@xw.arg和@xw.ret

a装饰器之于UDF就如options方法之于Range对象:他们允许对函数的参数(@xw.arg) 和返回值(@xw.ret)使用转换器和各种选项。比如,要把参数x转换成pandas DataFrame并且在返回时抑制索引,可以这样做:rg

@xw.func
@xw.arg('x', pd.DataFrame)
@xw.ret(index=False)
def myfunction(x):
   return x

默认情况,@xw.func传递的参数只是单元格或区域的数值,要传递Range,需要设置@xw.arg:

@xw.func
@xw.arg("data",xw.Range)
def MySolver(data):    
    if data.formula:
        return data.formula
    else:
        return "Not Found"

(1)装饰器@xw.ret 的transpose参数实现读写转置,比如,我们可以把一个列表在Excel中写为一列:

>>>sheet['A1'].options(transpose=True).value = [1, 2, 3]

@xw.arg('x', transpose=True)
@xw.ret(transpose=True, expand=’table’)
def myfunction(x):
    return x

 (2)expand参数的功能和区域的 table , vertical 及 horizontal 属性一样,只是在区域取值的时候才去求值: 

>>> import xlwings as xw
>>> sheet = xw.Book().sheets[0]
>>> sheet['A1'].value = [[1,2], [3,4]]
>>> range1 = sheet['A1'].expand()
>>> range2 = sheet['A1'].options(expand='table')
>>> range1.value
[[1.0, 2.0], [3.0, 4.0]]
>>> range2.value
[[1.0, 2.0], [3.0, 4.0]]
>>> sheet['A3'].value = [5, 6]
>>> range1.value
[[1.0, 2.0], [3.0, 4.0]]
>>> range2.value
[[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]

(3)numbers参数:单元格里的数字,缺省作为 float 类型,不过也可以转换为 int 类型 

>>> sheet['A1'].value = 1
>>> sheet['A1'].value
1.0
>>> sheet['A1'].options(numbers=int).value
1

或者,可以指定为其他函数或类型,只要它们关键字参数和float类型的相同即可。用在UDF中时是这样的:

@xw.func
@xw.arg('x', numbers=int)
def myfunction(x):
    # all numbers in x arrive as int
    return x

 3. 自定义公式UDF中的字符串

下面的例子演示了怎样在函数中加上函数及参数x、y的文档字符串,这些文档字符串能在Excel的函数向导里面显示出来:

import xlwings as xw
@xw.func
@xw.arg('x', doc='This is x.')
@xw.arg('y', doc='This is y.')
def double_sum(x, y):
    return 2 * (x + y)

4. UDF的caller参数

通常需要知道哪个单元格调用UDF。为此,xlwings 提供了保留参数,该参数将调用单元格作为 xlwings 范围对象返回:caller。

@xw.func
def get_caller_address(caller):
    return caller.address

 5. UDF中的VBA关键字

通过使用关键字,可以以 pywin32 对象的形式访问任何 Excel VBA对象。例如,如果要以sheet对象的形式传递CodeName,可以按如下方式执行:

arg('sheet', vba='Sheet4') #第4个sheet传递给参数sheet
def get_name(sheet):
    return sheet.Name

 6. @xw.sub装饰器新建宏

Windows系统中除了通过RunPython调用创建宏,还可以使用@xw.sub装饰器创建宏。

import xlwings as xw
@xw.sub
def my_macro():
    """Writes the name of the Workbook into Range("A1") of Sheet 1"""
    wb = xw.Book.caller()
    wb.sheets[0].range('A1').value = wb.name

 

当点击“Import Functions”后,就可以使用这个宏了,通过Alt+F8执行或者将其绑定在一个按钮上。要执行后者,请确保开启“开发工具”选项卡。然后,在选项卡下,可以插入一个按钮,绘制按钮后,系统将提示为其分配宏,可以选择这个宏。

需要主要的是:@xw.func定义的UDF作为用户自定义方程,只能在调用单元格(及@xw.ret定义的expand区域)输入函数返回值,并不能在其它单元格写入数值。要对所有单元格自由读写,较好的办法是@xw.sub定义的宏来实现或在py文件中直接读写,但是都无法传递参数。

7. xlwings.Book().sheets[].expand()的用法

xlwings库中的expand方法用于扩展Excel单元格范围。该方法可以接受一个参数,用于指定扩展的方向。参数可以是字符串或整数。当参数为字符串时,可以取以下几种值:

table:读取整个表格中的数据,包括表头和数据区域。

down:从指定单元格向下读取数据,直到遇到第一个空白行为止。

right:从指定单元格向右读取数据,直到遇到第一个空白列为止。

>>> sheet['a1'].value = [['yisl','wll','yhw'],[42,45,13],['A','B','C']]
>>> sheet['a1'].expand('down').value
['yisl', 42.0, 'A']
>>> sheet['a1'].expand('right').value
['yisl', 'wll', 'yhw']

 8. 在Excel中插入Matplotlib图表

>>> import matplotlib.pyplot as plt
>>> fig = plt.figure()
>>> plt.plot([1,2,3,4,5])
[<matplotlib.lines.Line2D object at 0x000001C9925F5F50>]
>>> sheet.pictures.add(fig, name="Matplotlib Fig", update=True)
<Picture 'Matplotlib Fig' in <Sheet [pendulum_correction.xlsm]test>>

通过使用 pictures.add(),能很容易地把Matplotlib图表当作图片贴进Excel中。

import matplotlib.pyplot as plt
import xlwings as xw

fig = plt.figure()
plt.plot([1, 2, 3])

sheet = xw.Book().sheets[0]
sheet.pictures.add(fig, name='MyPlot', update=True)

如果设置 update=True,就可以做到Excel里移动和缩放图表后,后续调用 pictures.add() 时如果图表名称相同('MyPlot'),会更新图表,不改变它的位置和大小

用 RunPython 调用上面的代码并把它绑定到一个按钮上是件水到渠成的事情,并且能够跨平台工作。不过,在Windows系统上可以通过下面的代码来定义一个 UDF 使得集成度更高:

@xw.func
def myplot(n, caller): #caller参数,调用单元格
    fig = plt.figure()
    plt.plot(range(int(n)))
    caller.sheet.pictures.add(fig, name='MyPlot', update=True)
    return 'Plotted with n={}'.format(n)

导入这个UDF函数并在B2上调用它,图表会随着B1的值而变化:

 大小、位置和其他属性可以通过 pictures.add() 的参数设定,也可以通过对返回的图片对象进行操作。

import matplotlib.pyplot as plt
import xlwings as xw
@xw.func
def myplot(n, caller):
    fig = plt.figure()
    plt.plot(range(int(n)))
    sht = caller.sheet
plot = sht.pictures.add(fig, name='MyPlot', update=True, 
left=sht.range("A3").left, top=sht.range("A3").top)
    plot.height /= 2 #高度缩小50%
    plot.width /= 2 #宽度缩小50%
    return 'Plotted with n={}'.format(n)

9. 在VBA编辑器中用RunPython调用Python创建宏

打开Excel的开发工具菜单的VBA编辑器(Alt+F11),可以在VBA模块中写入如下代码:

Sub HelloWorld()
    RunPython "import hello; hello.world()"
End Sub

 上面的代码会调用hello.py中的内容:

# hello.py
import xlwings as xw
def world():
wb = xw.Book.caller()
sheet = wb.sheets['test']
sheet["A1"].value = "Writing OK!"

10. 在VBA中调用UDF

导入的函数同样可以在VBA中使用。例如,一个返回2维数组的函数:

Sub MySub()
Dim arr() As Variant
Dim i As Long, j As Long
    arr = my_imported_function(...)
    For j = LBound(arr, 2) To UBound(arr, 2)
        For i = LBound(arr, 1) To UBound(arr, 1)
            Debug.Print "(" & i & "," & j & ")", arr(i, j)
        Next i
    Next j
End Sub

11. 使用Jupyter实现和Excel的交互:xlwings.view(),xlwings.load() 

使用Jupyter笔记将Excel用作交互式数据查看器或暂存器,从中可以加载数据帧。

 xlwings.view()函数:视图函数几乎接受任何感兴趣的对象,无论是数字、字符串、嵌套列表、NumPy 数组还是 pandas DataFrame。

xlwings.load()函数:要在 Excel 工作表中的某个范围内加载为 pandas DataFrame,请使用该函数,如果您只选择一个单元格,它将自动扩展以覆盖整个范围。但是,如果选择大于一个单元格的特定范围,则该范围将仅加载到所选单元格中。如果 Excel 中的数据没有索引或标题,请将它们设置为如下所示: 

12.  在VBA中使用RunPython向Python脚本传递参数

在Excel的VBA中,定义sub函数和main函数:

Sub HelloWorld(ArrayAddress As String)
    RunPython "import hello; hello.world('" & ArrayAddress & "')"
End Sub

Sub HelloMain()
    HelloWorld "A1:B3"
End Sub

 通过ArrayAddress传递操作单元格地址,在hello.py文件中定义world函数:

import xlwings as xw
import numpy as np

def world(ArrayAddress):
    wb = xw.Book.caller()
    sheet = wb.sheets.active
    data = sheet[ArrayAddress].options(np.array,ndim=2)
    for row in data:
        for cell in row:
            cell.value = "Cell {} is OK!".format(cell.address)

  • 21
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值