dahe_1984 发表于 2018-03-22 21:02

总结贴,记录下pandas处理一大批数据

零零散散写过好多脚本,总没有记录的习惯,花了两天时间,从坑里爬了出来,回头看看,常在河边走,以后常备尿不湿。

简单说下目的,目的不明确,每一行代码都是bug。

有一批文件,每个文件每行按照 | 分隔,文件中的字段与Excel表中的sheet页对应。



简单说就是按照Excel中sheet页中对应的字段规则,去验证一批文件字段是否正确。
我用的是python 3.6, python有点坑,向下不兼容,肯定不支持2.7
# -*- coding: utf-8 -*-

import os
import re
import time
import argparse
import numpy as np
import xlrd
import xlwt
import glob
from framework.logger import Logger
import pandas as pd

    #
    # 解析Excel, 得到表头和验证规则
    #
    header_names = {}       # 存储Excel中的sheet和字段
    for e in glob.glob(os.path.join(args.configExcel, "*.xlsx")):# 多个Excel中一同处理
      logger.info("Getting config file: {}".format(e))               # logger 可以去掉
      workbook = xlrd.open_workbook(e)                              # 用xlrd打开每个excel
      sheets   = workbook.sheet_names()                           #得到打开Excel的所有sheet页得名字
      for idx in range(1,len(sheets)):                                     # 遍历所有的sheet页面                        
            cols = workbook.sheet_by_name(sheets).col_values(2)    # 得到第二列的长度,就是图中第一个红框的长度workbook.sheet_by_name(),by谁就是用那个sheet
            for tmp_row in range(1, len(cols)):                                        # 遍历第二列中所有元素
                if(workbook.sheet_by_name(sheets).cell(tmp_row, 2).value and (not workbook.sheet_by_name(sheets).cell(tmp_row, 4).value)):
                  #
                  # 字段和值必须一一匹配,不能有值没字段或者有字段没有值
                  #
                  logger.error("%s: line %s not match in %s"%(sheets, workbook.sheet_by_name(sheets).cell(tmp_row, 2).value,workbook.sheet_by_name(sheets).cell(tmp_row, 4).value))                # logger,可以去掉
                else:
                  if(workbook.sheet_by_name(sheets).cell(tmp_row, 2).value.strip() != ""):# 这里判断第二列是否有空值,合并单元格可能就是空值(这个没尝试)。
                                                                                                                                       # 看着很长,by就是用哪个sheet,cell就是(行,列),value就是取值,python3.6有个好处就是不用判断中英文,方便多了

                        if sheets not in header_names:                                             # 字典的传统套路,用之前先看看key是否存在,不存在就初始化,存在就是直接用                     
                            header_names] = []                                                # 不存在就是赋予这个key新的使命,value是列表。
                        header_names].append(workbook.sheet_by_name(sheets).cell(tmp_row, 2).value)# sheet是字典的key值,sheet中所有的第二列字段是list值
                        if sheets not in dict:                                                            # 这个字典存储sheet表中第四列的值
                            dict] = {}                                                            # 也可以用一个字典,但是可能需要字典升级为三维,考虑到我的单核脑处理器,在搞一个字典
                        if(('BigtoPre' in workbook.sheet_by_name(sheets).cell(tmp_row, 4).value) and ("=" in workbook.sheet_by_name(sheets).cell(tmp_row, 4).value)):
                            #
                            # 10<collastcol<100(D=-1)
                            #这个是特殊列,这列的意思是 这列减去上列大于10或者小于100,或者是缺省值-1
                            #
                            list_a = re.findall("[\-|\d]+\.?\d*",workbook.sheet_by_name(sheets).cell(tmp_row, 4).value)# 抓出第四列中所有的数字,正负都要
                            actualValue = workbook.sheet_by_name(sheets).cell(tmp_row, 2).value + ',' + workbook.sheet_by_name(sheets).cell((tmp_row -1), 2).value + "," + ">pythonD" + "," + str(list_a)            # 做了转换,换成以后好处理的格式,就是全是逗号分隔,并且写了关键字 pythonD,以后我匹配pythonD,就知道是这个特殊行
                            dict]).cell(tmp_row, 2).value] = actualValue# 换了之后的值是 col, lastcol, pythonD, 10, 100, -1
                        elif(('BigtoPre' in workbook.sheet_by_name(sheets).cell(tmp_row, 4).value) and ("=" not in workbook.sheet_by_name(sheets).cell(tmp_row, 4).value)):
                            #
                            # 10<BigtoPre<100
                            # 这个和上面一样,就是少了默认值而已
                            #
                            list_a = re.findall("[\-|\d]+\.?\d*",workbook.sheet_by_name(sheets).cell(tmp_row, 4).value)
                            actualValue = workbook.sheet_by_name(sheets).cell(tmp_row, 2).value + ',' + workbook.sheet_by_name(sheets).cell((tmp_row -1), 2).value + "," + ">" + str(list_a)
                        else:
                            #
                            # 这个是其他正常值的匹配, 第二列对应第四列
                            #
                            dict]).cell(tmp_row, 2).value] = workbook.sheet_by_name(sheets).cell(tmp_row, 4).value


