关于excel的一些操作

以下这写代码是把公司的excel报表的原数据转变成需要的excel数据的过程。
def stepone2725(tuple,data_path,dict_fen):
    for i in tuple:
        '''
            下面这段代码也可以用"list(iter(data_path.rglob(f'{i}果蔬销售源报表.xlsx')))[0]"
            由于"data_path.rglob(f'{i}果蔬销售源报表.xlsx')"在此处的结果只有一个,所以将其转换成
            列表加下标进行提取,但是绝不可用"data_path.rglob(f'{i}果蔬销售源报表.xlsx')[0]",
            因为"data_path.rglob(f'{i}果蔬销售源报表.xlsx')"只是个对象,对象没有下标。如果非要迭代
            可以用"for f_dir in data_path.rglob(f'{i}果蔬销售源报表.xlsx'):",这样会在语句上较
            为麻烦。
        '''
        for f_dir in data_path.rglob(f'{i}果蔬销售源报表.xlsx'):
            file=pd.read_excel(f_dir)#读取文件
            file_fen = file.groupby(by="商品分类编码")#按字段“商品分类编码”进行分组
            for name,group in file_fen:#通过遍历分组文件,分别提取两个类别(蔬菜、水果)并分别创建文件夹
                p = Path(r'G:\生鲜每月数据分析\数据源\%s\%s' % (f_dir.parts[3],dict_fen[name]))#指定路径
                p.mkdir(parents=True,exist_ok=True)#创建路径,如果找不到要建文件夹的上级则直接新建,如果新建的文件夹已存在也不会报错
                print(f'正在生成{dict_fen[name]}报表\n请稍后...')
                group.to_excel(p/Path(i+"%s销售源报表.xlsx" % dict_fen[name]),index=0)#将分组后的DataFrame对象写入指定路径中
def concat_excel(data_path):
    '''
    这段代码是将存放在"./数据源/2023年数据源"下面的"水果"和"蔬菜"文件夹和
    "./数据源/2024年数据源"下面的"水果"和"蔬菜"文件夹里的“*销售源报表.xlsx”
    通过pd.concat()函数拼接起来,并增加'日期起止'列,生成文件“*对比表.xlsx”
    存放在“./数据源/合并分析”下面以日期起止格式命名的文件夹里。
    '''
    for i in "蔬菜","水果":#通过外for循环,分别从"水果"和"蔬菜"文件夹下面提取文件
        file_list = []#用于存放要拼接的DataFrame对象
        date_list=[]#用于存放日期字符,用于生成文件路径
        #通过遍历在要查找的文件路径加入日期索引变量date_indexes_tuple
        for t in tuple:
            '''
                用于生成date_list(日期拼接字符串列表)和file_List(用于拼接的文件对象列表)
                用于concat_excel函数的内循环
             '''
            for file_dir in data_path.rglob(f'./{i}/{t}{i}*.xlsx'):
                file = pd.read_excel(file_dir)  # 读入文件
                date_list.append("-".join(re.findall(r'\d+', str(file_dir))[1:4]) + "至" + "-".join(
                    re.findall(r'\d+', str(file_dir))[4:]))  # 通过re模块的findall,找出数字在进行拼接和切片提取,再用"-".join
                # 拼接后生成日期字符串,添加到date_list列表中
                # 插入'日期起止'列,用列表推到式生成以'部门编码'列长度一致的日期字符串列表作为,新列'日期起止'的值value
                file.insert(loc=0, column='日期起止',
                            value=["-".join(re.findall(r'\d+', str(file_dir))[1:4]) + "至" + "-".join(
                                re.findall(r'\d+', str(file_dir))[4:]) for i in range(len(file['部门编码']))])
                file_list.append(file)  # 将DataFrame对象添加到列表file_list里
        file_mer=pd.concat(file_list)#拼接

        pth_x = data_path/Path('合并分析/%s'% "__".join(date_list))#按日期字符串生成路径
        pth_x.mkdir(parents=True,exist_ok=True)#创建路径,如果找不到要建文件夹的上级则直接新建,如果新建的文件夹已存在也不会报错
        print(f'正在生成{pth_x.name}文件夹\n请稍后...')
        file_mer.to_excel(pth_x/Path('%s%s对比表.xlsx'%(i,"和".join(date_list))),index=0)#将新DataFrame写入新路径
