时间序列数据,python预处理

类似于图片的时间序列数据,这是我EXCEL处理完毕的。

拿到数据之后需要做三件事,第一件,检查数据缺失情况,序列是每周一个数据,如果发现上下两个数据之间缺少一周数据,那么插入一条数据,数值为上下两条记录值的均值。最大可能连续缺失两个数据。

第二,检查日期是否为周五,如果不是周五把日期修正为周五,(最近的没数据的周五)

第三,如果一年周五的周数有53周,那么将52周和53周数据去掉。用51周和第二年第一周数据平均,记为52周数据,日期为51周和第二年第一周中间(不要求周五)

 

还有一个问题,就是要解决类似上述问题,有什么书或资源推荐?

 

代码编辑完成,基本实现三个需求。

# coding='utf-8'

import os
import datetime
import shutil
import openpyxl
from openpyxl import load_workbook

pathOri = r'C:\Coding\Python\CSDN\dateSequence'

xlsxName = 'input.xlsx'
outputName = 'output.xlsx'

def getDate(value):
	# 因为excel里面保存的可能是日期格式,也可能是字符串形式。
	if(str(value.__class__) == "<class 'datetime.datetime'>"):
		# datetime.datetime格式
		return value
	else:
		# 字符串格式。
		value_list = value.split('/')
		year = int(value_list[0])
		month = int(value_list[1])
		day = int(value_list[2])
		rowDay = datetime.date(year, month, day)
		return rowDay



def dealEachSheet(inputSheet, outputSheet):
	inputRows = inputSheet.max_row
	inputCols = inputSheet.max_column
	# 将表头原样复制到新文件中
	for i in range(1, 3):
		for j in range(1, inputCols+1):
			outputSheet.cell(row=i, column=j).value = inputSheet.cell(i, j).value

	outputSheet.cell(row=1, column=3).value = "周"
	outputSheet.cell(row=1, column=4).value = "星期"

	# 假设日期是第二列
	dateColumn = 1
	valueColumn = dateColumn + 1
	weekIndexColumn = dateColumn + 2
	weekdayColumn = dateColumn + 3


	valueDict = {}

	# 记下来上一条记录是几月几号
	lastDate = None
	for i in range(3, inputRows+1):
		eachDate = getDate(inputSheet.cell(i, dateColumn).value)
		if(eachDate.weekday() != 4):
			# 因为是从上到下遍历,所以如果不是周五,则优先填充上一周。
			if eachDate.weekday() < 4:
				eachDate = eachDate + datetime.timedelta(days=-3-eachDate.weekday())
			else:
				eachDate = eachDate + datetime.timedelta(days=4-eachDate.weekday())
			# 填充上一周
			if(eachDate != lastDate):
				pass
			# 填充到这一周
			else:
				eachDate = eachDate + datetime.timedelta(days=7)
			inputSheet.cell(i, dateColumn).value = eachDate
		valueDict[eachDate] = inputSheet.cell(i, valueColumn).value


	startDate = getDate(inputSheet.cell(3, 1).value)
	endDate = getDate(inputSheet.cell(inputRows, 1).value)

	week53Dates = []

	eachDate = startDate
	lastDate = eachDate
	while eachDate != endDate:
		# 遍历所有的周五,进行数据插值
		eachDate = eachDate + datetime.timedelta(days=7)
		if eachDate not in valueDict.keys():
			# 如果这一周是个缺失数值,则找到字典中有数值的上一个和下一个,并取这两个数值的平均数
			# 如果有多个缺失值,比如说是1 缺失 缺失 缺失 10
			# 则首先补充第一个,补充为  1 5.5 缺失 缺失 10
			# 接下来补充第二个,补充为  1 5.5 7.75 缺失 10
			# 接下来补充第三个,补充为  1 5.5 7.75 8.875 10
			# 采用的是二分插值方法。
			# 因为每次填充之后,上一个日期总是在列表中的,所以只需要找到下一个进行插分即可。
			nextDate = eachDate + datetime.timedelta(days=7)
			while nextDate not in valueDict.keys():
				nextDate = nextDate + datetime.timedelta(days=7)
			valueDict[eachDate] = (valueDict[lastDate] + valueDict[nextDate]) / 2;
		lastDate = eachDate
		# 在这次遍历中,同时记录一下有53周的日期
		if eachDate.isocalendar()[1] == 53:
			week53Dates.append(eachDate)

	# 如果有53周,则用51周五和第二年第一周的日期中间值进行替代。
	for eachWeek53Date in week53Dates:
		# 第二年第一周周五
		week1date = eachWeek53Date + datetime.timedelta(days=7)
		# 第一年第51周周五
		week51date = eachWeek53Date + datetime.timedelta(days=-14)
		# 第一年第52周周五
		week52date = eachWeek53Date + datetime.timedelta(days=-7)
		diff = (week1date - week51date) / 2

		# 计算51周周五和第二年第一周周五的中间那一天
		midDate = week51date + datetime.timedelta(days=diff.days)
		# 计算中间那一天的数值
		midValue = (valueDict[week51date] + valueDict[week1date]) / 2
		# 删除第52周和53周
		valueDict.pop(eachWeek53Date)
		valueDict.pop(week52date)
		valueDict[midDate] = midValue

	keys = sorted(valueDict)

	id = 3
	weekDict = {0:'星期一',1:'星期二',2:'星期三',3:'星期四',4:'星期五',5:'星期六',6:'星期日'}
	for key in keys:
		outputSheet.cell(id, dateColumn).value = key.strftime('%Y/%m/%d')
		outputSheet.cell(id, valueColumn).value = valueDict[key]
		outputSheet.cell(id, weekIndexColumn).value = key.isocalendar()[1]
		outputSheet.cell(id, weekdayColumn).value = weekDict[key.weekday()]
		id += 1

