【python实战】批量合并excel

1.背景

这两天脑子修复ing的时候,把mooc上嵩天老师的《python数据分析与展示》刷了一下,

课程主要讲了numpy、pandas和matplotlib库的入门,

正好这段时间就频繁用到了这几个库对数据进行操作,

之前用的时候都是根据需求边百度边敲(chao)代码的,

刷了下课程视频后,算是有个稍微系统性的认识和了解了,

整理归纳一下,加强下理解,争取下次能徒手写~

2.需求

需要定期整理国内,国外,境外的疫情数据。

这些excel中都有两个sheet,第一个sheet是每个省的,第二个sheet是每个省所有城市的数据,需要将这些数据都合并起来。

此外,还需要将这些数据进行宽数据转长数据的操作。

excels.png

了解到这个需求后,第一反应就是stata和python都是可以操作的。其中,我要是用起stata来会更加顺手。

但是,一想到用stata来合并的话,stata操作的其实是dta数据,并不是excel数据,而且,虽然stata16允许多个dataframe在内存里跑,但是,总觉得会很慢。

抱着学习和试验的心态吧,就用python来进行批量合并excel,结果效果还是不错的,虽然碰到不少坑,花了些时间,但最后跑出结果也就2-3分钟的,还是挺有成就感的。

大概记录一下主要思路和代码。

3. 思路

总的思路来说,就是:

①先将所有excel的第一个sheet的省的数据纵向合并起来,并插入一列字段“地市”;

②再将所有excel的第二个sheet的城市的数据纵向合并起来;

③将省合并的数据和城市合并后的数据进行合并。

④统一将多列字段的宽数据转为字段名,数值的长数据。

4.过程

①导入库

import os,time,re,openpyxl,xlrd
import pandas as pd
import numpy as np

②路径文件选取

#合并后数据存储路径
newfile_dir='合并后数据\\截止0409'
#获取当前日期
current_date=time.strftime("%Y-%m-%d")
#合并后数据路径名称
sheng_filename=newfile_dir+'\\'+current_date+'_国内(省).xlsx'
shi_filename=newfile_dir+'\\'+current_date+'_国内(市).xlsx'

# 原始数据文件路径
file_dir='data\\4.9\\国内'
# 获取原始数据excel文件列表
file_list=os.listdir(file_dir)

获得的excel文件名称列表:

file_list.png

③省份数据合并处理

由于每个excel的字段名并不是十分相同,即使字段名表达的意思相同,但是,在1个excel里可能会多个空格或者少个字,因此,需要先纵向合并后,再看看哪些字段需要进行修改。

合并的思路就是,先将每个excel导入成dataframe,并存到列表中,再用pd.concat将列表中的dataframe进行合并。

#新建列表,用于装dataframe
sheng_list=[]
#对每个excel文件进行循环
for file in file_list:
    #将每个excel文件的路径获取
    file_path=os.path.join(file_dir,file)
    #将每个excel文件读入成dataframe并赋给对象dataframe
    dataframe=pd.read_excel(file_path)
    #获取dataframe的列名列表
    dfcolls=list(dataframe.columns.values)
    #原始字段名(举例),这个需要多次合并后后续不断补充
    oldcolnamels=['累计病亡率(%)','累计确诊率(%)','累计治愈率(%)','累计确诊率 ']
    #新的字段名(举例),这个需要多次合并后后续不断补充
    newcolnamels=['累计病亡率','累计确诊率','累计治愈率','累计确诊率']
    #批量修改字段名,若原始字段名存在某个excel的列名中,则将原始字段名替换成对应的新字段名
    for i in range(len(oldcolnamels)):
        if oldcolnamels[i] in dfcolls:
            dataframe.rename(columns={oldcolnamels[i]:newcolnamels[i]},inplace=True)
            #打印显示,哪些excel进行了列名替换
            print(file,oldcolnamels[i],'替换成',newcolnamels[i])
    #将dataframe添加到列表中
    sheng_list.append(dataframe)

#将列表中的各个省份的dataframe纵向合并
sheng=pd.concat(sheng_list)
# 删除不需要的列
sheng.drop(columns=['累计无症状感染者病例(待删)','Unnamed: 36',], axis=1, inplace=True)

#在第二列添加‘地市’字段,先对列名列表插入元素,再用.reindex整理列
colls=list(sheng.columns.values)
colls.insert(2,'地市')
sheng = sheng.reindex(columns=colls, fill_value='')
#将'省份'列数据复制到'地市'列
sheng['地市']=sheng['省份']
#将合并后的省数据导出excel,不显示索引
sheng.to_excel(sheng_filename,index=False)

④合并市的数据

所有地市的数据都在excel的第二个sheet,但有些excel也没有地市数据。

因此,这里涉及判断是否存在第二个sheet,如果有的话,将第二个sheet的数据读取并合并,而读取合并的过程与合并省的数据的过程一样。

所以,关键点就是判断并获取excel的第二个sheet的数据。