def creat_pivot(tuple,st_dic,data_path):
    '''
    这段代码是把存放在“./数据源/合并分析”下面以日期起止格式命名的文件夹里的文件“*对比表.xlsx”
    通过pd.pivot_table()分别以'1':"部门名称",'2':"商品名称"为行标,以values=['销售金额','销售毛利','毛利率']
    为值,以columns='日期起止'为列标创建数据透视表,并以"*透视表*.xlsx"文件名格式存放在“./数据源/合并分析”文件加下
    '''
    for i in data_path.rglob("*透视表*.xlsx"):#先把文件夹下已存在的透视表文件删除,这里必须是rglob逐级遍历才行
        i.unlink(missing_ok=True)
    for st in range(1,3):#外循环用range生成字典的键
        for i in data_path.rglob(f'*{tuple[0]}和{tuple[1]}和{tuple[2]}对比表.xlsx'):#获取指定日期索引的文件路径
            file=pd.read_excel(i)#向内存读入获取的对比表路径
            #生成透视表
            f=pd.pivot_table(file,values=['销售金额','销售毛利','毛利率'],index=st_dic[str(st)],columns='日期起止',aggfunc={'销售金额':'sum','销售毛利':'sum','毛利率':'mean'})
            print(f'正在生成{st_dic[str(st)][:2]}透视表{i.stem[:-3]}\n请稍后...')
            #将获取的透视表写入指定的路径
            f.to_excel(r'G:\生鲜每月数据分析\数据源\合并分析\%s透视表%s.xlsx'%(st_dic[str(st)][:2],i.stem[:-3]))

