代码地址如下:
http://www.demodashi.com/demo/11650.html

看完本篇需要:

10min

作业练习需要:

0.5h~3h(依练习者对python熟悉程度而定)

看完本篇可以学到:

1、用xlrd模块读取Excel文件中的数据

2、用xlsxwriter模块向Excel文件写入数据并保存

3、用time和datetime模块将字符串转换成时间类,并进行时间的比较

本篇目录

  1. 作业需求
  2. 整体思路
  3. 详细实现步骤

    3.1. 读取表格数据

    3.2. 将行数据list按时间先后升序排序

    3.3. 维护一个map并新增数据到行数据

    3.4. 将修改后的行数据list写入Excel表格并保存为xslx格式
  4. 完整代码
  5. 结果展示
  6. 参考
  7. 源码及作业练习文件

作业需求



一个朋友在某运动品牌公司上班,老板给他布置了一个处理客户订单数据的任务。要求是根据订单时间和客户id判断生成四个新的数据:

1、记录该客户是第几次光顾

2、上一次的日期时间是什么时候

3、与上次订单的间隔时间

4、这是一个existing客户还是一个new客户(见定义)

文件说明:

1、第一列是订单日期和时间(乱序)

2、第二列是客户的id

3、第三列不需要使用

相关定义如下:

1、existing:此次下单日期时间与上次日期时间的距离在N天以内,精确到时间(时分秒)

2、new:即超过N天

整体思路

1、读取表格的行数据存储成list,并按照时间列的升序排序。

2、维护一个map(在python里是字典dict),每个用户 id 作为key,一个二元组(第几次下单,上一次的日期时间)作为value。

3、遍历表格行数据的list。判断客户 id 是否已经存在于map中,若首次出现,则置该客户 id 在map中的value为[1,'首次下单'],对应行数据新增的4个数据为[1,'首次下单',该次日期时间与上次日期时间差,'new']。若已经存在,则更新map中对应的value为[原次数+1,该次日期时间],对应行数据新增的4个数据为[原次数+1,上次日期时间,间隔时间,new/existing取决于间隔时间与预设N]。

4、将修改过后的行数据list写入到Excel工作簿并保存。

详细实现步骤

读取表格数据

我们可以用xlrd模块对Excel文件进行读取,以便进一步分析处理数据。示例代码如下:

wb=xlrd.open_workbook('../excel/buyer_day.xlsx')# 打开工作簿,参数为文件地址
sheet=wb.sheets()[0]# 获取工作簿中的第一张工作表
for i in range(100):
if i==0:# 跳过首行的标题
continue
time_str= sheet.row_values(i)[0]# 读取该工作表第i行的第一个单元格数据
print time_str

以上代码成功输出前100行的日期则说明已经成功读取到数据。输出结果如下:



可以看到,这里输出的日期前后有空格,而且最后的时间有小数点,这不便于我们转换成时间类,所以要进行一些处理。用strip函数去掉前后空格,用切片切掉末尾的".0"。将前面的第4行代码更改为:

time_str= sheet.row_values(i)[0].strip()[:-2]

既然读取文件没有问题,进一步浏览整个文件发现存在多余的空行和重复的标题行(如图3),在读取和转存中可以用正则匹配过滤掉这些行。

另外,可以从图2看出时间是乱序的,这不利于后续的逻辑实现,所以将读取的行数据转存到list中,以便进行排序。

list_row=[]# 将行数据存储到list中,便于排序

wb=xlrd.open_workbook('../excel/buyer_day.xlsx')
sheet=wb.sheets()[0]
nrows=sheet.nrows# 工作表的行数 for i in range(nrows):
#用正则匹配过滤掉空行和标题行
str_date=sheet.row_values(i)[0].strip()[:-2]
if re.match('[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}', str_date)!=None:
list_row.append(sheet.row_values(i))# 插入到list

将行数据list按时间先后升序排序

这里用到sorted函数,可以对list进行排序。示例代码如下,key指定的函数会作用于list中的每一个元素,其返回值必须为可比较的变量。

list_row=sorted(list_row,key=self.getDatetime)# 将list_row排序,排序是对key进行比较,key指定的函数会作用于list中的每一个元素

行数据的第一格的日期时间字符串不便于直接比较,可以转换成datetime对象,以便直接比较。具体做法是将读取到的日期时间字符串用time模块的strptime转换成时间类,再用datetime模块转换成datetime类。

timeArray=time.strptime(time_str, "%Y-%m-%d %H:%M:%S")# 第二个参数是对应字符串的格式
Y,m,d,H,M,S=timeArray[0:6]
dt=datetime.datetime(Y,m,d,H,M,S)# 转换成datetime对象,可以直接进行比较

