Python、Pandas:如何在MultiIndex的情况下向Excel文件添加格式?

anauzrmj  于 2023-04-04  发布在  Python
关注(0)|答案(1)|浏览(128)

我创建了一个有多个索引的数据框,(3个标题和两个索引)我使用.to_excel()方法将其导出到Excel,第一个屏幕截图:before
我想改变颜色,看起来像第二个屏幕截图:after
我如何才能实现我正在寻找的东西?我很感激你能提供的任何帮助!我在其他网站上找到了一些帮助,但不是MultiIndex。下面是我的代码示例:

import xlsxwriter
from xlsxwriter import Workbook
import pandas as pd
import numpy as np
from numpy import *
import ctypes
from openpyxl.workbook import Workbook
from docxtpl import DocxTemplate

laenge=['50','75','100','125','150','175','200','225','250','275','300']
TM=["S", "2TM", "3TM", "4TM", "5TM", "6TM", "8TM", "10TM", "12TM"]

spannung=['400V','200V']
MotorenList=["200HX","200UHX" ,"240HX", "310HX", "360UHX","564HX"]
DataToCalculat=['M_N','M_0','n_MAX','n_N','I_N','I_p','I_0']

columns=[np.array(spannung),np.array(MotorenList),np.array(DataToCalculat)]
columnsList=pd.MultiIndex.from_product(columns,names=['Voltage:','Motor:',''])

rows=[np.array(laenge),np.array(TM)]
indexList=pd.MultiIndex.from_product(rows,names=['h','TM'])

df=pd.DataFrame(np.random.randn(indexList.shape[0],columnsList.shape[0]),index=indexList, columns=columnsList)

writer= pd.ExcelWriter('test2.xlsx', engine='xlsxwriter')

for motor in MotorenList:
    
    if motor=='200HX' or motor=='200UHX':
        sheetName='RM 200'
        df_400V=pd.DataFrame(df[['400V']].xs(motor,level='Motor:',axis=1,drop_level=False))
        df_200V=pd.DataFrame(df[['200V']].xs(motor,level='Motor:',axis=1,drop_level=False))
        if motor=='200UHX':
            df_400V.to_excel(writer,sheet_name=sheetName,startrow=0,startcol=df_400V.shape[1]+3)
            df_200V.to_excel(writer,sheet_name=sheetName,startrow=df_400V.shape[0]+5,startcol=df_400V.shape[1]+3)
        else:
            df_400V.to_excel(writer,sheet_name=sheetName,startrow=0,startcol=0)
            df_200V.to_excel(writer,sheet_name=sheetName,startrow=df_400V.shape[0]+5,startcol=0)

    elif motor=='240HX' or motor=='240UHX':
        sheetName='RM 240'
        df_400V=pd.DataFrame(df[['400V']].xs(motor,level='Motor:',axis=1,drop_level=False))
        df_200V=pd.DataFrame(df[['200V']].xs(motor,level='Motor:',axis=1,drop_level=False))
        if motor=='240UHX':
            df_400V.to_excel(writer,sheet_name=sheetName,startrow=0,startcol=df_400V.shape[1]+3)
            df_200V.to_excel(writer,sheet_name=sheetName,startrow=df_400V.shape[0]+5,startcol=df_400V.shape[1]+3)
        else:
            df_400V.to_excel(writer,sheet_name=sheetName,startrow=0,startcol=0)
            df_200V.to_excel(writer,sheet_name=sheetName,startrow=df_400V.shape[0]+5,startcol=0)
    
    elif motor=='310HX' or motor=='310UHX':
        sheetName='RM 310'
        df_400V=pd.DataFrame(df[['400V']].xs(motor,level='Motor:',axis=1,drop_level=False))
        df_200V=pd.DataFrame(df[['200V']].xs(motor,level='Motor:',axis=1,drop_level=False))
        if motor=='310UHX':
            df_400V.to_excel(writer,sheet_name=sheetName,startrow=0,startcol=df_400V.shape[1]+3)
            df_200V.to_excel(writer,sheet_name=sheetName,startrow=df_400V.shape[0]+5,startcol=df_400V.shape[1]+3)
        else:
            df_400V.to_excel(writer,sheet_name=sheetName,startrow=0,startcol=0)
            df_200V.to_excel(writer,sheet_name=sheetName,startrow=df_400V.shape[0]+5,startcol=0)

    elif motor=='360HX' or motor=='360UHX':
        sheetName='RM 360'
        df_400V=pd.DataFrame(df[['400V']].xs(motor,level='Motor:',axis=1,drop_level=False))
        df_200V=pd.DataFrame(df[['200V']].xs(motor,level='Motor:',axis=1,drop_level=False))
        if motor=='360UHX':
            df_400V.to_excel(writer,sheet_name=sheetName,startrow=0,startcol=df_400V.shape[1]+3)
            df_200V.to_excel(writer,sheet_name=sheetName,startrow=df_400V.shape[0]+5,startcol=df_400V.shape[1]+3)
        else:
            df_400V.to_excel(writer,sheet_name=sheetName,startrow=0,startcol=0)
            df_200V.to_excel(writer,sheet_name=sheetName,startrow=df_400V.shape[0]+5,startcol=0)

    elif motor=='564HX' or motor=='564UHX':
        sheetName='RM 564'
        df_200V=pd.DataFrame(df[['200V']].xs(motor,level='Motor:',axis=1,drop_level=False))
        df_200V=pd.DataFrame(df[['200V']].xs(motor,level='Motor:',axis=1,drop_level=False))
        if motor=='564UHX':
            df_400V.to_excel(writer,sheet_name=sheetName,startrow=0,startcol=df_400V.shape[1]+3)
            df_200V.to_excel(writer,sheet_name=sheetName,startrow=df_400V.shape[0]+5,startcol=df_400V.shape[1]+3)
        else:
            df_400V.to_excel(writer,sheet_name=sheetName,startrow=0,startcol=0)
            df_200V.to_excel(writer,sheet_name=sheetName,startrow=df_400V.shape[0]+5,startcol=0)

