Skip to content

Commit

Permalink
Copying everything over from the internal development repos that have…
Browse files Browse the repository at this point in the history
… grown out of control by now.
  • Loading branch information
fginter committed Jan 14, 2014
1 parent 80d2c49 commit f0a97dc
Show file tree
Hide file tree
Showing 12 changed files with 1,222 additions and 0 deletions.
352 changes: 352 additions & 0 deletions conllUtil.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,352 @@
from __future__ import division
import codecs
import sys
import re

c2i={} #{col name : col idx}, for fast translation
# 1 2 3 4 5 6 7 8 9 10 11 12 13 14
for colIdx,col in enumerate("ID FORM LEMMA PLEMMA POS PPOS FEAT PFEAT HEAD PHEAD DEPREL PDEPREL FILLPRED PRED".split()):
c2i[col]=colIdx

def printc(cols,out=sys.stdout):
#always enforce 14 columns from now on
if len(cols)==15:
cols=cols[:-1]
elif len(cols)==13:
cols.append(u"_")
elif len(cols)==14:
pass
else:
assert False, ("Whoa, trying to print",len(cols),"columns?", cols)
print >> out, (u"\t".join(cols)).encode("utf-8")

def checkSentence(currSentence):
#Fail if there's something structurally wrong here
roots=0
for cols in currSentence:
assert len(cols) in (13,14,15)
head=int(cols[c2i["HEAD"]])
phead=int(cols[c2i["PHEAD"]])
assert head>=0 and head<=len(currSentence)
if head==0:
roots+=1
assert cols[c2i["DEPREL"]]==u"ROOT"
assert roots==1
print ".",
sys.stdout.flush()

def sanity(fileArgs):
#Sanity-check the file
currSentence=[] #List of columns
for (line,) in readFiles(fileArgs):
if not line:
checkSentence(currSentence)
currSentence=[]
else:
currSentence.append(line.split(u"\t"))
else:
if currSentence:
checkSentence(currSentence)
print "ALL OK"

def dropNullsSent(sentenceCounter,currSentence):
if sentenceCounter!=0:
print
nulls=[]
corrections=[] #Correction at each token position
nullCounter=0
for cols in currSentence:
corrections.append(nullCounter)
if cols[1]==u"*null*":
nulls.append(int(cols[0]))
nullCounter+=1
assert len(corrections)==len(currSentence)
for idx,cols in enumerate(currSentence):
cols[c2i["ID"]]=unicode(int(cols[c2i["ID"]])-corrections[idx])
if cols[c2i["HEAD"]] not in (u"_",u"0"):
head=int(cols[c2i["HEAD"]])
if head in nulls:
cols[c2i["HEAD"]]=u"0"
cols[c2i["DEPREL"]]=u"ROOT"
else:
cols[c2i["HEAD"]]=unicode(head-corrections[head-1])
if cols[c2i["PHEAD"]] not in (u"_",u"0"):
phead=int(cols[c2i["PHEAD"]])
if phead in nulls:
cols[c2i["PHEAD"]]=u"0"
cols[c2i["PDEPREL"]]=u"ROOT"
else:
cols[c2i["PHEAD"]]=unicode(phead-corrections[phead-1])
oldLen=len(currSentence)
for idx in nulls[::-1]:
currSentence.pop(idx-1)
assert len(currSentence)==oldLen-len(nulls)
for cols in currSentence:
printc(cols)


def dropNulls(fileArgs):
currSentence=[] #List of columns
counter=0
for (line,) in readFiles(fileArgs):
line=line.strip()
if not line:
if currSentence!=[]:
dropNullsSent(counter,currSentence)
counter+=1
currSentence=[]
else:
currSentence.append(line.split(u"\t"))
else:
if currSentence:
dropNullsSent(counter,currSentence)


def eval(fileArgs):

UNK=0 #tokens with GSTAG==UNK
total=0 #total tokens to take into account
hardTotal=0

