2012-01-23 33 views
5

我有這個任務,我一直在努力,但我對我的方法有極大的疑慮。數據分析不一致的字符串格式

所以問題是我有很多格式奇怪(而且不一致)的excel文件,我需要爲每個條目提取某些字段。示例數據集中是

a look at the data

我原來的做法是這樣的:

  1. 導出爲CSV
  2. 分成縣
  3. 獨立成區
  4. 單獨分析每個區,拉out values
  5. write to output.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) 
+7

解決這個問題的最好也是最可靠的方法是強制爲您提供這些數據的人員遵循標準格式,但我不知道這是否可用於您。如果不強制執行某種結構,提供這些數據的人將會不斷打破您的計劃。 –

回答

3

在那裏處理這個問題,而不是簡單的字符串處理的更強大的方法是什麼?

不是。

我想到的更多的是模糊邏輯的方法,試圖固定一個項目是哪個字段,它可以處理輸入有點任意。你會如何解決這個問題?

經過大量的分析和編程之後,它不會比你得到的好得多。

閱讀人們準備的東西需要 - 可悲的是,人們的大腦。

你可以混淆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. 
) 

所以,你沒有創造大量的中間(而且大多無信息)寬鬆的變量。

此外,請繼續查看您的analyze函數(以及任何其他函數)以提取各種「模式匹配」規則。這個想法是,你會檢查一個縣的數據,通過一堆功能,直到匹配模式;這也將創建指定的元組。你想要這樣的東西。

for p in (some, list, of, functions): 
    match= p(data) 
    if match: 
     return match 

每個函數或者返回一個名爲元組(因爲它喜歡行)或None(因爲它不喜歡的行)。