writer.close()
neekobn8

neekobn81#

Xlsxwriter无法读取它只写入的现有数据。在to_excel写入后添加填充格式将覆盖现有单元格值,并且您无法在重写回单元格之前读取值。因此,使用Openpyxl可以添加填充而不会覆盖单元格中的现有值。
您可以将引擎更改为Openpyxl,然后将填充合并到代码中,而不是重新加载工作簿/工作表。
下面是一个代码示例;
该代码示例从您的代码导入继续,在现有导入的底部添加PatternFill导入;

from openpyxl.styles import PatternFill

然后添加一个新的函数。只显示两个电机表创建,因为它们保持相同,并在最后添加了函数,如示例所示。
ExcelWriter引擎也改为'openpyxl'

writer = pd.ExcelWriter('test2.xlsx', engine='openpyxl')

新函数fill_colour用于使用Openpyxl设置背景颜色,这将使用Excel工作表在每个“第二个屏幕截图”中以蓝色或灰色填充所有行和列。

fill_colour(writer.sheets[sheetName])

该函数将查找并填充整个工作表,就像有两个电机写入工作表一样。仅当单元格中有值时才会发生填充,因此如果第一个或第二个电机不存在,则不会为第一个或第二个电机填充空单元格。编写代码时,在每次电机写入后使用分配的sheetName调用fill函数,这意味着对于那些有两个电机的工作表,函数被调用两次,并且现有填充的单元格被重新填充。这具有最小的影响,并且似乎只有RM 200有两个电机。但是,函数可以移动,因此只有在写入电机后才能调用。
填充是通过循环列的单元格列表和迭代列的行来实现的。只有一列'B'被迭代,其他列同时使用cell.offset填充。
fill_colour函数使用设置的行和列位置,例如'C'和'M'作为合并的标题单元格,行4-103和108-207作为填充行。大多数其他单元格都是从这些单元格中确定的。
三种填充颜色被使用蓝色,浅棕色和灰色,这些是使用从Openpyxl导入的PatternFill设置的

blueFill = PatternFill(start_color='FF8DB4E2', end_color='FF8DB4E2', fill_type='solid')
grayFill = PatternFill(start_color='FFD9D9D9', end_color='FFD9D9D9', fill_type='solid')
lghtBrwnFill = PatternFill(start_color='FFC4BD97', end_color='FFC4BD97', fill_type='solid')

如果颜色不正确,可以更改十六进制值。

...
from docxtpl import DocxTemplate
from openpyxl.styles import PatternFill  # Included import