cats=("LEMMA","POS","POS+FEAT","LEMMA+POS+FEAT","HEAD","DEPREL","HEAD+DEPREL","LEMMA+POS+FEAT+HEAD+DEPREL")
#cats=("POS+FEAT",)

longestCat=max(len(combo) for combo in cats) #for result layout

counts={}
errs={} #{combo:{(gs,sys):count}}

for (line,) in readFiles(fileArgs):
if not line:
continue
cols=line.split(u"\t")
if cols[c2i["POS"]]==u"UNK":
UNK+=1
continue
# if cols[c2i["POS"]]==u"_": #NO GS TO WORK AGAINST
# continue
total+=1
for combo in cats:
gsR=u"---".join(cols[c2i[c]] for c in combo.split("+"))
sysR=u"---".join(cols[c2i["P"+c]] for c in combo.split("+"))
if gsR!=sysR:
err=(gsR,sysR)
#print >> sys.stderr, cols[1].encode("utf-8"), gsR, sysR
errDict=errs.setdefault(combo,{})
errDict[err]=errDict.get(err,0)+1
else:
counts[combo]=counts.get(combo,0)+1
for combo in cats:
corr=counts.get(combo,0)
print "%s %5.1f (%6d /%6d)"%(combo.rjust(longestCat),corr/total*100,corr,total)
print
#printErrs(errs,"POS+FEAT",1000)

def printErrs(err,combo,limit=100):
lst=list(err[combo].items())
lst.sort(key=lambda item: item[1],reverse=True)
for gs_sys,cnt in lst[:limit]:
print (u"% 6d GS: %s SYS: %s"%(cnt,gs_sys[0],gs_sys[1]))



def filter(feat,rewrites):
tags=feat.split(u"|")
res=[]
for t in tags:
for (exp,replacement) in rewrites:
if exp.match(t):
if replacement!=None:
res.append(replacement)
break #First hit applies
else: #No hits
res.append(t)
if res:
return u"|".join(res)
else:
return u"_"

def lines2sentences(lineList):
sentences=[[]]
for line in lineList:
line=line.strip()
if not line:
if sentences[-1]: #new sentence
sentences.append([])
else:
sentences[-1].append(line)
while sentences[-1]==[]:
sentences.pop(-1)
return sentences


def readFiles(args):
if not args:
args.append("-") #assume single stdin input
assert args.count("-")<=1
fNames=[]
for fName in args:
if fName=="-":
fNames.append("/dev/stdin")
else:
fNames.append(fName)
files=[codecs.open(fName,"rt","utf-8") for fName in fNames]
all_sentences=[lines2sentences(f.readlines()) for f in files] #Read everything in F lists of sentences
for sList in all_sentences:
assert len(sList)==len(all_sentences[0]) #all must be same length
for tupleIdx,sentenceTuple in enumerate(zip(*all_sentences)):
for sentence in sentenceTuple:
assert len(sentence)==len(sentenceTuple[0])
for lineTuple in zip(*sentenceTuple):
yield lineTuple
if tupleIdx<len(all_sentences[0])-1:
yield tuple(u"" for f in files) #Pretend empty lines



if __name__=="__main__":

def arg_option_callback(option,opt,value,parser,*args):
#stores the name of the option as "action" and its arguments as action_args
parser.values.action=opt.replace("--","")
parser.values.action_args=value



from optparse import OptionParser, OptionGroup
parser = OptionParser(usage="python conllUtil.py --<action> [options] (mainfile.conll09|-) [auxfile.conll09|-]\n\nIf any of the file arguments is specified as '-', it is read from stdin. If no files are given a single '-' is assumed. So you can chain as cat somefile | python conllUtil.py ... | python conllUtil.py ... - file2 | ... Output is always written to stdout.\n\n --!!!-- Careful: only a single action at a time for now, thank you!")

actionG=OptionGroup(parser, "Action", description="Pick a single action from these:")

