Python 不一致字符串格式的数据分析

Python 不一致字符串格式的数据分析,python,text,csv,processing,Python,Text,Csv,Processing,我一直在做这个任务,但对我的方法有着极大的疑虑 所以问题是,我有一大堆excel文件的格式很奇怪(而且不一致),我需要为每个条目提取某些字段。下面是一个示例数据集 我最初的做法是: 导出到csv 分县 分区 分别分析每个区域,提取值 写入输出.csv 我遇到的问题是格式(似乎组织得很好)在文件中几乎是随机的。每行包含相同的字段,但顺序、间距和措辞不同。我编写了一个脚本来正确处理一个文件,但它不能处理任何其他文件 所以我的问题是,有没有比简单的字符串处理更可靠的方法来解决这个问题?我想到的更多的

我一直在做这个任务,但对我的方法有着极大的疑虑

所以问题是,我有一大堆excel文件的格式很奇怪(而且不一致),我需要为每个条目提取某些字段。下面是一个示例数据集

我最初的做法是:

  • 导出到csv
  • 分县
  • 分区
  • 分别分析每个区域,提取值
  • 写入输出.csv
  • 我遇到的问题是格式(似乎组织得很好)在文件中几乎是随机的。每行包含相同的字段,但顺序、间距和措辞不同。我编写了一个脚本来正确处理一个文件,但它不能处理任何其他文件

    所以我的问题是,有没有比简单的字符串处理更可靠的方法来解决这个问题?我想到的更多的是一种模糊逻辑方法,用于确定一个项目是哪个字段,它可以处理有点随意的输入。你将如何处理这个问题

    如果这有助于解决问题,以下是我编写的脚本:

    # This file takes a tax CSV file as input
    # and separates it into counties
    # then appends each county's entries onto
    # the end of the master out.csv
    # which will contain everything including
    # taxes, bonds, etc from all years
    
    #import the data csv
    import sys
    import re
    import csv
    
    def cleancommas(x):
      toggle=False
      for i,j in enumerate(x):
        if j=="\"":
          toggle=not toggle
        if toggle==True:
          if j==",":
            x=x[:i]+" "+x[i+1:]
      return x
    
    def districtatize(x):
      #list indexes of entries starting with "for" or "to" of length >5
      indices=[1]
      for i,j in enumerate(x):
        if len(j)>2:
          if j[:2]=="to":
            indices.append(i)
        if len(j)>3:
          if j[:3]==" to" or j[:3]=="for":
            indices.append(i)
        if len(j)>5:
          if j[:5]==" \"for" or j[:5]==" \'for":
            indices.append(i)
        if len(j)>4:
          if j[:4]==" \"to" or j[:4]==" \'to" or j[:4]==" for":
            indices.append(i)
      if len(indices)==1:
        return [x[0],x[1:len(x)-1]]
      new=[x[0],x[1:indices[1]+1]]
      z=1
      while z<len(indices)-1:
        new.append(x[indices[z]+1:indices[z+1]+1])
        z+=1
      return new
      #should return a list of lists. First entry will be county
      #each successive element in list will be list by district
    
    def splitforstos(string):
      for itemind,item in enumerate(string):      # take all exception cases that didn't get processed
        splitfor=re.split('(?<=\d)\s\s(?=for)',item)  # correctly and split them up so that the for begins
        splitto=re.split('(?<=\d)\s\s(?=to)',item)    # a cell
        if len(splitfor)>1:
          print "\n\n\nfor detected\n\n"
          string.remove(item)
          string.insert(itemind,splitfor[0])
          string.insert(itemind+1,splitfor[1])
        elif len(splitto)>1:
          print "\n\n\nto detected\n\n"
          string.remove(item)
          string.insert(itemind,splitto[0])
          string.insert(itemind+1,splitto[1])
    
    def analyze(x):
      #input should be a string of content
      #target values are nomills,levytype,term,yearcom,yeardue
      clean=cleancommas(x)
      countylist=clean.split(',')
      emptystrip=filter(lambda a: a != '',countylist)
      empt2strip=filter(lambda a: a != ' ', emptystrip)
      singstrip=filter(lambda a: a != '\' \'',empt2strip)
      quotestrip=filter(lambda a: a !='\" \"',singstrip)
      splitforstos(quotestrip)
      distd=districtatize(quotestrip)
      print '\n\ndistrictized\n\n',distd
      county = distd[0]
      for x in distd[1:]:
        if len(x)>8:
          district=x[0]
          vote1=x[1]
          votemil=x[2]
          spaceindex=[m.start() for m in re.finditer(' ', votemil)][-1]
          vote2=votemil[:spaceindex]
          mills=votemil[spaceindex+1:]
          votetype=x[4]
          numyears=x[6]
          yearcom=x[8]
          yeardue=x[10]
          reason=x[11]
          data = [filename,county,district, vote1, vote2, mills, votetype, numyears, yearcom, yeardue, reason]
          print "data",data
        else:
          print "x\n\n",x
          district=x[0]
          vote1=x[1]
          votemil=x[2]
          spaceindex=[m.start() for m in re.finditer(' ', votemil)][-1]
          vote2=votemil[:spaceindex]
          mills=votemil[spaceindex+1:]
          votetype=x[4]
          special=x[5]
          splitspec=special.split(' ')
          try:
            forind=[i for i,j in enumerate(splitspec) if j=='for'][0]
            numyears=splitspec[forind+1]
            yearcom=splitspec[forind+6]
          except:
            forind=[i for i,j in enumerate(splitspec) if j=='commencing'][0]
            numyears=None
            yearcom=splitspec[forind+2]
          yeardue=str(x[6])[-4:]
          reason=x[7]
          data = [filename,county,district,vote1,vote2,mills,votetype,numyears,yearcom,yeardue,reason]
          print "data other", data
        openfile=csv.writer(open('out.csv','a'),delimiter=',', quotechar='|',quoting=csv.QUOTE_MINIMAL)
        openfile.writerow(data)
    
    # call the file like so: python tax.py 2007May8Tax.csv
    filename = sys.argv[1] #the file is the first argument
    f=open(filename,'r')
    contents=f.read() #entire csv as string
    #find index of every instance of the word county
    separators=[m.start() for m in re.finditer('\w+\sCOUNTY',contents)] #alternative implementation in regex
    
    # split contents into sections by county
    # analyze each section and append to out.csv
    for x,y in enumerate(separators):
      try:
        data = contents[y:separators[x+1]]
      except:
        data = contents[y:]
      analyze(data)
    
    #此文件将税务CSV文件作为输入
    #把它分成几个县
    #然后将每个县的条目附加到
    #主输出的结尾。csv
    #它将包含所有内容,包括
    #历年所得税、债券等
    #将数据导入csv
    导入系统
    进口稀土
    导入csv
    def清洁逗号(x):
    toggle=False
    对于枚举(x)中的i,j:
    如果j==“\”:
    切换=不切换
    如果toggle==True:
    如果j==“,”:
    x=x[:i]+“”+x[i+1:]
    返回x
    def分区(x):
    #列出长度大于5的以“for”或“to”开头的条目索引
    指数=[1]
    对于枚举(x)中的i,j:
    如果len(j)>2:
    如果j[:2]=“to”:
    索引.附加(i)
    如果len(j)>3:
    如果j[:3]=“to”或j[:3]=“for”:
    索引.附加(i)
    如果len(j)>5:
    如果j[:5]=“for”或j[:5]=“for”:
    索引.附加(i)
    如果len(j)>4:
    如果j[:4]=“to”或j[:4]=“to”或j[:4]=“for”:
    索引.附加(i)
    如果len(指数)==1:
    返回[x[0],x[1:len(x)-1]]
    新=[x[0],x[1:索引[1]+1]]
    z=1
    而z
    有没有比简单的字符串处理更可靠的方法来解决这个问题

    不是真的

    我想到的更多的是一种模糊逻辑方法,用于确定一个项目是哪个字段,它可以处理有点随意的输入。您如何处理这个问题

    经过大量的分析和编程之后,它不会比您所拥有的更好

    阅读人们准备的东西需要——不幸的是——人们喜欢大脑

    您可以尝试使用NLTK来做得更好,但效果也不太好

    你不需要一个全新的方法,你需要简化你现有的方法

    比如说

      district=x[0]
      vote1=x[1]
      votemil=x[2]
      spaceindex=[m.start() for m in re.finditer(' ', votemil)][-1]
      vote2=votemil[:spaceindex]
      mills=votemil[spaceindex+1:]
      votetype=x[4]
      numyears=x[6]
      yearcom=x[8]
      yeardue=x[10]
      reason=x[11]
      data = [filename,county,district, vote1, vote2, mills, votetype, numyears, yearcom, yeardue, reason]
      print "data",data
    
    可以通过使用命名元组进行改进

    然后建造这样的东西

    data = SomeSensibleName( 
        district= x[0], 
        vote1=x[1], ... etc.
    )
    
    for p in ( some, list, of, functions ):
        match= p(data)
        if match: 
            return match
    
    这样,您就不会创建大量中间(基本上是无信息的)松散变量

    另外,继续查看
    analyze
    函数(以及任何其他函数)以提取各种“模式匹配”规则。其思想是,您将检查一个县的数据,逐步遍历一组函数,直到其中一个匹配模式;这也将创建命名元组。您需要类似这样的内容

    data = SomeSensibleName( 
        district= x[0], 
        vote1=x[1], ... etc.
    )
    
    for p in ( some, list, of, functions ):
        match= p(data)
        if match: 
            return match
    

    每个函数要么返回一个命名元组(因为它喜欢该行),要么返回
    None
    (因为它不喜欢该行).

    解决此问题的最佳和最可靠的方法是强制提供此数据的人员遵循标准格式,但我不知道这是否是您可用的选项。如果不强制执行某些结构,提供此数据的人员将不断破坏您的程序。