def format_excel(tuple, data_path):#初步格式化,是透视表更具有表格的性质
    '''
    这段代码主要是将"./数据源/合并分析"下面的临时透视表格式后存入
    "./数据源/合并分析/格式过分析表"
    '''
    print('初步格式过程,请稍后...')
    app = xw.App(visible=False, add_book=False)  # 启动excel程序,与app.quit()相对应,有启动就有退出,否则会出错
    format_file_dir = data_path / Path('合并分析/格式过分析表') / "__".join(tuple)  # 格式后文件存储的上一级路径"*__*"

    for i in data_path.rglob('*透视表*%s和%s和%s.xlsx' % (tuple[0],tuple[1],tuple[2])):
        workbook = app.books.open(i)
        sheet = workbook.sheets[0]
        columns=sheet.range('a2').expand('table').shape[1]#获取sheet总列数
        area = sheet.range((1,2),(1,columns))#指定数据区域的第一行
        area.unmerge()#主要是值('销售金额','销售毛利','毛利率')字段取消合并
        # 用for循环分别从zip()函数对象中分别取出例如(2,(‘b2:c2’)),(4,(‘d2:e2’)),(6,(‘f2:g2’))
        # 这样就可以让sheet.range('b2:c2')两个单元格分别加上sheet.range(1,2)也就是“b1”的值
        # 这样就可以让sheet.range('d2:e2')两个单元格分别加上sheet.range(1,4)也就是“d1”的值
        # 这样就可以让sheet.range('f2:g2')两个单元格分别加上sheet.range(1,6)也就是“f1”的值
        for f, g in zip(range(2, columns, 3), (list(range(2,columns+1))[:len(list(range(2,columns+1)))//3], list(range(2,columns+1))[len(list(range(2,columns+1)))//3:-len(list(range(2,columns+1)))//3], list(range(2,columns+1))[-len(list(range(2,columns+1)))//3:])):
            for a in g:
                sheet.range(2,a).value = str(sheet.range(2,a).value) + str(sheet.range(1, f).value)  # 使“日期起止”行的每一列标都加上值的列标也就是
                # '销售金额','销售毛利','毛利率',形成类似与
                # “2024-5-1至2024-5-15销售金额”的列标
        data = sheet.range('b2').expand('right').value
        sheet.range('b3').value = data  # 由于现在的第3行是空行,故把第二行值赋第3行,等待进一步删除多余的行
        format_file_dir.mkdir(parents=True, exist_ok=True)  # 创建路径,如果找不到要建文件夹的上级则直接新建,如果新建的文件夹已存在也不会报错
        format_filename = Path("格式表" +i.name[:2] +  i.name[5:])  # 构建要保存的文件名格式“*格式表*.xlsx”
        workbook.save(format_file_dir / format_filename)  # 保存文件
    app.quit()  # 退出excel,很重要哦,不然下面的程序会出错哦。
    # 下面会多次出现“for i in format_file_dir.rglob('*格式表*%s和%s.xlsx'%(tuple[0],tuple[1])):”
    # 原因是因为rglob()不可重复迭代,对已经初步格式过的文件“*格式表*.xlsx”继续格式。
    for i in format_file_dir.rglob(f'格式*{tuple[0]}和{tuple[1]}和{tuple[2]}.xlsx'):
        file = pd.read_excel(i, header=2, index_col=0)  # 读入文件,列标签以第3行命名,行标签以第一列命名
        file.insert(loc=0, column='销售同比', value=None)
        file.insert(loc=0, column='销售环比', value=None)
        file.insert(loc=0, column='毛利同比', value=None)
        file.insert(loc=0, column='毛利环比', value=None)  # 在第一列(不是行标签列)前先后插入“销售同比”,“毛利同比”两列
        fil = file.fillna(value=0)  # 以零填充所有缺失值,这里要用到“pd.set_option('future.no_silent_downcasting', True)”
        # 否则将不能兼容
        p = ~fil[fil.columns[-1]].isin([0])  # 从最后一列(本期‘销售金额’列)查找所有不是零的数据,赋给p
        fil = fil[p]  # 将过滤掉零的数据重新赋给fil
        fil.sort_values(by=fil.columns[-1], ascending=False, inplace=True)  # 按本期‘销售金额’降序排序
        fil.to_excel(i, columns=list(fil.columns)[::-1])  # 写入excel,列标签是原来列标签的反转,也就是反向排序。用到了list[::-1]的语法
def fomula_cell():#给最后几列添加公式
    """
    此函数是为了在最后几列添加公式
    """
    format_file_dir = data_path / Path('合并分析/格式过分析表') / "__".join(tuple)  # 格式后文件存储的上一级路径"*__*"
    app = xw.App(visible=False, add_book=False)  # 重新启动excel,继续格式,很重要哦,因为刚才已经退出,所以必须重新启动
    for i in format_file_dir.rglob(
            f'格式*{tuple[0]}和{tuple[1]}和{tuple[2]}.xlsx'):  # 因为rglob()不可重复迭代,对已经初步格式过的文件“*格式表*.xlsx”继续格式。
        print(f'正在给{i.name}添加公式\n请稍等...')
        workbook = app.books.open(i)
        worksheet = workbook.sheets[0]
        # 下面是指定以列为单位的单元格区域
        col_b = worksheet.range('b2').expand('down')
        col_c = worksheet.range('c2').expand('down')
        col_d = worksheet.range('d2').expand('down')
        col_e = worksheet.range('e2').expand('down')
        col_f = worksheet.range('f2').expand('down')
        col_g = worksheet.range('g2').expand('down')
        col_h = worksheet.range('h2').expand('down')
        col_i = worksheet.range('i2').expand('down')
        col_j = worksheet.range('j2').expand('down')
        col_k = worksheet.range('k2').expand('down')
        col_l = worksheet.range('l2').expand('down')
        col_m = worksheet.range('m2').expand('down')
        col_n = worksheet.range('n2').expand('down')


        # 下面这段代码是分别在h--n列写入公式,用到了f-sting语法,他的优点是无须事先转换数据类型就能
        # 将不同的数据类型拼接成字符串。f-sting的基本格式是用“F”或“f”引领的字符串,然后在字符串中用大括号{}
        # 标明要拼接的变量。
        for b, c, d, e, f, g, h, i,j,k,l,m,n in zip(col_b, col_c, col_d, col_e, col_f, col_g, col_h, col_i,col_j,col_k,col_l,col_m,col_n):
            d_addr = d.get_address(row_absolute=False, column_absolute=False)
            b_addr = b.get_address(row_absolute=False, column_absolute=False)
            c_addr = c.get_address(row_absolute=False, column_absolute=False)
            e_addr = e.get_address(row_absolute=False, column_absolute=False)
            f_addr = f.get_address(row_absolute=False, column_absolute=False)
            g_addr = g.get_address(row_absolute=False, column_absolute=False)
            h.formula = f'=text(IFERROR({e_addr}/{b_addr},0),"0.00%")'
            i.formula = f'=text(IFERROR({f_addr}/{c_addr},0),"0.00%")'
            j.formula = f'=text(IFERROR({g_addr}/{d_addr},0),"0.00%")'
            k.formula = f'=TEXT(IFERROR(({b_addr}-{d_addr})/{d_addr},0),"0.00%")'
            l.formula = f'=TEXT(IFERROR(({b_addr}-{c_addr})/{c_addr},0),"0.00%")'
            m.formula = f'=TEXT(IFERROR(({e_addr}-{g_addr})/abs({g_addr}),0),"0.00%")'
            n.formula = f'=TEXT(IFERROR(({e_addr}-{f_addr})/abs({f_addr}),0),"0.00%")'
        workbook.save()
    app.quit()
    """
    下面这几句代码没有什么用处
    """
    # last_cell = worksheet.used_range.last_cell
    # insert_row_index = last_cell.row+1
    # #在下方插入一行
    # worksheet.range((insert_row_index, 1)).api.EntireRow.Insert()
def insert_newrow():
    """
    此函数是为了在表格结尾插入汇总行
    """
    format_file_dir = data_path / Path('合并分析\格式过分析表') / "__".join(tuple)  # 格式后文件存储的上一级路径"*__*"
    for i in format_file_dir.rglob(
            f'格式*{tuple[0]}和{tuple[1]}和{tuple[2]}.xlsx'):  # 因为rglob()不可重复迭代,对已经初步格式过的文件“*格式表*.xlsx”继续格式。
        print(f'正在给{i.name}添加汇总行\n请稍等...')
        file=pd.read_excel(i)
        sum_list=['汇总']
        for col in file.columns[1:]:
            if file[col].dtype == 'float64':
                sum_list.append(file[col].sum())
            else:
                sum_list.append(0)
        file.loc['汇总']=sum_list
        file.to_excel(i,index=0)

def formatworkbook():
    app = xw.App(visible=False, add_book=False)  # 启动excel程序,与app.quit()相对应,有启动就有退出,否则会出错
    format_file_dir = data_path / Path('合并分析\格式过分析表') / "__".join(tuple)  # 格式后文件存储的上一级路径"*__*"
    for i in format_file_dir.rglob(
            f'格式*{tuple[0]}和{tuple[1]}和{tuple[2]}.xlsx'):  # 因为rglob()不可重复迭代,对已经初步格式过的文件“*格式表*.xlsx”继续格式。
        print(f'正在给{i.name}终极格式化\n请稍等...')
        workbook = app.books.open(i)
        worksheet = workbook.sheets[0]
        worksheet.autofit()  # 自动调节sheet行高列高
        color_dict={2:'#FF0000',0:'#808080',1:'#0000FF'}
        for i in range(2,11):
            worksheet.range(1,i).color=color_dict[i%3]#按列号填充颜色
        for i in range(11,15):
            worksheet.range(1,i).color=color_dict[i%2]#按列号填充颜色
        area1 = worksheet.range('a1').expand('right')
        area2 = worksheet.range('a2').expand('table')  # 指定两个单元格区域
        worksheet.range('a1').color = '#008000'  # 设置a1单元格的填充色为绿色
        area1.api.WrapText = True  # 设置自动换行
        # 分别设置area1区域的字体、字号、字体粗细、字体颜色
        area1.font.name = '微软雅黑'
        area1.font.size = 11
        area1.font.bold = True
        area1.font.color = '#FFFF00'
        # 分别设置area2区域的字体、字号、字体粗细、字体颜色
        area2.font.name = '微软雅黑'
        area2.font.size = 9
        area2.font.bold = False
        area2.font.color = '#000000'
        a1 = worksheet.range('a1').expand('table')
        a1.api.HorizontalAlignment = -4108  # 设置水平居中
        a1.api.VerticalAlignment = -4108  # 设置垂直居中
        # 通过循环设置上下左右边框,十字边框
        for i in range(7, 13):
            b = a1.api.Borders(i)
            if i < 11:  # 当i值小于11,设置外边
                b.LineStyle = 1  # 设置线形为实线
                b.Weight = 4  # 设置粗细为最粗
            else:
                b.LineStyle = -4115  # 设置内边,线形为虚线
                b.Weight = 2  # 设置粗细为“细”
                b.Color = rgb_to_int((255, 0, 255))  # 设置内边颜色为紫色
        for col in a1.columns:
            col.column_width = 19  # 设置每一列的宽距
        for row_int in range(1, len(area2.rows) + 1):
            if row_int % 2 == 0:
                # 设置偶数行的填充颜色,这样更能在视觉上更加清楚的看数据
                a1.rows[row_int].color = rgb_to_int((199, 237, 204))
        workbook.save()  # 分别保存工作簿
    app.quit()  # 退出excel
def concat_formatexcel(tuple,pth):
    '''
    这段代码是将两张格式表用pd.concat()合并后,生成新表
    但,这段代码更精确,因为多了
     if "蔬菜" in dir_sp.stem:
            area_sp.copy(destination=area_sc)
        else:
            area_sp.copy(destination=area_sg)
    该段代码进行验算
    '''
    #分别提取部门格式表和商品格式表,进行合并
    app=xw.App(visible=False,add_book=False)#加载excel
    for dir_bm,dir_sp in zip(pth.rglob(f'格式表部门*{tuple[0]}和{tuple[1]}和{tuple[2]}.xlsx'),pth.rglob(f'格式表商品*{tuple[0]}和{tuple[1]}和{tuple[2]}.xlsx')):
        #指定新的生成文件的保存路径p

        p=dir_bm.parent/Path(str(dir_bm.stem[5:])+'合成分析表'+str(dir_bm.suffixes[-1]))
        workbook2=app.books.add()#利用excel创建workbook2工作簿用于创建合并的文件
        workbook1=app.books.open(dir_bm)#打开部门格式表,创建workbook1工作簿
        worksheet1=workbook1.sheets[0]#指定worksheet1
        worksheet2 = workbook2.sheets[0]#指定目标worksheet2
        worksheet1.copy(after=worksheet2)#首先将部门格式表worksheet1复制到目标worksheet2后面
        worksheet2.delete()#再将worksheet2目标sheet删除
        worksheet2 = workbook2.sheets[0]#在次复制给目标sheet2,此时worksheet2已经有了部门格式表的内容了
        workbook_sp=app.books.open(dir_sp)#打开商品格式表
        worksheet_sp=workbook_sp.sheets[0]#指定商品格式表的worksheet_sp
        area_sp=worksheet_sp.range('a1').expand('table')#指定商品格式表sheet所以数据的单元格区域
        area_2=worksheet2.range('a1').expand('down').last_cell.offset(row_offset=1)#指定目标粘贴区域的第一个空白单元格
        area_sp.copy(destination=area_2)#执行赋值操作
        #分别保存工作簿
        workbook1.save(dir_bm)
        workbook_sp.save(dir_sp)
        workbook2.save(p)
    app.quit()#退出excel
    print(f'合并文件完成\n报表已全部生成,生成报表为:')
    for i in p_end.rglob('*.xlsx'):
        print(i.name)
if __name__ == "__main__":
    from pathlib import Path  # 导入Path 模块
    import re  # 导入re模块
    import xlwings as xw  # 导入xlwings模块
    from xlwings.utils import rgb_to_int  # 导入xlwings.utils模块下的颜色转换函数rgb_to_int
    import pandas as pd  # 导入pandas模块
    pd.set_option('future.no_silent_downcasting', True)  # 向下兼容,因为填充缺失值这句代码fil=file.fillna(value=0)
    st_dic = {'1': "部门名称", '2': "商品名称"}  # 创建用于遍历的字典,作为生成透视表的行字段
    data_path = Path(r'G:\生鲜每月数据分析\数据源')  # 查询的数据源路径

    # 创建tuple元组用于存储输入的查询日期
    # tuple = input('请输入去年同期的起止日期(格式:"YYYY-MM-DD至YYYY-MM-DD")'), \
    #         input('请输入本年度(本期)的起止日期(格式:"YYYY-MM-DD至YYYY-MM-DD")'), \
    #         input('请输入本年度(上期)的起止日期(格式:"YYYY-MM-DD至YYYY-MM-DD")')
    tuple=[]#其实应该是元组,但因为后来改进的不想把代码中的变量都得改过来,所以tuple=[],有点好笑
    for item in open(data_path/'date_list.txt',encoding='utf-8').readlines():#读取日期文本
        tuple.append(item.strip())#strip()去除空格和换行符

    #这里的date_indexex_tuple有2个元素分别是起始日和结束日
    date_indexes_tuple = (tuple[0][8:10], tuple[0][-2:])
    dict_fen = {25: '水果', 27: '蔬菜'}  # 创建字典用于分别构建文件夹,当然这里也可以不指定字典,
    # 可以在分组的时候直接用group中的name名称
    # 这样就可以分出更多的科别,而不会报
    pth = data_path / Path(r'合并分析\格式过分析表')  # 创建查找路径
    p_end=pth/Path("__".join(tuple))#这是最后遍历结果文件的路径
    stepone2725(tuple, data_path, dict_fen)#将果蔬分开
    concat_excel(data_path)#将本期,上期,去年同期文件分别合并
    creat_pivot(tuple, st_dic, data_path)#创建透视表
    format_excel(tuple, data_path)#初步格式化透视表
    insert_newrow()#插入汇总行
    fomula_cell()#在组后几列插入公式
    formatworkbook()#再一次格式化
    concat_formatexcel(tuple, pth)#合并部门和商品两张表格

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值