datetime之间的比较可以直接用>,<,=符号,而且可以直接相减求间隔时间,间隔时间的类型是timedelta,也可以直接比较。示例代码如下:

dt1=datetime.datetime(2017,5,2,13,23,01)
dt2=datetime.datetime(2017,3,2,12,00,00)
dt3=datetime.datetime(2017,6,19)
dt4=datetime.datetime(2017,5,21) dis1=dt1-dt2# 相减返回的类型是timedelta
dis2=dt3-dt4
print dis1
print dis2
print dis1>dis2
print dis1<datetime.timedelta(days=30)

示例输出:

维护一个map并新增数据到行数据

map={客户 id :[第几次下单,上次日期时间]}

搞清楚了日期时间的比较和时间间隔的比较,我们就可以按之前整体思路的2、3步的逻辑进行map的维护更新和list中行数据的修改了。逻辑之前已经提过了,细节见代码注释。

for row_value in list_rowValues:

    dt_current=self.getDatetime(row_value)# 订单日期时间的datetime类型
mber_id=row_value[1].strip()# 客户id # 维护一个dict,用一个dict保存,客户id作为key,[当前第几次,上次订单日期时间]作为value
# 并且依此写入新数据到list的行数据中
if mber_id in self.dict_mid_data: # 如果存在这个key,说明该顾客之前有订单记录,更新dict,同时插入新数据到row_value self.dict_mid_data[mber_id][0]+=1# 更新下单次数+1
row_value[3]=self.dict_mid_data[mber_id][0]# 插入下单次数 dt_last=self.dict_mid_data[mber_id][1]
row_value[4]=dt_last.strftime("%Y-%m-%d %H:%M:%S")# 插入上次订单日期时间 dis=abs(dt_current-dt_last)# 时间差的绝对值
row_value[5]=str(dis)# 插入与上次订单时间的间隔时间差 # 插入usertype
if dis <= datetime.timedelta(days=N):# 如果间隔在N天内
row_value[6]='existing'
else:
row_value[6]='new' if dt_current>dt_last:# 如果当前时间更近,更新dict里的上次日期时间
self.dict_mid_data[mber_id][1]=dt_current
else:# 不存在这个key,直接保存初始值
self.dict_mid_data[mber_id]=[1,dt_current]
row_value[3]=1 # 当前是第几次订单
row_value[4]=u'首次下单' # 当前日期时间
row_value[5]='-' # 与上次订单间隔时间
row_value[6]='new' # usertype

将修改后的行数据list写入Excel表格并保存为xslx格式

xlrd模块读取的工作簿是不能修改的,也就是只能读,不能写。想要新增数据进原来的工作簿,要用到xlsxwriter模块生成新的Excel工作簿,然后把修改后的list写入到一张新的工作表中,再保存到原路径(或者新的路径),以达到修改的目的。

wb=xlsxwriter.Workbook('../excel/buyer_day_new.xlsx')
sheet=wb.add_worksheet('sheet1')# 新增一张工作表sheet1
# 写入标题
sheet.write(0,0,'order_dt')# 三个参数分别是:单元格横坐标,纵坐标,写入内容
sheet.write(0,1,'member_id')
sheet.write(0,2,'member_type')
sheet.write(0,3,'times')
sheet.write(0,4,'last_order_dt')
sheet.write(0,5,'interval')
sheet.write(0,6,'user_type') # 写入处理后的数据
len_list=len(list_rowValues)
for i in range(len_list):
row_value=list_rowValues[i]
len_row=len(row_value)
for j in range(len_row):
sheet.write(i+1,j,row_value[j]) wb.close()

完整代码