actionG.add_option("--retagUNK", dest="action", action="store_const", const="retagUNK", help="Retags UNK. Same as LEMMA:=PLEMMA,POS:=PPOS,FEAT:=PFEAT restricted to tokens where POS==UNK in the first file")
actionG.add_option("--swap", action="callback", callback=arg_option_callback, type="string", help="Comma separated list of swaps to perform (will be done in left-to-right order). COL1<->COL2 swaps the values of COL1 and COL2, COL1:=COL2 sets COL1 to the value of COL2, COL1:=None sets the value of COL1 to '_'. If two arguments are given, <-> works on left file and := assigns to left file taking values from right file",metavar="SWAP LIST")
actionG.add_option("--fix14", dest="action", action="store_const", const="fix14", help="Add or remove empty columns to get the standard 14 columns (add max 1 or remove max 1)")
actionG.add_option("--eval", dest="action", action="store_const", const="eval", help="Evaluates columns versus Pcolumns.")
actionG.add_option("--sanity", dest="action", action="store_const", const="sanity", help="Do basic sanity check on the file. Checks the tree for single root and dependencies not being all over the place")
actionG.add_option("--cat", dest="action", action="store_const", const="cat", help="Catenate the input files.")
actionG.add_option("--dropnulls", dest="action", action="store_const", const="dropnulls", help="Drop *null* tokens from the data. Reattaches their dependents to root.")
actionG.add_option("--droptags", action="callback", callback=arg_option_callback, type="string", help="Comma separated list of morpho tags to drop/rewrite in FEAT and PFEAT. Format: tagregularexpression->string|None Example: POSS_.*3->POSS_3,CASECHANGE_cap->None,CLIT_->None will merge 3rd person poss sufixes into POSS_3, delete CASECHANGE_cap, and delete all clitics. The regular expressions are matched from start, so use $ if you need exact match. ",metavar="LIST")




parser.add_option_group(actionG)

(options, args) = parser.parse_args()

if options.action=="cat":
for inFile in args:
f=open(inFile,"rt").read()
sys.stdout.write(f)
print
elif options.action=="retagUNK":
assert len(args)==2, "you need two files on input"
for mainLine,auxLine in readFiles(args):
if not mainLine:
assert not auxLine
print
continue
mainCols=mainLine.split(u"\t")
auxCols=auxLine.split(u"\t")
assert mainCols[:2]==auxCols[:2]
if mainCols[c2i["POS"]].startswith(u"UNK"):
mainCols[c2i["POS"]]=auxCols[c2i["PPOS"]]
mainCols[c2i["LEMMA"]]=auxCols[c2i["PLEMMA"]]
mainCols[c2i["FEAT"]]=auxCols[c2i["PFEAT"]]
printc(mainCols)
else:
print mainLine.encode("utf-8")
elif options.action=="swap":
assert options.action_args, "Need a list of swapped fields"
#parse what to swap
assignments=[] #(src,dst,"set|swap")
pairs=options.action_args.split(",")
for p in pairs:
if "<->" in p:
L,R=p.split("<->")
A="swap"
elif ":=" in p:
L,R=p.split(":=")
A="set"
else:
assert False, ("Do not understand:", p)
L=c2i[L]
if R=="None":
R=None
else:
R=c2i[R]
assignments.append((L,R,A))
for lines in readFiles(args):
if len(lines)==1:
mainLine=lines[0]
auxLine=None
else:
mainLine,auxLine=lines
if not mainLine:
print
continue
cols=mainLine.split(u"\t")
assert len(cols) in (13,14,15)
for L,R,A in assignments:
if A=="set":
if R==None:
cols[L]=u"_"
else: #Assignment
if auxLine!=None: #we have two files
auxCols=auxLine.split(u"\t")
assert len(auxCols) in (13,14,15), (len(auxCols), auxCols)
assert auxCols[:2]==cols[:2], "Mismatch in the files!" #token number and token text should always match
cols[L]=auxCols[R]
else:
cols[L]=cols[R]
elif A=="swap":
tmp=cols[L]
cols[L]=cols[R]
cols[R]=tmp
printc(cols)
elif options.action=="fix14":
assert len(args)<2, "Only one file can be on input"
for (mainLine,) in readFiles(args):
if not mainLine:
print
continue
cols=mainLine.split(u"\t")
if len(cols)==15: #all okay
del cols[-1]
elif len(cols)==14:
pass #all okay
elif len(cols)==13:
cols.append(u"_")
else:
assert False #odd-looking file
printc(cols)
elif options.action=="eval":
assert len(args)<2, "Only one file is expected"
eval(args)
elif options.action=="sanity":
assert len(args)<2, "Only one file is expected"
sanity(args)
elif options.action=="dropnulls":
assert len(args)<2, "Only one file is expected"
dropNulls(args)
elif options.action=="droptags":
rewrites=[] #[(regexp,result|None),...]
pairs=options.action_args.split(",")
for p in pairs:
regexp,result=p.rsplit("->",1)
if result=="None":
result=None
exp=re.compile(unicode(regexp,"utf-8"),re.U)
rewrites.append((exp,result))
#Now I have all expressions compiled, can process the data
for (mainLine,) in readFiles(args):
if not mainLine:
print
continue
cols=mainLine.split(u"\t")
if cols[c2i["PFEAT"]]!=u"_":
cols[c2i["PFEAT"]]=filter(cols[c2i["PFEAT"]],rewrites)
if cols[c2i["FEAT"]]!=u"_":
cols[c2i["FEAT"]]=filter(cols[c2i["FEAT"]],rewrites)
printc(cols)

