Python + openpyxl + pandas 一步实现将Excel表中内容按指定条件筛选分成多个子表并保留指定表头内容

文章目录

  • 前言
  • 一、将多个dataframe写入同一个Excel的不同工作Sheet
  • 二、完整地复制粘贴Excel中的某些内容
    • 1.导入模块
    • 2.将列宽对应
    • 3.将行宽对应
    • 4.复制粘贴单元格内容
    • 5.完整代码
  • 三、总结


前言

应用背景:
工作需要将表格按某些条件拆分成多个工作薄,且一个工作薄中再按某些条件存放多个工作表,且保留格式,例如颜色,合并单元格,字体大小等等,主要用于表头的复制时保留格式。
难点:
1.将数据保存到同一工作薄的不同工作表。
2.复制excel表中的某些内容并保留对应的格式。
实现目标:完美地模拟“将一个总表数据,筛选负责人拆分成对应的负责人表,且在每一个表内分成库存和备货两张子表,并对表头进行处理”


一、将多个dataframe写入同一个Excel的不同工作Sheet

多个dataframe需要写入同一个excel时,每次使用df.to_excel(文件名)的形式去写,系统都会重新创建一个新的文件。也就意味着前面的文件会被覆盖掉,你得到的只能是最后一个df写入的结果文件

通过创建一个ExcelWriter对象,可以解决上面的问题

import pandas as pd