def main():
	# 读取文件
	# 因为原始数据中有许多地方是用公式进行计算得到的,需要指定data_only=True使得读入的时候就是完整的数值。
	# 比如A286=A285+7等
	inputWorkbook = load_workbook(os.path.join(pathOri, xlsxName), data_only=True)
	inputSheet = inputWorkbook[inputWorkbook.sheetnames[0]]

	# 输出文件
	outputWorkbook = openpyxl.Workbook()
	outputSheet = outputWorkbook.active
	outputSheet.title = inputWorkbook.sheetnames[0]

	# 处理文件
	dealEachSheet(inputSheet, outputSheet)
	for i in range(1, len(inputWorkbook.sheetnames)):
		sheetName = inputWorkbook.sheetnames[i]
		inputSheet = inputWorkbook[sheetName]
		outputSheet = outputWorkbook.create_sheet(sheetName)
		dealEachSheet(inputSheet, outputSheet)

	outputWorkbook.save(os.path.join(pathOri, outputName))


if __name__ == '__main__':
	main()

 

https://pandas.pydata.org/docs/search.html?q=to_datetime

建议学习Python pandas 库里面的to_datetime,有很多关于数据处理的知识,时间处理,数值残缺处理,

如果你想自己学习Python数据分析领域的知识的话。

 

重要的地方

#获取第几周

datetime.date(myyear, mymonth, myday).isocalendar()

 

#取下一周五

newdate = data + datetime.timedelta(days = 7)

安装openpyxl

python中与excel操作相关的模块:

  • xlrd库:从excel中读取数据,支持xls、xlsx
  • xlwt库:对excel进行修改操作,不支持对xlsx格式的修改
  • xlutils库:在xlw和xlrd中,对一个已存在的文件进行修改。
  • openpyxl:主要针对xlsx格式的excel进行读取和编辑。

安装方式:pip install openpyxl

Excel中的三大对象

  • WorkBook:工作簿对象
  • Sheet:表单对象
  • Cell:表格对象

提供思路

 

 

import xlwt

#或者打开当前表

 

work_book=xlwt.load_workbook('XXXX.xlsx')

sheet=work_book['sheet1']

rowcount=sheet.max_row

columncount=sheet.max_column

#新建一个sheet用来保存结果

 

sheet1=work_book.add_sheet('sheet表名')

#while循环读取数据

data = xlrd.xldate_as_tuple(table.cell(i, 6).value, 0)
# 日期格式转化成元组 
myyear=data.year

varAmt=0要求平均的值,累加要平均的值
varcount=0&&记录数量每次插入+1即可

##########################
mymonth=data.month
myday=data.day

#获取第几周51,52,53数据直接忽略,51求平均
datetime.date(myyear, mymonth, myday).isocalendar()

如果是51周

插入平均值varAmt/varcount

写入sheet1

#获取下一周星期五
newdate = data + datetime.timedelta(days = 7)

如果下一周跟读取的周不一样,就插入newdate到sheet1//用刚读取的数据和上一数据做对比

不想敲代码

 

 

 

 

 

原始数据的格式就是上面图片中的吗

这个都是python 比较基础的内容了,掌握python 基础语法,然后看看pandas 包的使用就可以解决这几个问题。 我给你写一下代码吧