# -*- coding:utf-8 -*-
'''
Created on 2017年5月31日 @author: wycheng
'''
import xlrd
import xlsxwriter
import time,datetime
import re class BuyerManager:
dict_mid_data={}# 维护的一个 map{客户id:[第几次下单,上次日期时间]} # 获取对应行数据的订单时间
def getDatetime(self,row_value):
time_str=row_value[0].strip()[:-2]
timeArray=time.strptime(time_str, "%Y-%m-%d %H:%M:%S")
Y,m,d,H,M,S=timeArray[0:6] dt_current=datetime.datetime(Y,m,d,H,M,S)# 转换成datetime对象,可以直接进行比较
return dt_current # 将所有工作表的行按照订单日期升序排序
def getList_sorted(self,list_xl):# list_xl: Excel文件的地址list list_row=[]# 将行数据存储到list中,便于排序 for exl in list_xl:
print u'正在打开文件 '+exl
wb=xlrd.open_workbook(exl)
sheet=wb.sheets()[0]
nrows=sheet.nrows# 工作表的行数
print u'正在插入文件 '+exl+u'的row_value'
for i in range(nrows):
#用正则匹配过滤掉空行和标题行
str_date=sheet.row_values(i)[0].strip()[:-2]
if re.match('[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}', str_date)!=None:
list_row.append(sheet.row_values(i)) print u'正在排序……'
list_row=sorted(list_row,key=self.getDatetime)# 将list_row排序,排序是对key进行比较,key指定的函数会作用于list中的每一个元素
return list_row def process(self,list_rowValues,N):# list_rowValues: 存放所有row_value的list N: 间隔N天内是existing
# 遍历每一行
line=1
for row_value in list_rowValues:
print u'正在处理第'+str(line)+u'行'
line+=1 dt_current=self.getDatetime(row_value)# 订单日期时间的datetime类型
mber_id=row_value[1].strip()# 客户id # 维护一个dict,用一个dict保存,客户id作为key,[当前第几次,上次订单日期时间]作为value
# 并且依此写入新数据到EXcel
if mber_id in self.dict_mid_data: # 如果存在这个key,说明该顾客之前有订单记录,更新dict,同时插入新数据到row_value self.dict_mid_data[mber_id][0]+=1# 更新下单次数+1
row_value[3]=self.dict_mid_data[mber_id][0]# 插入下单次数 dt_last=self.dict_mid_data[mber_id][1]
row_value[4]=dt_last.strftime("%Y-%m-%d %H:%M:%S")# 插入上次订单日期时间 dis=abs(dt_current-dt_last)# 时间差的绝对值
row_value[5]=str(dis)# 插入与上次订单时间的间隔时间差 # 插入usertype
if dis <= datetime.timedelta(days=N):# 如果间隔在N天内
row_value[6]='existing'
else:
row_value[6]='new' if dt_current>dt_last:# 如果当前时间更近,更新dict里的上次日期时间
self.dict_mid_data[mber_id][1]=dt_current
else:# 不存在这个key,直接保存初始值
self.dict_mid_data[mber_id]=[1,dt_current]
row_value[3]=1 # 当前是第几次订单
row_value[4]=u'首次下单' # 当前日期时间
row_value[5]='-' # 与上次订单间隔时间
row_value[6]='new' # usertype return list_rowValues # 写入Excel并保存
def write_t_xl(self,list_rowValues,xl_addr):
wb=xlsxwriter.Workbook(xl_addr)
sheet=wb.add_worksheet('sheet1')
# 写入标题
sheet.write(0,0,'order_dt')
sheet.write(0,1,'member_id')
sheet.write(0,2,'member_type')
sheet.write(0,3,'times')
sheet.write(0,4,'last_order_dt')
sheet.write(0,5,'interval')
sheet.write(0,6,'user_type') # 写入处理后的数据
len_list=len(list_rowValues)
for i in range(len_list):
print u'正在写入第'+str(i+1)+u'行……'
row_value=list_rowValues[i]
len_row=len(row_value)
for j in range(len_row):
sheet.write(i+1,j,row_value[j]) wb.close()
print u'写入完毕,excel文件已生成!' l=['../excel/buyer_day.xlsx']#需要输入处理的文件路径list,即可以输入多个文件进行处理
buyerManager=BuyerManager()
list_rowValues=buyerManager.getList_sorted(l)
list_rowValues_new=buyerManager.process(list_rowValues, 100)
buyerManager.write_t_xl(list_rowValues, '../excel/buyer_day_new.xlsx')

结果展示

源码截图

其中附带的Excel数据文件

参考

python高手之路python处理excel文件(方法汇总)

python模块之XlsxWriter

python基础 实战作业 ---Excel基本读写与数据处理

代码地址如下:
http://www.demodashi.com/demo/11650.html

注:本文著作权归作者,由demo大师代发,拒绝转载,转载需要作者授权

