从多层文件夹的多个excel中统计汇总各个文件夹和各个excel的全部SHEET中的指定数据。
一个文件夹,里面有多个子文件夹和多个excel表格,每个子文件夹里面也有多个子文件夹和多个excel表格,每个excel表格中有若干个SHEET。
每个excel表是每个员工自己制作的表格,其中每个sheet存储着每个月的报销清单,每个文件夹分类存放着每个小组的表格,这些小组的表格又放在他们的部门文件夹里,每个部门的文件夹放在分公司的文件夹里,再按分公司命名后放到总公司的文件夹里。
现在要统计里面的金额和条目数,金额所在的列名,有的叫“(金 额)”,有的叫“B_A.金额”,这种里面只有“金额”两个汉字的要统计,还有的列叫“已报销金额”,这种的不能被统计。
(总之,文件夹很多,层次很深,表格文件很多,可能还有其他非表格文件,SHEET很多,奇葩列名也有)
统计结果要按分公司、部门、小组、员工的层级分组。
实现
我把各个分公司的文件夹放在同一个文件夹中,给这个文件夹起个名字,叫“文件放这里”,然后用python编程来搞定这个统计任务,运行之前保证这个文件夹的所有文件没有被其他软件占用。
import os
import re
import pandas as pd
class 目录树:
def __init__(self):
self.当前目录=''
self.子树=[]
self.表格文件=[]
self.各文件的总金额=[]
self.各文件的总人次=[]
def 构建树(目录):
树 = 目录树()
树.当前目录 = 目录
for i in os.listdir(目录):
path = 目录+os.sep+i
#print('读取目录',path)
if os.path.isdir(path):
树.子树.append(构建树(path))
elif os.path.isfile(path):
if len(i)> 4 and i[-4:]=='.xls'or len(i)> 5 and i[-5:]=='.xlsx':
树.表格文件.append(path)
总金额=0
总人次=0
#读一下表格
for df in pd.read_excel(path,sheet_name=None).values():
cs = df.columns.to_list()
#统计一下金额和人次(条目数)
for i in cs:
if '金额' == ''.join(re.findall("[^A-Za-z0-9_().\s]",i)):
总金额 += df[i].sum()
总人次 += df[i].count()
break
树.各文件的总金额.append(总金额)
树.各文件的总人次.append(总人次)
return 树
def 遍历树(节点,索引表,索引栈,金额列表,人次列表):
#节点入栈
索引栈.append(os.path.basename(节点.当前目录))
#遍历子节点
for 子节点 in 节点.子树:
遍历树(子节点,索引表,索引栈,金额列表,人次列表)
#遍历文件
for i in zip(节点.表格文件,节点.各文件的总金额,节点.各文件的总人次):
索引栈.append(os.path.basename(i[0]).rstrip('.xlsx').rstrip('.xls'))
索引表.append(tuple(索引栈[1:]))
金额列表.append(i[1])
人次列表.append(i[2])
索引栈.pop(-1)
#节点出栈
索引栈.pop(-1)
path = os.getcwd()
l1 = os.listdir(path)
if '文件放这里' in l1:
path = path+os.sep+'文件放这里'
根节点 = 构建树(path)
索引表=[]
索引栈=[]
人次列表=[]
金额列表=[]
遍历树(根节点,索引表,索引栈,金额列表,人次列表)
df = pd.DataFrame(索引表)
df['人次']=人次列表
df['金额']=金额列表
df = df.set_index([0,1,2,3])#分组层级有四层
df#这个df就是我要的结果
把多个excel文件中的SHEET1按其中的一列分组,每一组创建一个以该组的名称命名的文件夹,把每一组的数据放进一个excel表格(文件名还是原来的),再分别放到对应组的文件夹中。
解决方法就像是邮递员送信,把原始数据存放的文件夹(没有子文件夹)和新的文件夹、代码运行目录放在同一目录下,然后运行下面这个即可。有些列可能会从文本变为数字,如果不想改变需要在读取表格时设置converters。
import pandas as pd
import os
分组的列名="什么"
原始表格所在的文件夹='原始数据文件夹'
新表格存放的文件夹 = '新建文件夹'
for i in os.listdir(原始表格所在的文件夹):
df = pd.read_excel('{}{}'.format(原始表格所在的文件夹,os.sep+i),converters={i:str for i in ['必需保留原文的列1的字段名','必需保留原文的列2的字段名']})
a = df[分组的列名].drop_duplicates()
for j in a.values:
k = df[df[分组的列名]==j]
r = k.set_index(df.columns.values[0])
new_dir ='{}{}'.format(新表格存放的文件夹,os.sep+j)
if not os.path.exists(new_dir):
os.mkdir(new_dir)
r.to_excel(new_dir+'{}.xlsx'.format(os.sep+i.split('.')[0]))
合并excel
在逐行读取CSV的时候,遇到换行符会被认为是不同的行,然而,有的数据项本身就存在换行符“\n”……
办法是加一个字段数校验,每次必须读入足够的内容:
with open('a.csv','r') as f:
for line in f:
s = line.split(',')
print(s)
l = len(s)
break
n0=eval('['+input('输入要统计求和的每个统计量的索引,用半角逗号分割:')+']')
sum0=[0 for i in n0]
is_all=True
for line in f:
print(line)
if is_all:
s = line.split(',')
else:
new = line.split(',')
s[-1] = s[-1]+new[0]
s += new[1:]
if len(s)==l:
is_all=True
j=0
for i in n0:
sum0[j]+=eval(s[i])
j+=1
else:
is_all=False
print(sum0)
输出表格的格式怎么修改
参考这个:改to_excel()格式
写成python函数——直接在原有的表格上改变格式:
def format_excel(filepath):
if filepath[-4:].lower()=='.xls' or filepath[-5:].lower()=='.xlsx':
dfs= pd.read_excel(filepath,sheet_name=None)
writer = pd.ExcelWriter(filepath,engine="xlsxwriter")
for sname,df in dfs.items():
for cname in df:
#对长整数转换存储格式,避免精度丢失
if df[cname].dtype == 'int64' and (df[cname]>9999999).any():
df[cname] = df[cname].astype('object')
df.to_excel(writer, sheet_name=sname,index=False)
#格式化输出
workbook = writer.book
worksheets = writer.sheets
#在这里自定义表头格式
header_format = workbook.add_format({'font_size': 9,'border': 0,'bold':True})
##在这里自定义每一列的单元格格式
cell_format = workbook.add_format({'font_size': 9,'border': 0})
for sname, df in dfs.items():
worksheet = worksheets[sname]
#每列改一遍
for col_num, value in enumerate(df.columns.values):
worksheet.write(0, col_num, value, header_format)
worksheet.set_column(col_num,col_num, len(value)*1.6, cell_format)
writer.save()
writer.close()
print('文件“{}”格式化已完成'.format(filepath))
else:
print('文件“{}”不是电子表格'.format(filepath))
(未完待续,更过功能,持续更新)