shi_list=[]
for file in file_list:
    file_path=os.path.join(file_dir,file)
    #读取excel,并赋给对象b
    b=xlrd.open_workbook(file_path)
    #获取excel的sheet数目
    sheet_num=len(b.sheets())
    #判断sheet数目是否为2
    if sheet_num==2:
        #若excel存在两个sheet,则读取第二个sheet
        sheetname=b.sheets()[1]
        sheetname=sheetname.name
        #将每个excel的第二个sheet(存在的话)读入并转为dataframe
        dataframe=pd.read_excel(file_path,sheet_name=sheetname)
        #下同上述的省合并数据
        dfcolls=list(dataframe.columns.values)
        oldcolnamels=['累计病亡率(%)','累计确诊率(%)','累计治愈率(%)','累计确诊率 ']
        newcolnamels=['累计病亡率','累计确诊率','累计治愈率','累计确诊率']
        for i in range(len(oldcolnamels)):
            if oldcolnamels[i] in dfcolls:
                dataframe.rename(columns={oldcolnamels[i]:newcolnamels[i]},inplace=True)
                print(file,oldcolnamels[i],'替换成',newcolnamels[i])
        shi_list.append(dataframe)
shi=pd.concat(shi_list)
shi.drop(columns=['地区代码(待删)'], axis=1, inplace=True)
shi.to_excel(shi_filename,index=False)

⑤合并省市数据

将省和市的数据合并起来,过程一样,用pd.concat将两个dataframe纵向合并。

由于合并后的数据太大,电脑带不起来,所以,就每次都要将那些之后日期的数据或者空行删除,

这就涉及到合并后的数据筛选的问题,在这里有个坑,琢磨了好久才发现原因。

两个数据框合并后,相同列名会合并到一起,不同的就单列,这个很好理解,

但是,数据框除了有column索引,还有index索引(理解成行名),

当两个数据框纵向合并的时候,如果没有自定义index索引的话,

index索引就很可能重复,从而之后的操作,如筛选数据上出现问题。

因此,纵向合并数据框后,要再操作的话,最好重新设置一下index索引。

#省市数据合并后excel文件路径
guonei_filename=newfile_dir+'\\'+current_date+'_国内.xlsx'
#将省市数据合并
guonei=pd.concat([sheng,shi])
#删除字段'省份'
guonei.drop(columns=['省份'], axis=1, inplace=True)
#将字段'日期'转为字符串格式
guonei['日期']=guonei['日期'].astype(str)
#!!!重置index索引
guonei=guonei.reset_index(drop=True)
#删除字段'日期'为空的行
guonei=guonei.drop(guonei[guonei['日期']=='NaT'].index)
#删除字段'日期'大于cut_data的行
cut_data='2020-04-09'
guonei=guonei.drop(guonei[guonei['日期'] > cut_date].index)
#将数据导出excel
guonei.to_excel(guonei_filename,index=False)

⑥宽数据变长数据

由于需要把数据写入数据库,因此,需要将数据转为长数据。

长宽数据的形式,用stata的help文件里展示一下,如下图。

长宽数据.png

stata里进行转换,好像还得保证宽数据各个列名前缀相同,

但是,在python里,并不强求,用.melt(id_vars=[''])方式,就可以轻松转换。

#宽数据转成长数据,数据条数就很多了,所以,先选择目标日期的数据
date=['2020-04-08','2020-04-09'] 
guonei=guonei[guonei['日期'].isin(date)]
#转换后excel路径名
guonei_chang_filename=newfile_dir+'\\'+current_date+'_国内(SQL).xlsx'
#!!!宽数据转长数据,保留字段'日期','地市'和'地区代码',其他字段转为两列,一列字段名为'variable',另一列为'value'
guonei_chang=guonei.melt(id_vars=['日期','地市','地区代码'])
#删除字段'value'为空值的行
guonei_chang=guonei_chang.dropna(subset=['value'])
#通过拼接字符串生成sql语句
guonei_chang['s1']="INSERT INTO `db`.`nCoV2019` (`NominalTime`, `SpaceName`, `SpaceCode`, `Index`, `DataValue`) VALUES ('"
guonei_chang['s2']="', '"
guonei_chang['s3']="');"
guonei_chang=guonei_chang.fillna('null')
#多列字符串拼接
guonei_chang['sql']=guonei_chang['s1'].str.cat([guonei_chang['日期'],guonei_chang['s2'],
    guonei_chang['地市'],guonei_chang['s2'],guonei_chang['地区代码'].map(lambda x:str(x)),guonei_chang['s2'],guonei_chang['variable'],
    guonei_chang['s2'],guonei_chang['value'].map(lambda x:str(x)),guonei_chang['s3']],sep='')
#删除补充字符串列
guonei_chang.drop(columns=['s1','s2','s3'], axis=1, inplace=True)
#结果导出excel
guonei_chang.to_excel(guonei_chang_filename,index=False,header=False)

至此,就导出以下excel文件,也便于查验。

生成excels.png

5.最后

同理,国外和境外数据思路和过程相同,换一下数据路径和需要修改的列名就行。

之后再整理的话,如果字段没有发生变化,每次跑下代码结果就出来了。

奈何每次都有变化,也不知道会增加什么字段,这部分只能花些时间看看了~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容