python基础 实战作业 ---Excel基本读写与数据处理的更多相关文章

  1. python基础实战之猜年龄游戏

    目录 一.Python基础实战之猜年龄游戏 给定年龄,用户可以猜三次年龄 年龄猜对,让用户选择两次奖励 用户选择两次奖励后可以退出 age = 18 # 答案 count = 0 # 游戏次数控制 p ...

  2. 001 python基础实战

    报名了阿里大学的AI,一直没有学习,今天开始正式学习. 今天是第一节,Python的基础编程实战,里面包含两个示例. 一:任务实现文件的批量重命名. 1.创建一个目录 2.程序 #!/usr/bin/ ...

  3. Python-Day3 Python基础进阶之集和/文件读写/函数

    一.集和 集合是一个无序的,不重复的数据组合,它的主要作用如下: 去重,把一个列表变成集合,就自动去重了 关系测试,测试两组数据之前的交集.差集.并集等关系 1.创建集合 >>> s ...

  4. Python基础学习七 Excel操作

    python操作excel,python操作excel使用xlrd.xlwt和xlutils模块, xlrd模块是读取excel的,xlwt模块是写excel的,xlutils是用来修改excel的. ...

  5. python基础:day3作业

    修改haproxy配置文件 基本功能:1.获取记录2.添加记录3.删除记录 代码结构:三个函数一个主函数 知识点:1.python简单数据结构的使用:列表.字典等 2.python两个模块的使用:os ...

  6. python基础day2作业:购物车

    #功能:1.可注册账号2.登录买家账号3.可查询编辑购物车里商品4.可以余额充值5.可提示余额不足6.购物车结算 #使用:1.第一次使用先注册账号填写账号金额2.账号金额信息保存在buyer_acco ...

  7. 我的Python成长之路---第一天---Python基础(作业2:三级菜单)---2015年12月26日(雾霾)

    作业二:三级菜单 三级菜单 可一次进入各个子菜单 思路: 这个题看似不难,难点在于三层循环的嵌套,我的思路就是通过flag的真假来控制每一层的循环的,简单来说就是就是通过给每一层循环一个单独的布尔变量 ...

  8. 我的Python成长之路---第一天---Python基础(作业1:登录验证)---2015年12月26日(雾霾)

    作业一:编写登录接口 输入用户名密码 认证成功系那是欢迎信息 输错三次后锁定 思路: 1.参考模型,这个作业我参考了linux的登录认证流程以及结合网上银行支付宝等锁定规则 1)认证流程参考的是Lin ...

  9. python基础之作业1---用户登录

    作业:编写登陆接口 输入用户名密码 认证成功后显示欢迎信息 输错三次后锁定 import sys, os, getpass os.system('clear')i = 0while i < 3: ...

随机推荐

  1. PHP极速开发框架LotusAdmin page版发布

    体验地址及账号如下: 地址    https://page.waytomilky.com/ 账号:admin 密码:123456 LotusAdmin2.0其主要特性包括: 1.基于ThinkPHP5 ...

  2. 16.RDD实战

    第16课:RDD实战 由于RDD的不可修改的特性,导致RDD的操作与正常面向对象的操作不同,RDD的操作基本分为3大类:transformation,action,contoller 1.   Tra ...

  3. js跳转整理(简记)

    location.replace(URL)跳转脱离历史记录流: location.href=url;在历史记录中 子刷新父级 parent.location.replace(parent.locati ...

  4. Python与数据结构[3] -> 树/Tree[1] -> 表达式树和查找树的 Python 实现

    表达式树和查找树的 Python 实现 目录 二叉表达式树 二叉查找树 1 二叉表达式树 表达式树是二叉树的一种应用,其树叶是常数或变量,而节点为操作符,构建表达式树的过程与后缀表达式的计算类似,只不 ...

  5. Codeforces #447 Div2 D

    #447 Div2 D 题意 给一棵完全二叉树,每条边有权值为两点间的距离,每次询问 \(x, h\) ,从结点 \(x\) 出发到某一结点的最短路的距离 \(d\) 如果小于 \(h\) ,则答案加 ...

  6. Linux命令之umount

    umount [-hV] umount -a [-dflnrv] [-t vfstype] [-O option] umount [-dflnrv] {dir|device} [-t vfstype] ...

  7. 【权值分块】bzoj1503 [NOI2004]郁闷的出纳员

    权值分块,离散化非常蛋疼,只能离散化搞…… 需要支持操作:删除<=某个值得所有权值==打标记 O(sqrt(n)) 码长和我的平衡树差不多……速度快3倍左右. #include<cstdi ...

  8. HTML5 Boilerplate笔记(3)

    HTML5 Boilerplate项目网址:https://github.com/h5bp/html5-boilerplate

  9. LongPathException问题解析

    一.背景      当windows系统下使用System.IO命名空间下的方法,目录长度超过260个字符时,.net framework会抛出LongPathException.查阅相关资料,发现是 ...

  10. JS面向对象之作用域

    作用域 词法作用域 作用域 域表示的就是范围,即作用范围 就是一个名字在什么地方能使用,在什么地方不能使用 块级作用域 块级别的作用范围 // 在 c , java 等编程语言中,下面的语法报错 { ...