writer = pd.ExcelWriter(os.path.join(os.getcwd(), '自定义.xlsx'))
df1.to_excel(writer, sheet_name='自定义sheet_name'#startcol=**, startrow=**)
df2.to_excel(writer, sheet_name='自定义sheet_name'#startcol=**, startrow=**)
df3.to_excel(writer, sheet_name='自定义sheet_name'#startcol=**, startrow=**)
...
 
 
writer.save()# 写入硬盘

#不传sheet_name参数时,默认多个dataframe会写入同一个xlsx文件的同一个sheet里

#startrow, startcol 不传时默认dataframe在excel里在sheet1里其实行列都是一

#需注意,python和excel的索引起始数字不同,如果startrow=2, startcol=4,不是从Excel第二行第四列开始写,而是从Excel第三行第五列开始写
#注意一定要用write.save(),不然生成的文件是没有对应的内容的

原文链接:

https://blog.csdn.net/weixin_42130167/article/details/89705581

二、完整地复制粘贴Excel中的某些内容

上文中使用pandas进行数据处理的时候,输出的表格都是不带格式的,甚至于在读取存在“合并单元格”的数据的时候,还会出现一些难以处理的空值(基于个人理解,excel表格中的合并单元格,显示的值是取第一项的值,其他都只是空值)。
而实际工作中,领导要求的表格都是要好看的格式以及一些表头,颜色等等,因此使用pandas来对特定的表头格式进行完整的复制粘贴。

1.导入模块

import easygui as eg
import os
import openpyxl
import copy
import pandas as pd

2.将列宽对应

代码如下:

def colwidth(s0, s1):
    for i, row in enumerate(s0.iter_cols()):
    	# i = chr(i+97).upper()
        i = convertToTitle(i+1)
        lk = s0.column_dimensions[i].width
        if lk == 0:
            lk = 8.38
        s1.column_dimensions[i].width = lk

其中s0是待复制excel表,s1是待粘贴excel表,都是openpyxl的Worksheet对象,即工作表对象。

enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for循环当中。

openpyxl读取一个sheet所有列数据除了用cols属性,也可以用iter_cols()方法,返回值是生成器,该方法也可以指定读取行和列,和iter_rows不同的是他按照列来输出数据。

综上所述,enumerate(s0.iter_cols())生成的是一个下标对应列的生成器,i默认初始等于0,对应的是’A列’,可通过chr()将数字对应转化为字母(用于下文中column_dimensions的引用)。
但是chr()对于生成”两位数的字母“即不能生成’AA’,而实际上的excel表可能会超过’AA’列,就会产生等同于’超出索引值’效果的报错,不符合实际需求。

>>>i = 0 #生成器生成的列中'A'对应的下标是0
>>>i = chr(i+97).upper()
>>>i
'A'
>>>i = 26
>>>a = chr(i+97).upper()
>>>a
'{'

此时可以看到当i超出25(26个字母,因此最后一项是25)时,对应生成的不是’AA’,而是chr()本身编码中对应的值。
解决方法:

def convertToTitle(n):
	# n为添入的数值,此时1对应的是'A',0无对应,所以在应用时,注意赋予的n是否要+1
    """
    :type n: int
    :rtype: str
    """
    rStr = ""
    while n != 0:
        res = n % 26
        if res == 0:
            res = 26
            n -= 26
        rStr = chr(ord('A') + res - 1) + rStr
        n = n // 26
    return rStr
   # 返回字母

此时,数字下标就和excel表格中的列一一对应上了。

3.将行宽对应

代码如下:

def rowheight(s0, s1):
    for i in range(1, s0.max_row+1):
        hg = s0.row_dimensions[i].height
        if hg == 0:
            hg = 15
        s1.row_dimensions[i].height = hg

行的序号由纯数字组成,不存在字母与数字转化间的问题,但在使用range的时候注意与实际行数的对应关系。

4.复制粘贴单元格内容

def copycell(s0, s1, min_row=None, max_row=None, min_col=None, max_col=None):
    for i, row in enumerate(s0.iter_rows(min_row=min_row, max_row=max_row, min_col=min_col, max_col=max_col)):
        for j, cell in enumerate(row):
            s1.cell(i+1, j+1, cell.value)
            s1.cell(i+1, j+1).number_format = cell.number_format
            s1.cell(i+1, j+1).alignment = copy.copy(cell.alignment)
            s1.cell(i+1, j+1).font = copy.copy(cell.font)
            s1.cell(i+1, j+1).border = copy.copy(cell.border)
            s1.cell(i+1, j+1).fill = copy.copy(cell.fill)

其中,i代表第几列,row代表这一列下的所有单元格cell对象组成元组。通过enumerate(),将row中的所有单元格及其对应的行号列出。
iter_rows()和iter_cols()的参数都有(min_row=None, max_row=None, min_col=None, max_col=None),因此可以通过参数设置来圈定我们想要复制粘贴的区域,例如,我想复制前两行,那我就将max_row=2,以此来达成表头的复制粘贴的效果。

5.完整代码

import easygui as eg
import os
import openpyxl
import copy
import pandas as pd


# 用于数字与excel列字母之间的转换
def convertToTitle(n):
    """
    :type n: int
    :rtype: str
    """
    rStr = ""
    while n != 0:
        res = n % 26
        if res == 0:
            res = 26
            n -= 26
        rStr = chr(ord('A') + res - 1) + rStr
        n = n // 26
    return rStr


# 复制列宽
def colwidth(s0, s1):
    for k, co in enumerate(s0.iter_cols()):
        k = convertToTitle(k+1)
        lk = s0.column_dimensions[k].width
        if lk == 0:
            lk = 8.38
        s1.column_dimensions[k].width = lk


# 复制行宽
def rowheight(s0, s1):
    for i in range(1, s0.max_row+1):
        hg = s0.row_dimensions[i].height
        if hg == 0:
            hg = 15
        s1.row_dimensions[i].height = hg


# 复制单元格
def copycell(s0, s1, min_row=None, max_row=None, min_col=None, max_col=None):
    for i, row in enumerate(s0.iter_rows(min_row=min_row, max_row=max_row, min_col=min_col, max_col=max_col)):
        for j, cell in enumerate(row):
            s1.cell(i+1, j+1, cell.value)
            s1.cell(i+1, j+1).number_format = cell.number_format
            s1.cell(i+1, j+1).alignment = copy.copy(cell.alignment)
            s1.cell(i+1, j+1).font = copy.copy(cell.font)
            s1.cell(i+1, j+1).border = copy.copy(cell.border)
            s1.cell(i+1, j+1).fill = copy.copy(cell.fill)


# 执行复制
def copyfile(sht, sht1):
    colwidth(sht, sht1)
    rowheight(sht, sht1)
    copycell(sht, sht1)
    for merg in sht.merged_cells:
        sht1.merge_cells(str(merg))


# 进行数据处理后复制表头
def screen_data(fn):
    df = pd.read_excel(fn, header=1)
    g = os.path.dirname(fn)
    # 把要复制的表头和要处理的表格放在同一个目录内
    header_file = os.path.join(g, '产品总表_表头.xlsx')
    wb = openpyxl.load_workbook(header_file)
    name_list = list(df['负责人'].drop_duplicates('first'))
    for name in name_list:
        file_name = os.path.join(g, name + '库存及备货表.xlsx')
        writer = pd.ExcelWriter(file_name)
        # 进行数据处理,生成对应的表
        for sheet_name in wb.sheetnames:
            header_df = pd.read_excel(header_file, header=1, sheet_name=sheet_name)
            header = header_df.columns
            mid_df = pd.DataFrame(df[df['负责人'] == name], columns=header)
            mid_df.to_excel(writer, sheet_name=sheet_name, index=False, startrow=1)
        # 一定要先保存并且关闭,否则无法生成有效的excel表
        writer.save()
        writer.close()
        # 进行对应工作薄的各个工作表的表头的复制
        for sheet_name in wb.sheetnames:
            ws = wb[sheet_name]
            wb1 = openpyxl.load_workbook(file_name)
            ws1 = wb1[sheet_name]
            copyfile(ws, ws1)
            wb1.save(file_name)


if __name__ == '__main__':
    ff = eg.fileopenbox('选择文件', '选择文件')
    screen_data(ff)


三、总结

本博文在解决“python 如何将多个DataFrame保存到不同工作表”,“python 如何复制粘贴excel表中的格式到另一个excel表” 这类的问题时应该都能提供些帮助,或者是直接可套用(伸手党)的代码。因为在长期的自学自用的过程中,知道了百度的重要性,更知道了上网找资料时的心态(能直接套用就最好了),大多数时候都不需要了解原理,只需要解决当前问题即可,因此对于关键的功能点都有单独拎出描述,望能帮助读者解决问题。
本人非专业的IT工作人员,各类参数调整与自身实际工作时的应用问题,可在评论中提出,一起讨论解决,但我不一定能解决,请见谅。
这是第一次写一篇正式的博文,如有不足之处,可指出改正,谢谢。

你可能感兴趣的:(python+excel,python,excel)