### Add fill_colour function
def fill_colour(sheet):
    ### Blue Fill
    col_list = ['C', 'M']
    row_list = [1, 105]
    for l in col_list:
        for j in row_list:
            fill_cell = sheet[f'{l}{j}']
            if fill_cell.value is not None:
                sheet[f'{l}{j}'].fill = blueFill

    ### Gray & Brown Fill
    ### Fill header cells
    head_dict = {4: 103, 108: 207}
    for hrow, v in head_dict.items():
        ### Create list of cells in the Header section to fill
        ### Columns C to S for row 3 or 107 (excludes cols J, K & L)
        head_list = [f'{chr(i)}{hrow - 1}' for i in range(ord('C'), ord('S') + 1)
                     if chr(i) != 'J' and chr(i) != 'K' and chr(i) != 'L']
        ### Background fill each cell in the head_list
        for hcell in head_list:
            gray_cell = sheet[hcell]
            if gray_cell.value is not None:
                gray_cell.fill = grayFill

        ### Add the cells for the 'Motor' row in light  brown
        motor_list = [f'C{hrow-2}', f'M{hrow-2}']
        for mot_cell in motor_list:
            brn_cell = sheet[mot_cell]
            if brn_cell.value is not None:
                brn_cell.fill = lghtBrwnFill

        ### Fill cells in columns A, B and K, L if used
        for row in sheet.iter_rows(min_col=2, max_col=2, min_row=hrow, max_row=v):
            for cell in row:
                if cell.value is not None:
                    cell.fill = grayFill
                col_list = [-1, 9, 10]
                for col in col_list:
                    if cell.offset(column=col).value is not None:
                        cell.offset(column=col).fill = grayFill

blueFill = PatternFill(start_color='FF8DB4E2', end_color='FF8DB4E2', fill_type='solid')
grayFill = PatternFill(start_color='FFD9D9D9', end_color='FFD9D9D9', fill_type='solid')
lghtBrwnFill = PatternFill(start_color='FFC4BD97', end_color='FFC4BD97', fill_type='solid')

laenge = ['50', '75', '100', '125', '150', '175', '200', '225', '250', '275', '300']
TM = ["S", "2TM", "3TM", "4TM", "5TM", "6TM", "8TM", "10TM", "12TM"]

spannung = ['400V', '200V']
MotorenList = ["200HX", "200UHX", "240HX", "310HX", "360UHX", "564HX"]
DataToCalculat = ['M_N', 'M_0', 'n_MAX', 'n_N', 'I_N', 'I_p', 'I_0']

columns = [np.array(spannung), np.array(MotorenList), np.array(DataToCalculat)]
columnsList = pd.MultiIndex.from_product(columns, names=['Voltage:', 'Motor:', ''])

rows = [np.array(laenge), np.array(TM)]
indexList = pd.MultiIndex.from_product(rows, names=['h', 'TM'])

df = pd.DataFrame(np.random.randn(indexList.shape[0], columnsList.shape[0]), index=indexList, columns=columnsList)

### Change engine to 'openpyxl'
writer = pd.ExcelWriter('test2.xlsx', engine='openpyxl')

for motor in MotorenList:

    if motor == '200HX' or motor == '200UHX':
        sheetName = 'RM 200'
        df_400V = pd.DataFrame(df[['400V']].xs(motor, level='Motor:', axis=1, drop_level=False))
        df_200V = pd.DataFrame(df[['200V']].xs(motor, level='Motor:', axis=1, drop_level=False))
        if motor == '200UHX':
            df_400V.to_excel(writer, sheet_name=sheetName, startrow=0, startcol=df_400V.shape[1] + 3)
            df_200V.to_excel(writer, sheet_name=sheetName, startrow=df_400V.shape[0] + 5, startcol=df_400V.shape[1] + 3)
        else:
            df_400V.to_excel(writer, sheet_name=sheetName, startrow=0, startcol=0)
            df_200V.to_excel(writer, sheet_name=sheetName, startrow=df_400V.shape[0] + 5, startcol=0)

        ### Add the fill_colour function, with worksheet created from sheetName
        fill_colour(writer.sheets[sheetName])

    elif motor == '240HX' or motor == '240UHX':
        sheetName = 'RM 240'
        df_400V = pd.DataFrame(df[['400V']].xs(motor, level='Motor:', axis=1, drop_level=False))
        df_200V = pd.DataFrame(df[['200V']].xs(motor, level='Motor:', axis=1, drop_level=False))
        if motor == '240UHX':
            df_400V.to_excel(writer, sheet_name=sheetName, startrow=0, startcol=df_400V.shape[1] + 3)
            df_200V.to_excel(writer, sheet_name=sheetName, startrow=df_400V.shape[0] + 5, startcol=df_400V.shape[1] + 3)
        else:
            df_400V.to_excel(writer, sheet_name=sheetName, startrow=0, startcol=0)
            df_200V.to_excel(writer, sheet_name=sheetName, startrow=df_400V.shape[0] + 5, startcol=0)

        fill_colour(writer.sheets[sheetName])

    elif motor == '310HX' or motor == '310UHX':
        sheetName = 'RM 310'    
...

###The rest of the code is the same as your sample code with the fill_colour function for each Motor as shown in the two above Motors.

更新后的工作表示例,此链接显示第一个工作表的顶部
'RM 200' sheet
openpyxl

相关问题