前戏了半天,下面才是pandas的主题。前戏因人而异,一点没有总有人抱怨没情趣,无聊,不知所云。

文件一大堆,首先分堆,找出哪些文件是属于同一个sheet页面规则的。

    stat_dict = {}   # 统计被验证文件的字典
    for key in header_names:   # 遍历所有sheet页面
      print("Processing files:", key)
      pfiles = subfiles(key, args.dat)    # 在所有文件中找出sheet页面规则对应的所有文件
      if(len(pfiles) == 0):
            logger.error("Please check the %s and %s"%(key, header_names))
      else:
            i = 0
            frames = []                           # 临时列表,存入打开的验证文件
            for file in pfiles:
                df = "df" + str(i)               # 临时变量
                #logger.info("Processing files: %s"%file)
                #print("Processing files:", file)
                try:
                  df = pd.read_table(file, header=None, names=header_names, sep="|",index_col=False)   
                  #
                  # 用pandas读入文件,file就是读入的文件名,header=None和 index_col=False这个地方有个坑,如果不需要最好不加,否则输出成csv后可能会少一列
                  # 这里多说点,header和index_col都不要,就默认不需要第一列作为索引列,否则默认第一列为索引列,计算无影响,但是to_csv会被吃掉一列。总之,不需要就别写。
                  # names=header_names,sheet中对应的第二列作为pandas中columns的名字header_names就是["one","two","three"]
                  #            one                     two   three
                  # 2016-03-22 00:06:24.4463094 3123
                  # 2016-03-22 00:06:32.4565680 432
                  # 2016-03-22 00:06:32.6835965 543
                  # 2016-03-22 00:06:32.8041945 432
                  # sep 不用说了,按照什么分隔,是什么就写什么
                  #
                  if(df.shape != len(header_names)):# 这句是废话了,开始不会用,这句应该去掉
                        logger.error("file %s columns are not equal to Excel %s"%(file, header_names))
                except:
                  # try...except...就是上面某句错了,这里继续执行,也可以打印出特定的错误类型
                  logger.error("Unsupported type file: %s, len of cols is %d"%(file, len(header_names)))
                  print(key, header_names)
                  continue   # 很多文件,错误继续读下一个
                finally:
                  # 这个就是不管上面对错,这里都要执行。目的就是把都进来的,都拼接在一起。
                  if not isinstance(df, str):
                        frames.append(df)
            stat_dict = pd.concat(frames)   # 这个就是将同一类型的文件,也就是符合同一sheet规则的文件拼接在一起。frame就是所有打开文件的句柄?
            print(key, stat_dict.shape)      # .shape就是pandas的维度,这个值应该和awk中显示的一样。
                                                                  比如:    $ ls *.dat | xargs wc -l      $ 89947 总用量                                                               

            for col_key in dict:
                if("in" in dict):
                  #print(col_key, dict)
                  #
                  # 不在指定范围内的值
                  #
                  filename = key + '-' + col_key + ".csv"
                  results_csv = os.path.join(args.results, filename)
                  list_a =re.findall("[\-|\d]+\.?\d*", dict)
                  print("list_a :", list_a)
                  stat_dict[~stat_dict.isin(list_a)].to_csv(results_csv, sep=',',header=True, columns=header_names, index=False)
                elif("N" == dict):
                  #
                  # 如果此列有值则输出
                  #
                  filename = key + '-' + col_key + ".csv"
                  print("Empty :", filename)
                  results_csv = os.path.join(args.results, filename)
                  stat_dict[~stat_dict.isnull()].to_csv(results_csv, sep=',',header=True, columns=header_names, index=False)
                elif("Y" == dict):
                  #
                  # 如果此列是空值则输出
                  #
                  filename = key + '-' + col_key + ".csv"
                  print("NoEmpty :",filename)
                  results_csv = os.path.join(args.results, filename)
                  stat_dict.isnull()].to_csv(results_csv, sep=',',header=True, columns=header_names, index=False)
                elif(("Y" in dict) and "D" in dict and ("<" not in dict)):
                  #
                  # 如果此列是空值和默认值则输出
                  # Y(D=0)
                  #
                  filename = key + '-' + col_key + ".csv"
                  print("Empty and default :", filename)
                  results_csv = os.path.join(args.results, filename)
                  list_a = re.findall("[\-|\d]+\.?\d*", dict)
                  stat_dict[(stat_dict.isnull()) | (stat_dict == int(list_a))].to_csv(results_csv, sep=',',header=True, columns=header_names, index=False)
                elif(("<" in dict) and ("D" in dict) and ("Y" in dict)):
                  #
                  # Y<500(D=-1)
                  #
                  filename = key + '-' + col_key + ".csv"
                  print("lt and default :", filename)
                  results_csv = os.path.join(args.results, filename)
                  list_a =re.findall("[\-|\d]+\.?\d*", dict)
                  print(list_a)
                  #print("files: ", pfiles)
                  #print("type: ", stat_dict)
                  stat_dict[(stat_dict >= int(list_a)) | (stat_dict == int(list_a))].to_csv(results_csv, sep=',',header=True, columns=header_names, index=False)
                elif((">" in dict) and ("ZCTTD" in dict)):
                  #
                  # 10<BigtoPre<100(D=-1)
                  #
                  filename = key + '-' + col_key + ".csv"
                  print("< col - lastCol < and default :", filename)
                  results_csv = os.path.join(args.results, filename)
                  list_col = dict.split(",")
                  print("list_col :", list_col)
                  list_a =re.findall("[\-|0-9]{1,}",dict)
                  print("list_a :", list_a)
                  stat_dict[(((stat_dict] - stat_dict]) <= int(list_a)) & (stat_dict] - stat_dict]) >= int(list_a)) | (stat_dict] == int(list_a[-1]))].to_csv(results_csv, sep=',',header=True, columns=header_names, index=False)
                elif((">" in dict) and ("ZCTTD" not in dict)):
                  #
                  # 10<BigtoPre<100
                  #
                  filename = key + '-' + col_key + ".csv"
                  print("num < col - lastCol < num :", filename)
                  results_csv = os.path.join(args.results, filename)
                  list_col = dict.split(",")
                  list_a =re.findall("[\-|0-9]{1,}",dict)
                  stat_dict[((stat_dict] - stat_dict]) <= int(list_a)) & ((stat_dict] - stat_dict]) >= int(list_a))].to_csv(results_csv, sep=',',header=True, columns=header_names, index=False)

#
#
#
def subfiles(keyword, path):
    files = []
    allfiles = glob.glob(os.path.join(args.dat, "*.dat"))
    for tmp in allfiles:
      if keyword in tmp:
            files.append(tmp)
   
    if(len(files) == 0):
      logger.error("There are no files called %s files under the %s"%(keyword, path))
      
    return files

页: [1]
查看完整版本: 总结贴,记录下pandas处理一大批数据