12 changes: 12 additions & 0 deletions data/wiki-test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Maanmittauksessa kiintopiste on maastoon pysyvästi merkitty maastonkohta, jonka sijainti tai korkeus on tarkasti määritetty suhteessa muihin kiintopisteisiin tai tiettyyn koordinaatistoon. Merkkinä voi olla esimerkiksi pultti kalliossa tai kadun pinnassa, maston tai majakan huippu, tai valtakunnan rajapyykki. Kiintopisteitä käytetään lähtökohtana, kun määritetään muiden maastonkohtien sijainteja tai korkeuksia. Kiintopiste voi olla tasokiintopiste, jolloin sen paikka tunnetaan tarkasti leveys- ja korkeuspiirien suhteen, korkeuskiintopiste, jolloin sen paikka tunnetaan pystysuunnassa suhteessa keskimääräiseen merenpinnan tasoon tai teoreettiseen geoidiin, tai se voi olla molempia samanaikaisesti.
Suomessa valtakunnallista kiintopisterekisteriä pitää maanmittauslaitos. Kunnilla on omia kiintopisteverkkojaan.
Tasokiintopisteiden mittauksissa on aiemmin käytetty kolmiomittausta, kun taas nykyään käytetään erityisesti maanmittaustarkoitukseen suunniteltuja, tavallista tarkempia satelliittipaikannusmenetelmiä. Korkeuskiintopisteet on puolestaan määritetty vaaitsemalla.
Mittaustavoista johtuen kiintopisteen paikan epävarmuus on suurimmillaan korkeudessa. Vaakatasossa tarkkuus on parhaimmillaan millimetriluokkaa, samalla kun pystysuuntaan harvoin päästään laajemmalla alalla edes alhaisiin senttimetreihin, satelliittejakaan hyödyntäen.
Tamman tiineys kestää noin 11 kalenterikuukautta. Tiineyden alussa tammaa voidaan kouluttaa aivan tavalliseen tapaan. Kun tamman vatsa alkaa kasvaa, ja sen paino alkaa nousta, on käytettävä maalaisjärkeä. Tammalta ei saa vaatia liikaa. On tärkeää, että kantava tamma saa olla paljon ulkona ja liikkua mielihalujensa mukaan. Eräät tammat saattavat muuttua hieman ärtyisiksi tiineyden aikana: asiat, jotka ovat olleet niille yhdentekeviä aikaisemmin, ovatkin nyt vaikeampia. Tammaa saattaa esimerkiksi ärsyttää, kun sitä harjataan vatsan alta tai kun satulavyötä kiristetään. Jotkut tammat saattavat muuttua levottomiksi tiineyden edetessä. Kaikki tämä on aivan normaalia, joten syytä huoleen ei ole. Tämä kaikki on selvä merkki siitä, että tamma tietää, mikä sitä odottaa. Eräät tammat ovat tiineyden aikana niin ärtyisiä, että niiden vatsa pitäisi jättää rauhaan. Kun tamma on kantava, täytyy ajatella tamman ja kohdussa olevan varsan terveyttä. Tiineelle tammalle ei kannata antaa tavallista rehua. Kantaville tammoille on tarkoitettu oma erityisrehu, jota kaikki rehuntoimittajat myyvät. Muutama päivä ennen synnytystä jo aiemmin varsoneiden tammojen utareisiin ilmestyy ns. vahatapit. Siitä tietää, että varsominen on lähellä.
Selkie on kylä Kontiolahdella. Se sijaitsee kunnan itäosassa Ilomantsintieltä erkanevan Jakokoski-Heinävaara -tien eli Mönnin-Selkien maantien (yhdystie 5100) varrella. Kylän naapurikyliä ovat Heinävaara ja Mönni. Asukkaita kylällä on noin 270.[1] Selillä on kyläkoulu sekä oma evankelisluterilainen hautausmaakappeli Selkien-Mönnin hautausmaalla. Alakoulussa on tällä hetkellä noin 35 oppilasta Seliltä ja Mönnistä. Kylästä on arkipäivisin linja-autoyhteys Kontiolahden ja Joensuun suuntiin. Liikuntatiloja on Palotalon nykyaikaisessa urheiluhallissa. Mustavaaran laskettelukeskus sijoittuu kylän etelälaidalle.
Selkie-Lehtoi on nauhamainen kyläkokonaisuus, joka ryhmittyy korkealle vaaralinjalle vanhan Tohmajärven maantien varteen. Tie seuraa paikoitellen 1600-luvun ensimmäistä, rinteille kaskiviljelyn tuoman asutuksen synnyttämää kylätien linjaa. Vaarat nousevat paikoin yli 200 metrin korkeudelle merenpinnasta. Kylältä on toistaiseksi laajoja selkeitä näkymiä ympäristöön: suora näköyhteys jopa Joensuun keskustaan, Pielisjoelle ja Kolille asti. Möykynmäki on seudun tieliikenneympäristöistä paikallisesti koettavin.
Kylän nimi "Selkien kylä" taipuu muodossa "Selillä, Seliltä, Selille"; Lehtoin kylä (varhain oma kylänsä) "Lehtoilla, Lehtoilta, Lehtoille". Nykyinen kirjoitusasu on todennäköisesti peräisin Venäjän vallan ajalta, ja tämä on edesauttanut nykyisiäkin poikkeavia kielellisiä variaatioita. Virallisissa yhteyksissä sallitaan muodollisesti "Selkiellä" jne.
Selkien pohjoisin osa kuuluu valtakunnallisesti merkittäviin rakennettuihin kulttuuriympäristöihin. Samoin Selkien-Lehtoin-Heinävaaran kyläalue on yksi valtioneuvoston periaatepäätöksen mukaisista valtakunnallisesti arvokkaista maisema-alueista (koko maassa 156).
Muiden Pohjois-Karjalan vaarakylien tapaan Selkie on osa ympäristöministeriön vuonna 1992, Suomen 75-vuotisjuhlavuonna, julkistamia Suomen kansallismaisemia[3]. Historiallisen, kulttuuri- tai luonnonmaisemansa puolesta merkityksellisiksi kansallismaisemiksi on valittu yhteensä 27 väljää aluekokonaisuutta (maisematyyppiä) ympäri Suomen. Luokittelulla ei ole juridista merkitystä.
Selkien kyläalueeseen kuuluvat Selkien ja Lehtoin lisäksi muiden muassa Elovaara, Havukkavaara, Jukajoki ja Mustavaara.
Kylän postitoimipaikka oli 81235 Lehtoi vuoden 2012 lopputalveen saakka.
Loading

0 comments on commit f0a97dc

Please sign in to comment.